emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/pgtk 66a36f1: Merge branch 'master' of git.sv.gnu.org:/srv/git/e


From: Yuuki Harano
Subject: feature/pgtk 66a36f1: Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs into feature/pgtk
Date: Tue, 27 Apr 2021 09:41:02 -0400 (EDT)

branch: feature/pgtk
commit 66a36f1e5a323aed3d39db1044a1b71373123832
Merge: b302a55 6fb80c9
Author: Yuuki Harano <masm+github@masm11.me>
Commit: Yuuki Harano <masm+github@masm11.me>

    Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs into feature/pgtk
---
 .gitignore                                     |    1 +
 Makefile.in                                    |   31 +-
 admin/MAINTAINERS                              |    7 +
 admin/emake                                    |    2 +
 admin/make-tarball.txt                         |   14 +-
 configure.ac                                   |  133 +-
 doc/emacs/emacs.texi                           |    2 +-
 doc/emacs/text.texi                            |   38 +-
 doc/lispref/files.texi                         |    2 +-
 doc/lispref/keymaps.texi                       |    2 +-
 doc/lispref/macros.texi                        |   26 +-
 doc/lispref/minibuf.texi                       |   89 +-
 doc/misc/cl.texi                               |   15 +-
 etc/NEWS                                       |   19 +-
 etc/TODO                                       |    7 +
 lib/Makefile.in                                |    6 +
 lib/af_alg.h                                   |  115 +
 lisp/Makefile.in                               |   41 +-
 lisp/cus-dep.el                                |    1 +
 lisp/emacs-lisp/advice.el                      |    5 +
 lisp/emacs-lisp/autoload.el                    |    4 +-
 lisp/emacs-lisp/byte-opt.el                    |    1 +
 lisp/emacs-lisp/byte-run.el                    |   10 +
 lisp/emacs-lisp/bytecomp.el                    |  180 +-
 lisp/emacs-lisp/cl-macs.el                     |   34 +
 lisp/emacs-lisp/comp-cstr.el                   | 1190 ++++++
 lisp/emacs-lisp/comp.el                        | 4207 ++++++++++++++++++
 lisp/emacs-lisp/disass.el                      |   29 +-
 lisp/emacs-lisp/find-func.el                   |   11 +-
 lisp/emacs-lisp/map-ynp.el                     |   86 +-
 lisp/emacs-lisp/nadvice.el                     |   18 +
 lisp/emacs-lisp/package.el                     |   71 +-
 lisp/erc/erc.el                                |    1 +
 lisp/faces.el                                  |    3 +-
 lisp/files.el                                  |    7 +-
 lisp/frame.el                                  |  121 +-
 lisp/gnus/gnus-msg.el                          |   11 +-
 lisp/gnus/gnus-registry.el                     |    7 +-
 lisp/gnus/gnus.el                              |    5 +-
 lisp/help-fns.el                               |    2 +
 lisp/help-macro.el                             |   19 +-
 lisp/help.el                                   |   14 +-
 lisp/international/mule.el                     |    5 +-
 lisp/loadup.el                                 |   46 +-
 lisp/mail/emacsbug.el                          |    2 +-
 lisp/net/tramp.el                              |    2 +-
 lisp/printing.el                               |  227 +-
 lisp/progmodes/cc-bytecomp.el                  |   11 +-
 lisp/progmodes/cc-fonts.el                     |    4 +-
 lisp/progmodes/cc-langs.el                     |    3 +-
 lisp/progmodes/elisp-mode.el                   |   26 +-
 lisp/ses.el                                    |   76 +-
 lisp/startup.el                                |   25 +
 lisp/subr.el                                   |    7 +-
 lisp/term/w32-win.el                           |    3 +-
 lisp/textmodes/fill.el                         |   10 +-
 lisp/url/url-proxy.el                          |   10 +-
 lisp/window.el                                 |   39 -
 lisp/winner.el                                 |   13 +-
 nt/epaths.nt                                   |    5 +
 nt/mingw-cfg.site                              |    4 +
 src/Makefile.in                                |   18 +-
 src/alloc.c                                    |   37 +-
 src/callproc.c                                 |    2 +-
 src/charset.c                                  |    2 +-
 src/comp.c                                     | 5410 ++++++++++++++++++++++++
 src/comp.h                                     |  114 +
 src/data.c                                     |   95 +-
 src/decompress.c                               |  102 +
 src/dispextern.h                               |    4 +-
 src/dispnew.c                                  |  120 +-
 src/doc.c                                      |   12 +-
 src/dynlib.c                                   |    4 -
 src/editfns.c                                  |    2 +
 src/emacs.c                                    |  178 +-
 src/epaths.in                                  |    4 +
 src/eval.c                                     |  121 +-
 src/fns.c                                      |   43 +-
 src/frame.c                                    |  732 ++--
 src/frame.h                                    |  213 +-
 src/gtkutil.c                                  |  212 +-
 src/image.c                                    |    4 +-
 src/keyboard.c                                 |    7 +-
 src/lisp.h                                     |   76 +-
 src/lread.c                                    |  229 +-
 src/nsfns.m                                    |   26 +-
 src/nsterm.m                                   |   41 +-
 src/pdumper.c                                  |  331 +-
 src/pdumper.h                                  |   15 +-
 src/pgtkfns.c                                  |   69 +-
 src/pgtkterm.c                                 |   15 +-
 src/print.c                                    |   13 +-
 src/process.c                                  |    2 +-
 src/sound.c                                    |    5 +-
 src/term.c                                     |    4 +-
 src/termhooks.h                                |    2 +-
 src/verbose.mk.in                              |    8 +
 src/w32.c                                      |   28 +-
 src/w32.h                                      |    3 +
 src/w32common.h                                |    8 +
 src/w32fns.c                                   |  100 +-
 src/w32inevt.c                                 |   12 +-
 src/w32proc.c                                  |   13 +-
 src/w32term.c                                  |  156 +-
 src/widget.c                                   |   75 +-
 src/window.c                                   |    2 +-
 src/xdisp.c                                    |   29 +-
 src/xfns.c                                     |  135 +-
 src/xmenu.c                                    |   32 +-
 src/xterm.c                                    |  276 +-
 src/xterm.h                                    |    2 +-
 test/Makefile.in                               |   10 +
 test/infra/gitlab-ci.yml                       |   38 +
 test/lisp/auth-source-tests.el                 |    4 +-
 test/lisp/emacs-lisp/comp-cstr-tests.el        |  229 +
 test/lisp/help-fns-tests.el                    |    8 +-
 test/lisp/subr-tests.el                        |    2 +-
 test/src/comp-resources/comp-test-45603.el     |   28 +
 test/src/comp-resources/comp-test-funcs-dyn.el |   50 +
 test/src/comp-resources/comp-test-funcs.el     |  710 ++++
 test/src/comp-resources/comp-test-pure.el      |   40 +
 test/src/comp-tests.el                         | 1443 +++++++
 test/src/emacs-tests.el                        |    2 +-
 123 files changed, 16875 insertions(+), 1937 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7be876b..1bc3338 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,6 +135,7 @@ src/gl-stamp
 *.dll
 *.core
 *.elc
+*.eln
 *.o
 *.res
 *.so
diff --git a/Makefile.in b/Makefile.in
index 64ad53a..3115e63 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -96,6 +96,8 @@ NTDIR=@NTDIR@
 top_builddir = @top_builddir@
 -include ${top_builddir}/src/verbose.mk
 
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
+
 # ==================== Where To Install Things ====================
 
 # Location to install Emacs.app under GNUstep / macOS.
@@ -209,6 +211,10 @@ gsettingsschemadir = @gsettingsschemadir@
 # These variables hold the values Emacs will actually use.  They are
 # based on the values of the standard Make variables above.
 
+# Where lisp files are installed in a distributed with Emacs (relative
+# path to the installation directory).
+lispdirrel=@lispdirrel@
+
 # Where to install the lisp files distributed with Emacs.
 # This includes the Emacs version, so that the lisp files for different
 # versions of Emacs will install themselves in separate directories.
@@ -320,6 +326,14 @@ CONFIG_STATUS_FILES_IN = \
 COPYDIR = ${srcdir}/etc ${srcdir}/lisp
 COPYDESTS = "$(DESTDIR)${etcdir}" "$(DESTDIR)${lispdir}"
 
+ifeq (${ns_self_contained},no)
+BIN_DESTDIR='$(DESTDIR)${bindir}/'
+ELN_DESTDIR = $(DESTDIR)${libdir}/emacs/${version}/
+else
+BIN_DESTDIR='${ns_appbindir}/'
+ELN_DESTDIR = ${ns_appresdir}/
+endif
+
 gsettings_SCHEMAS = etc/org.gnu.emacs.defaults.gschema.xml
 
 all: ${SUBDIR} info $(gsettings_SCHEMAS:.xml=.valid)
@@ -356,6 +370,7 @@ epaths-force:
        @(gamedir='${gamedir}'; \
          sed < ${srcdir}/src/epaths.in > epaths.h.$$$$         \
          -e 's;\(#.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
+         -e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";'     \
          -e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
          -e 's;\(#.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
          -e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g'            \
@@ -386,6 +401,7 @@ epaths-force-w32:
          w32locallisppath=$${w32locallisppath//$${w32prefix}/"%emacs_dir%"} ; \
          sed < ${srcdir}/nt/epaths.nt > epaths.h.$$$$          \
          -e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 
"'"$${w32locallisppath//;/\\;}"'";' \
+         -e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
          -e '/^.*#/s/@VER@/${version}/g'                       \
          -e '/^.*#/s/@CFG@/${configuration}/g'                 \
          -e "/^.*#/s|@SRC@|$${w32srcdir}|g") &&                \
@@ -413,7 +429,8 @@ lib lib-src lisp nt: Makefile
 dirstate = .git/logs/HEAD
 VCSWITNESS = $(if $(wildcard $(srcdir)/$(dirstate)),$$(srcdir)/../$(dirstate))
 src: Makefile
-       $(MAKE) -C $@ VCSWITNESS='$(VCSWITNESS)' all
+       $(MAKE) -C $@ VCSWITNESS='$(VCSWITNESS)' BIN_DESTDIR='$(BIN_DESTDIR)' \
+                ELN_DESTDIR='$(ELN_DESTDIR)' all
 
 blessmail: Makefile src
        $(MAKE) -C lib-src maybe-blessmail
@@ -453,14 +470,14 @@ $(srcdir)/configure: $(srcdir)/configure.ac 
$(srcdir)/m4/*.m4
 # ==================== Installation ====================
 
 .PHONY: install install-arch-dep install-arch-indep install-etcdoc install-info
-.PHONY: install-man install-etc install-strip install-$(NTDIR)
+.PHONY: install-man install-etc install-strip install-$(NTDIR) install-eln
 .PHONY: uninstall uninstall-$(NTDIR)
 
 ## If we let lib-src do its own installation, that means we
 ## don't have to duplicate the list of utilities to install in
 ## this Makefile as well.
 
-install: all install-arch-indep install-etcdoc install-arch-dep 
install-$(NTDIR) blessmail install-gsettings-schemas
+install: all install-arch-indep install-etcdoc install-arch-dep 
install-$(NTDIR) blessmail install-eln install-gsettings-schemas
        @true
 
 ## Ensure that $subdir contains a subdirs.el file.
@@ -740,6 +757,13 @@ install-etc:
          done ; \
        done
 
+### Install native compiled Lisp files.
+install-eln:
+ifeq ($(HAVE_NATIVE_COMP),yes)
+       find native-lisp -type d -exec $(MKDIR_P) "$(ELN_DESTDIR){}" \; ; \
+       find native-lisp -type f -exec ${INSTALL_DATA} "{}" "$(ELN_DESTDIR){}" 
\;
+endif
+
 ### Build Emacs and install it, stripping binaries while installing them.
 install-strip:
        $(MAKE) INSTALL_STRIP=-s install
@@ -849,6 +873,7 @@ clean: $(clean_dirs:=_clean) clean-gsettings-schemas
        [ ! -d test ] || $(MAKE) -C test $@
        -rm -f ./*.tmp etc/*.tmp*
        -rm -rf info-dir.*
+       -rm -rf native-lisp
 
 ### 'bootclean'
 ###      Delete all files that need to be remade for a clean bootstrap.
diff --git a/admin/MAINTAINERS b/admin/MAINTAINERS
index 53afe87..02b8cf3 100644
--- a/admin/MAINTAINERS
+++ b/admin/MAINTAINERS
@@ -131,6 +131,13 @@ Amin Bandali
            lisp/erc/*
            doc/misc/erc.texi
 
+Andrea Corallo
+       Lisp native compiler
+           src/comp.c
+           lisp/emacs-lisp/comp.el
+           lisp/emacs-lisp/comp-cstr.el
+           test/src/comp-*.el
+
 ==============================================================================
 2. Areas that someone is willing to maintain, although he would not
 necessarily mind if someone else was the official maintainer.
diff --git a/admin/emake b/admin/emake
index e95b17d..834a184 100755
--- a/admin/emake
+++ b/admin/emake
@@ -68,6 +68,8 @@ GEN.*loaddefs|\
 ^\"configure\" file built.|\
 ^There seems to be no|\
 ^config.status:|\
+ELN_DESTDIR|\
+--bin-dest |\
 ^ *$|\
 ^Makefile built|\
 The GNU allocators don't work|\
diff --git a/admin/make-tarball.txt b/admin/make-tarball.txt
index c207895..2f763a5 100644
--- a/admin/make-tarball.txt
+++ b/admin/make-tarball.txt
@@ -259,8 +259,9 @@ General steps (for each step, check for possible errors):
 
 UPDATING THE EMACS WEB PAGES AFTER A RELEASE
 
-As soon as possible after a release, the Emacs web pages should be updated.
-(See admin/notes/www for general information.)
+As soon as possible after a release, the Emacs web pages at
+https://www.gnu.org/software/emacs/ should be updated.  (See
+admin/notes/www for general information.)
 
 The pages to update are:
 
@@ -270,7 +271,14 @@ add the new NEWS file as news/NEWS.xx.y
 
 For every new release, a banner is displayed on top of the emacs.html
 page.  Uncomment and the release banner in emacs.html.  Keep it on the
-page for about a month, then comment it again.
+page for about a month, then comment it again.  The new release banner
+looks like this:
+
+    <div class="release-banner">
+       <div class="container">
+           <h2><em>Emacs 27.1 is out</em>, download it <a 
href="download.html">here</a>!</h2>
+       </div>
+    </div>
 
 Regenerate the various manuals in manual/.
 The scripts admin/make-manuals and admin/upload-manuals summarize the process.
diff --git a/configure.ac b/configure.ac
index 0b5c0fe..61b747b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -187,7 +187,8 @@ dnl It is important that variables on the RHS not be 
expanded here,
 dnl hence the single quotes.  This is per the GNU coding standards, see
 dnl (autoconf) Installation Directory Variables
 dnl See also epaths.h below.
-lispdir='${datadir}/emacs/${version}/lisp'
+lispdirrel='${version}/lisp'
+lispdir='${datadir}/emacs/'${lispdirrel}
 standardlisppath='${lispdir}'
 locallisppath='${datadir}/emacs/${version}/site-lisp:'\
 '${datadir}/emacs/site-lisp'
@@ -484,6 +485,7 @@ OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS 
support])
 OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support])
 OPTION_DEFAULT_ON([modules],[don't compile with dynamic modules support])
 OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
+OPTION_DEFAULT_OFF([native-compilation],[compile with Emacs Lisp native 
compiler support])
 
 AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
  [use a file notification library (LIB one of: yes, inotify, kqueue, gfile, 
w32, no)])],
@@ -1896,6 +1898,7 @@ if test "${with_ns}" != no; then
   # so avoid NS_IMPL_COCOA if macuvs.h is absent.
   # Even a headless Emacs can build macuvs.h, so this should let you bootstrap.
   if test "${opsys}" = darwin && test -f "$srcdir/src/macuvs.h"; then
+     lispdirrel=Contents/Resources/lisp
      NS_IMPL_COCOA=yes
      ns_appdir=`pwd`/nextstep/Emacs.app
      ns_appbindir=${ns_appdir}/Contents/MacOS
@@ -3735,6 +3738,7 @@ AC_SUBST(LIBZ)
 LIBMODULES=
 HAVE_MODULES=no
 MODULES_OBJ=
+NEED_DYNLIB=no
 case $opsys in
   cygwin|mingw32) MODULES_SUFFIX=".dll" ;;
   darwin) MODULES_SUFFIX=".dylib" ;;
@@ -3770,7 +3774,8 @@ if test "${with_modules}" != "no"; then
 fi
 
 if test "${HAVE_MODULES}" = yes; then
-   MODULES_OBJ="dynlib.o emacs-module.o"
+   MODULES_OBJ="emacs-module.o"
+   NEED_DYNLIB=yes
    AC_DEFINE(HAVE_MODULES, 1, [Define to 1 if dynamic modules are enabled])
    AC_DEFINE_UNQUOTED(MODULES_SUFFIX, "$MODULES_SUFFIX",
      [System extension for dynamic libraries])
@@ -3797,6 +3802,124 @@ module_env_snippet_28="$srcdir/src/module-env-28.h"
 emacs_major_version="${PACKAGE_VERSION%%.*}"
 AC_SUBST(emacs_major_version)
 
+### Emacs Lisp native compiler support
+
+AC_DEFUN([libgccjit_smoke_test], [
+  AC_LANG_SOURCE(
+    [[#include <libgccjit.h>
+      #include <stdlib.h>
+      #include <stdio.h>
+      int
+      main (int argc, char **argv)
+      {
+        gcc_jit_context *ctxt;
+        gcc_jit_result *result;
+        ctxt = gcc_jit_context_acquire ();
+        if (!ctxt)
+          exit (1);
+        gcc_jit_type *int_type =
+          gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+        gcc_jit_function *func =
+          gcc_jit_context_new_function (ctxt, NULL,
+                                        GCC_JIT_FUNCTION_EXPORTED,
+                                        int_type, "foo", 0, NULL, 0);
+        gcc_jit_block *block = gcc_jit_function_new_block (func, "foo");
+        gcc_jit_block_end_with_return (
+          block,
+          NULL,
+          gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+        result = gcc_jit_context_compile (ctxt);
+        if (!result)
+          exit (1);
+        typedef int (*fn_type) (void);
+        fn_type foo =
+          (fn_type)gcc_jit_result_get_code (result, "foo");
+        if (!foo)
+          exit (1);
+        if (foo () != 1)
+          exit (1);
+        gcc_jit_context_release (ctxt);
+        gcc_jit_result_release (result);
+        return 0;
+      }]])])
+
+AC_DEFUN([libgccjit_not_found], [
+  AC_MSG_ERROR([elisp native compiler requested but libgccjit not found.
+Please try installing libgccjit or similar package.
+If you are sure you want Emacs compiled without elisp native compiler, pass
+  --without-native-compilation
+to configure.])])
+
+AC_DEFUN([libgccjit_dev_not_found], [
+  AC_MSG_ERROR([elisp native compiler requested but libgccjit header files were
+not found.
+Please try installing libgccjit-dev or similar package.
+If you are sure you want Emacs compiled without elisp native compiler, pass
+--without-nativecomp
+to configure.])])
+
+AC_DEFUN([libgccjit_broken], [
+  AC_MSG_ERROR([Installed libgccjit has failed passing the smoke test.
+You can verify it yourself compiling:
+<https://gcc.gnu.org/onlinedocs/jit/intro/tutorial01.html>.
+Please report the issue to your distribution if libgccjit was installed through
+that.
+Here instructions on how to compile and install libgccjit from source:
+<https://gcc.gnu.org/wiki/JIT>.])])
+
+HAVE_NATIVE_COMP=no
+LIBGCCJIT_LIB=
+if test "${with_native_compilation}" != "no"; then
+    if test "${HAVE_PDUMPER}" = no; then
+       AC_MSG_ERROR(['--with-nativecomp' requires '--with-dumping=pdumper'])
+    fi
+    if test "${HAVE_ZLIB}" = no; then
+       AC_MSG_ERROR(['--with-nativecomp' requires zlib])
+    fi
+
+    # Ensure libgccjit installed by Homebrew can be found.
+    if test -n "$BREW"; then
+      BREW_LIBGCCJIT_PREFIX=`$BREW --prefix --installed libgccjit 2>/dev/null`
+      if test "$BREW_LIBGCCJIT_PREFIX"; then
+        brew_libdir=`find ${BREW_LIBGCCJIT_PREFIX}/ -name \*.so \
+                     | sed -e '1!d;s|/[[^/]]*\.so$||'`
+        CFLAGS="$CFLAGS -I${BREW_LIBGCCJIT_PREFIX}/include"
+        LDFLAGS="$LDFLAGS -L${brew_libdir} -I${BREW_LIBGCCJIT_PREFIX}/include"
+      fi
+    fi
+
+    # Check if libgccjit is available.
+    AC_CHECK_LIB(gccjit, gcc_jit_context_acquire, [], [libgccjit_not_found])
+    AC_CHECK_HEADERS(libgccjit.h, [], [libgccjit_dev_not_found])
+    emacs_save_LIBS=$LIBS
+    LIBS="-lgccjit"
+    # Check if libgccjit really works.
+    AC_RUN_IFELSE([libgccjit_smoke_test], [], [libgccjit_broken])
+    LIBS=$emacs_save_LIBS
+    HAVE_NATIVE_COMP=yes
+    case "${opsys}" in
+      # mingw32 loads the library dynamically.
+      mingw32) ;;
+      # OpenBSD doesn't have libdl, all the functions are in libc
+      netbsd|openbsd)
+        LIBGCCJIT_LIB="-lgccjit" ;;
+      *)
+        LIBGCCJIT_LIB="-lgccjit -ldl" ;;
+    esac
+    NEED_DYNLIB=yes
+    AC_DEFINE(HAVE_NATIVE_COMP, 1, [Define to 1 if native compiler is 
available.])
+fi
+AC_DEFINE_UNQUOTED(NATIVE_ELISP_SUFFIX, ".eln",
+  [System extension for native compiled elisp])
+AC_SUBST(HAVE_NATIVE_COMP)
+AC_SUBST(LIBGCCJIT_LIB)
+
+DYNLIB_OBJ=
+if test "${NEED_DYNLIB}" = yes; then
+  DYNLIB_OBJ="dynlib.o"
+fi
+AC_SUBST(DYNLIB_OBJ)
+
 ### Use -lpng if available, unless '--with-png=no'.
 HAVE_PNG=no
 LIBPNG=
@@ -5334,6 +5457,7 @@ AC_SUBST(sharedstatedir)
 AC_SUBST(libexecdir)
 AC_SUBST(mandir)
 AC_SUBST(infodir)
+AC_SUBST(lispdirrel)
 AC_SUBST(lispdir)
 AC_SUBST(standardlisppath)
 AC_SUBST(locallisppath)
@@ -5780,8 +5904,8 @@ optsep=
 emacs_config_features=
 for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
  HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
- M17N_FLT MODULES NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP SOUND \
- THREADS TIFF \
+ M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP 
\
+ SOUND THREADS TIFF \
  TOOLKIT_SCROLL_BARS UNEXEC X11 XAW3D XDBE XFT XIM XPM XWIDGETS X_TOOLKIT \
  ZLIB; do
 
@@ -5857,6 +5981,7 @@ AS_ECHO(["  Does Emacs use -lXaw3d?                       
          ${HAVE_XAW3D
   Does Emacs support the portable dumper?                 ${with_pdumper}
   Does Emacs support legacy unexec dumping?               ${with_unexec}
   Which dumping strategy does Emacs use?                  ${with_dumping}
+  Does Emacs have native lisp compiler?                   ${HAVE_NATIVE_COMP}
 "])
 
 if test -n "${EMACSDATA}"; then
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 925c701..590dc42 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -159,7 +159,7 @@ Fundamental Editing Commands
 
 Important Text-Changing Commands
 * Mark::                The mark: how to delimit a region of text.
-* Killing::             Killing (cutting) and yanking (copying) text.
+* Killing::             Killing (cutting) and yanking (pasting) text.
 * Registers::           Saving a text string or a location in the buffer.
 * Display::             Controlling what text is displayed.
 * Search::              Finding or replacing occurrences of a string.
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 54e1669..2c9d486 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -502,8 +502,8 @@ text.
 @cindex mode, Auto Fill
 
   @dfn{Auto Fill} mode is a buffer-local minor mode (@pxref{Minor
-Modes}) in which lines are broken automatically at spaces when the
-line becomes too wide.
+Modes}) in which lines are broken automatically when the line becomes
+too wide and you type @kbd{@key{SPC}} or @kbd{@key{RET}}.
 
 @table @kbd
 @item M-x auto-fill-mode
@@ -522,12 +522,21 @@ certain major modes, add @code{auto-fill-mode} to the 
mode hooks
 (@pxref{Major Modes}).  When Auto Fill mode is enabled, the mode
 indicator @samp{Fill} appears in the mode line (@pxref{Mode Line}).
 
-  Auto Fill mode breaks lines automatically at spaces whenever they
-get longer than the desired width.  This line breaking occurs only
-when you type @key{SPC} or @key{RET}.  If you wish to insert a space
-or newline without permitting line-breaking, type @kbd{C-q @key{SPC}}
-or @kbd{C-q C-j} respectively.  Also, @kbd{C-o} inserts a newline
-without line breaking.
+  Auto Fill mode breaks lines automatically at the appropriate places
+whenever lines get longer than the desired width.  This line breaking
+occurs only when you type @kbd{@key{SPC}} or @kbd{@key{RET}}.  If you
+wish to insert a space or newline without permitting line-breaking,
+type @kbd{C-q @key{SPC}} or @kbd{C-q C-j} respectively.  Also,
+@kbd{C-o} inserts a newline without line breaking.
+
+@cindex kinsoku line-breaking rules
+  The place where Auto Fill breaks a line depends on the line's
+characters.  For characters from @acronym{ASCII}, Latin, and most
+other scripts Emacs breaks a line on space characters, to keep the
+words intact.  But for CJK scripts, a line can be broken between any
+two characters.  (If you load the @file{kinsoku} library, Emacs will
+avoid breaking a line between certain pairs of CJK characters, where
+special rules prohibit that.)
 
   When Auto Fill mode breaks a line, it tries to obey the
 @dfn{adaptive fill prefix}: if a fill prefix can be deduced from the
@@ -549,6 +558,9 @@ described in the next section.
 (@pxref{Fill Commands}).
 @end ifnottex
 
+  A similar feature that wraps long lines automatically at display
+time is Visual Line Mode (@pxref{Visual Line Mode}).
+
 @node Fill Commands
 @subsection Explicit Fill Commands
 
@@ -571,7 +583,11 @@ Center a line.
 current paragraph.  It redistributes the line breaks within the
 paragraph, and deletes any excess space and tab characters occurring
 within the paragraph, in such a way that the lines end up fitting
-within a certain maximum width.
+within a certain maximum width.  Like Auto Fill mode, this and other
+filling commands usually break lines at space characters, but for CJK
+characters these commands can break a line between almost any two
+characters, and they can also obey the kinsoku rules.  @xref{Auto
+Fill}.
 
 @findex fill-region
   Normally, @kbd{M-q} acts on the paragraph where point is, but if
@@ -645,8 +661,8 @@ or before @samp{)}, @samp{:} or @samp{?}); and
 even if preceded by a non-whitespace character).
 
   Emacs can display an indicator in the @code{fill-column} position
-using the Display fill column indicator mode 
-(@pxref{Displaying Boundaries, display-fill-column-indicator}).
+using the Display fill column indicator mode (@pxref{Displaying
+Boundaries, display-fill-column-indicator}).
 
 @node Fill Prefix
 @subsection The Fill Prefix
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index a8b921e..2033177 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -2244,7 +2244,7 @@ form.
 
   A @dfn{directory name} is a string that must name a directory if it
 names any file at all.  A directory is actually a kind of file, and it
-has a file name (called the @dfn{directory file name}, which is
+has a file name (called the @dfn{directory file name}), which is
 related to the directory name but is typically not identical.  (This
 is not quite the same as the usual POSIX terminology.)  These two
 names for the same entity are related by a syntactic transformation.
diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi
index cbc94d8..4097c86 100644
--- a/doc/lispref/keymaps.texi
+++ b/doc/lispref/keymaps.texi
@@ -2920,7 +2920,7 @@ menu item.
 
 @item :active @var{enable}
 @var{enable} is an expression; if it evaluates to @code{nil}, the item
-is make unselectable..  @code{:enable} is an alias for @code{:active}.
+is made unselectable.  @code{:enable} is an alias for @code{:active}.
 
 @item :visible @var{include}
 @var{include} is an expression; if it evaluates to @code{nil}, the
diff --git a/doc/lispref/macros.texi b/doc/lispref/macros.texi
index 7c090ae..b8df363 100644
--- a/doc/lispref/macros.texi
+++ b/doc/lispref/macros.texi
@@ -480,15 +480,17 @@ in expressions ordinarily.
 
   Another problem can happen if the macro definition itself
 evaluates any of the macro argument expressions, such as by calling
-@code{eval} (@pxref{Eval}).  You have to take into account that the
-context of the caller is not accessible at that time since the macro expansion
-may take place long before the code is executed.  Also if your macro definition
-does not use @code{lexical-binding} its own variables may hide the
-user's variables, if the user happens to use a
-variable with the same name as one of the macro arguments.  Inside the
-macro body, the macro argument binding is the most local binding of this
-variable, so any references inside the form being evaluated do refer to
-it.  Here is an example:
+@code{eval} (@pxref{Eval}).  You have to take into account that macro
+expansion may take place long before the code is executed, when the
+context of the caller (where the macro expansion will be evaluated) is
+not yet accessible.
+
+  Also, if your macro definition does not use @code{lexical-binding}, its
+formal arguments may hide the user's variables of the same name.  Inside
+the macro body, the macro argument binding is the most local binding of
+such variable, so any references inside the form being evaluated do refer
+to it.  Here is an example:
+
 @example
 @group
 (defmacro foo (a)
@@ -510,9 +512,9 @@ it.  Here is an example:
 @code{x}, because @code{a} conflicts with the macro argument variable
 @code{a}.
 
-  Also the expansion of @code{(foo x)} above will return something
-different or signal an error when the code is compiled since in that case
-@code{(foo x)} is expanded during compilation whereas the execution of
+  Also, the expansion of @code{(foo x)} above will return something
+different or signal an error when the code is compiled, since in that case
+@code{(foo x)} is expanded during compilation, whereas the execution of
 @code{(setq x 'b)} will only take place later when the code is executed.
 
   To avoid these problems, @strong{don't evaluate an argument expression
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index 7cf2fcf..72f0e58 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -2239,9 +2239,10 @@ This function asks the user a series of questions, 
reading a
 single-character answer in the echo area for each one.
 
 The value of @var{list} specifies the objects to ask questions about.
-It should be either a list of objects or a generator function.  If it is
-a function, it should expect no arguments, and should return either the
-next object to ask about, or @code{nil}, meaning to stop asking questions.
+It should be either a list of objects or a generator function.  If it
+is a function, it will be called with no arguments, and should return
+either the next object to ask about, or @code{nil}, meaning to stop
+asking questions.
 
 The argument @var{prompter} specifies how to ask each question.  If
 @var{prompter} is a string, the question text is computed like this:
@@ -2252,19 +2253,20 @@ The argument @var{prompter} specifies how to ask each 
question.  If
 
 @noindent
 where @var{object} is the next object to ask about (as obtained from
-@var{list}).
+@var{list}).  @xref{Formatting Strings}, for more information about
+@code{format}.
 
-If not a string, @var{prompter} should be a function of one argument
-(the next object to ask about) and should return the question text.  If
-the value is a string, that is the question to ask the user.  The
-function can also return @code{t}, meaning do act on this object (and
-don't ask the user), or @code{nil}, meaning ignore this object (and don't
-ask the user).
+If @var{prompter} is not a string, it should be a function of one
+argument (the object to ask about) and should return the question text
+for that object.  If the value @var{prompter} returns is a string,
+that is the question to ask the user.  The function can also return
+@code{t}, meaning to act on this object without asking the user, or
+@code{nil}, which means to silently ignore this object.
 
-The argument @var{actor} says how to act on the answers that the user
-gives.  It should be a function of one argument, and it is called with
-each object that the user says yes for.  Its argument is always an
-object obtained from @var{list}.
+The argument @var{actor} says how to act on the objects for which the
+user answers yes.  It should be a function of one argument, and will
+be called with each object from @var{list} for which the user answers
+yes.
 
 If the argument @var{help} is given, it should be a list of this form:
 
@@ -2274,34 +2276,49 @@ If the argument @var{help} is given, it should be a 
list of this form:
 
 @noindent
 where @var{singular} is a string containing a singular noun that
-describes the objects conceptually being acted on, @var{plural} is the
+describes a single object to be acted on, @var{plural} is the
 corresponding plural noun, and @var{action} is a transitive verb
-describing what @var{actor} does.
+describing what @var{actor} does with the objects.
 
-If you don't specify @var{help}, the default is @code{("object"
-"objects" "act on")}.
+If you don't specify @var{help}, it defaults to the list
+@w{@code{("object" "objects" "act on")}}.
 
-Each time a question is asked, the user may enter @kbd{y}, @kbd{Y}, or
-@key{SPC} to act on that object; @kbd{n}, @kbd{N}, or @key{DEL} to skip
-that object; @kbd{!} to act on all following objects; @key{ESC} or
-@kbd{q} to exit (skip all following objects); @kbd{.} (period) to act on
-the current object and then exit; or @kbd{C-h} to get help.  These are
-the same answers that @code{query-replace} accepts.  The keymap
-@code{query-replace-map} defines their meaning for @code{map-y-or-n-p}
-as well as for @code{query-replace}; see @ref{Search and Replace}.
+Each time a question is asked, the user can answer as follows:
+
+@table @asis
+@item @kbd{y}, @kbd{Y}, or @kbd{@key{SPC}}
+act on the object
+@item @kbd{n}, @kbd{N}, or @kbd{@key{DEL}}
+skip the object
+@item @kbd{!}
+act on all the following objects
+@item @kbd{@key{ESC}} or @kbd{q}
+exit (skip all following objects)
+@item @kbd{.} (period)
+act on the object and then exit
+@item @kbd{C-h}
+get help
+@end table
+
+@noindent
+These are the same answers that @code{query-replace} accepts.  The
+keymap @code{query-replace-map} defines their meaning for
+@code{map-y-or-n-p} as well as for @code{query-replace}; see
+@ref{Search and Replace}.
 
 You can use @var{action-alist} to specify additional possible answers
-and what they mean.  It is an alist of elements of the form
-@code{(@var{char} @var{function} @var{help})}, each of which defines one
-additional answer.  In this element, @var{char} is a character (the
+and what they mean.  If provided, @var{action-alist} should be an
+alist whose elements are of the form @w{@code{(@var{char}
+@var{function} @var{help})}}.  Each of the alist elements defines one
+additional answer.  In each element, @var{char} is a character (the
 answer); @var{function} is a function of one argument (an object from
-@var{list}); @var{help} is a string.
-
-When the user responds with @var{char}, @code{map-y-or-n-p} calls
-@var{function}.  If it returns non-@code{nil}, the object is considered
-acted upon, and @code{map-y-or-n-p} advances to the next object in
-@var{list}.  If it returns @code{nil}, the prompt is repeated for the
-same object.
+@var{list}); and @var{help} is a string.  When the user responds with
+@var{char}, @code{map-y-or-n-p} calls @var{function}.  If it returns
+non-@code{nil}, the object is considered to have been acted upon, and
+@code{map-y-or-n-p} advances to the next object in @var{list}.  If it
+returns @code{nil}, the prompt is repeated for the same object.  If
+the user requests help, the text in @var{help} is used to describe
+these additional answers.
 
 Normally, @code{map-y-or-n-p} binds @code{cursor-in-echo-area} while
 prompting.  But if @var{no-cursor-in-echo-area} is non-@code{nil}, it
diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index 7464ba2..c89e0e7 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -2084,14 +2084,15 @@ This clause also accepts optional @samp{from @var{pos}} 
and
 @samp{to @var{pos}} terms, limiting the clause to overlays which
 overlap the specified region.
 
-@item for @var{var} being the intervals [of @var{buffer}] @dots{}
-This clause iterates over all intervals of a buffer with constant
-text properties.  The variable @var{var} will be bound to conses
-of start and end positions, where one start position is always equal
-to the previous end position.  The clause allows @code{of},
+@item for @var{var} being the intervals [of @var{object}] @dots{}
+This clause iterates over all intervals of a buffer or string with
+constant text properties.  The variable @var{var} will be bound to
+conses of start and end positions, where one start position is always
+equal to the previous end position.  The clause allows @code{of},
 @code{from}, @code{to}, and @code{property} terms, where the latter
 term restricts the search to just the specified property.  The
-@code{of} term may specify either a buffer or a string.
+@code{of} term may specify either a buffer or a string.  @xref{Text
+Properties,,,elisp}.
 
 @item for @var{var} being the frames
 This clause iterates over all Emacs frames. The clause @code{screens} is
@@ -2238,7 +2239,7 @@ were non-@code{nil}, the loop returns @code{t}:
 
 @item never @var{condition}
 This clause is like @code{always}, except that the loop returns
-@code{t} if any conditions were false, or @code{nil} otherwise.
+@code{t} if all conditions were false, or @code{nil} otherwise.
 
 @item thereis @var{condition}
 This clause stops the loop when the specified form is non-@code{nil};
diff --git a/etc/NEWS b/etc/NEWS
index 63de46a..9bf232a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,10 +24,14 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 28.1
 
+** Emacs now optionally supports native compilation of Lisp files.
+To enable this, configure Emacs with the '--with-native-compilation' option.
+This requires the libgccjit library to be installed and functional.
+
 ---
 ** Support for building with Motif has been removed.
 
-** Cairo graphics library is now used by default if found.
+** The Cairo graphics library is now used by default if present.
 '--with-cairo' is now the default, if the appropriate development files
 are found by 'configure'.  Note that building with Cairo means using
 Pango instead of libXFT for font support.  Since Pango 1.44 has
@@ -1282,6 +1286,14 @@ key             binding
 / u             package-menu-filter-upgradable
 / /             package-menu-filter-clear
 
+*** Option to automatically native-compile packages upon installation.
+Customize the user option 'package-native-compile' to enable automatic
+native compilation of packages when they are installed.  That option
+is nil by default; if set non-nil, and if your Emacs was built with
+native-compilation support, each package will be natively compiled
+when it is installed, by invoking an asynchronous Emacs subprocess to
+run the native-compilation of the package files.
+
 ---
 *** Column widths in 'list-packages' display can now be customized.
 See the new user options 'package-name-column-width',
@@ -2635,6 +2647,11 @@ the Emacs Lisp reference manual for background.
 * Lisp Changes in Emacs 28.1
 
 +++
+** New function 'sxhash-equal-including-properties'.
+This is identical to 'sxhash-equal' but accounting also for string
+properties.
+
++++
 ** 'unlock-buffer' displays warnings instead of signaling.
 Instead of signaling 'file-error' conditions for file system level
 errors, the function now calls 'display-warning' and continues as if
diff --git a/etc/TODO b/etc/TODO
index 9448617..f806b6c 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -500,6 +500,13 @@ access in cases which need more than Lisp.
 
 ** Fix portable dumping so that you can redump without using -batch
 
+*** Redumps and native compiler "preloaded" sub-folder.
+In order to depose new .eln files being compiled into the "preloaded"
+sub-folder the native compiler needs to know in advance if this file
+will be preloaded or not.  As .eln files are not moved afterwards
+subsequent redumps might refer to .eln file out of the "preloaded"
+sub-folder.
+
 ** Imenu could be extended into a file-structure browsing mechanism
 This could use code like that of customize-groups.
 
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 043ace2..68a0247 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -31,12 +31,18 @@ all:
 
 -include ${top_builddir}/src/verbose.mk
 
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
+
 ALL_CFLAGS= \
   $(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) $(DEPFLAGS) \
   $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) $(PROFILING_CFLAGS) $(CFLAGS) \
   -I. -I../src -I$(srcdir) -I$(srcdir)/../src \
   $(if $(patsubst e-%,,$(notdir $<)),,-Demacs)
 
+ifeq ($(HAVE_NATIVE_COMP),yes)
+ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM
+endif
+
 SYSTEM_TYPE = @SYSTEM_TYPE@
 ifeq ($(SYSTEM_TYPE),windows-nt)
   include $(srcdir)/../nt/gnulib-cfg.mk
diff --git a/lib/af_alg.h b/lib/af_alg.h
new file mode 100644
index 0000000..4c5854c
--- /dev/null
+++ b/lib/af_alg.h
@@ -0,0 +1,115 @@
+/* af_alg.h - Compute message digests from file streams and buffers.
+   Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Matteo Croce <mcroce@redhat.com>, 2018.
+   Documentation by Bruno Haible <bruno@clisp.org>, 2018.  */
+
+/* Declare specific functions for computing message digests
+   using the Linux kernel crypto API, if available.  This kernel API gives
+   access to specialized crypto instructions (that would also be available
+   in user space) or to crypto devices (not directly available in user space).
+
+   For a more complete set of facilities that use the Linux kernel crypto API,
+   look at libkcapi.  */
+
+#ifndef AF_ALG_H
+# define AF_ALG_H 1
+
+# include <stdio.h>
+# include <errno.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+# if USE_LINUX_CRYPTO_API
+
+/* Compute a message digest of a memory region.
+
+   The memory region starts at BUFFER and is LEN bytes long.
+
+   ALG is the message digest algorithm; see the file /proc/crypto.
+
+   RESBLOCK points to a block of HASHLEN bytes, for the result.
+   HASHLEN must be the length of the message digest, in bytes, in particular:
+
+      alg    | hashlen
+      -------+--------
+      md5    | 16
+      sha1   | 20
+      sha224 | 28
+      sha256 | 32
+      sha384 | 48
+      sha512 | 64
+
+   If successful, fill RESBLOCK and return 0.
+   Upon failure, return a negated error number.  */
+int
+afalg_buffer (const char *buffer, size_t len, const char *alg,
+              void *resblock, ssize_t hashlen);
+
+/* Compute a message digest of data read from STREAM.
+
+   STREAM is an open file stream.  The last operation on STREAM should
+   not be 'ungetc', and if STREAM is also open for writing it should
+   have been fflushed since its last write.  Read from the current
+   position to the end of STREAM.  Handle regular files efficiently.
+
+   ALG is the message digest algorithm; see the file /proc/crypto.
+
+   RESBLOCK points to a block of HASHLEN bytes, for the result.
+   HASHLEN must be the length of the message digest, in bytes, in particular:
+
+      alg    | hashlen
+      -------+--------
+      md5    | 16
+      sha1   | 20
+      sha224 | 28
+      sha256 | 32
+      sha384 | 48
+      sha512 | 64
+
+   If successful, fill RESBLOCK and return 0.
+   Upon failure, return a negated error number.
+   Unless returning 0 or -EIO, restore STREAM's file position so that
+   the caller can fall back on some other method.  */
+int
+afalg_stream (FILE *stream, const char *alg,
+              void *resblock, ssize_t hashlen);
+
+# else
+
+static inline int
+afalg_buffer (const char *buffer, size_t len, const char *alg,
+              void *resblock, ssize_t hashlen)
+{
+  return -EAFNOSUPPORT;
+}
+
+static inline int
+afalg_stream (FILE *stream, const char *alg,
+              void *resblock, ssize_t hashlen)
+{
+  return -EAFNOSUPPORT;
+}
+
+# endif
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif /* AF_ALG_H */
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
index d62c2cf..b970451 100644
--- a/lisp/Makefile.in
+++ b/lisp/Makefile.in
@@ -30,6 +30,13 @@ EXEEXT = @EXEEXT@
 # limitation.
 XARGS_LIMIT = @XARGS_LIMIT@
 
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
+ifeq ($(HAVE_NATIVE_COMP),yes)
+ifndef NATIVE_FULL_AOT
+NATIVE_SKIP_NONDUMP = 1
+endif
+endif
+
 -include ${top_builddir}/src/verbose.mk
 
 FIND_DELETE = @FIND_DELETE@
@@ -82,8 +89,12 @@ COMPILE_FIRST = \
        $(lisp)/emacs-lisp/macroexp.elc \
        $(lisp)/emacs-lisp/cconv.elc    \
        $(lisp)/emacs-lisp/byte-opt.elc \
-       $(lisp)/emacs-lisp/bytecomp.elc \
-       $(lisp)/emacs-lisp/autoload.elc
+       $(lisp)/emacs-lisp/bytecomp.elc
+ifeq ($(HAVE_NATIVE_COMP),yes)
+COMPILE_FIRST += $(lisp)/emacs-lisp/comp.elc
+COMPILE_FIRST += $(lisp)/emacs-lisp/comp-cstr.elc
+endif
+COMPILE_FIRST += $(lisp)/emacs-lisp/autoload.elc
 
 # Files to compile early in compile-main.  Works around bug#25556.
 MAIN_FIRST = ./emacs-lisp/eieio.el ./emacs-lisp/eieio-base.el \
@@ -260,9 +271,15 @@ TAGS: ${ETAGS} ${tagsfiles}
 THEFILE = no-such-file
 .PHONY: $(THEFILE)c
 $(THEFILE)c:
+ifeq ($(HAVE_NATIVE_COMP),yes)
+       $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) \
+               -l comp -f byte-compile-refresh-preloaded \
+               -f batch-byte-native-compile-for-bootstrap $(THEFILE)
+else
        $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) \
                -l bytecomp -f byte-compile-refresh-preloaded \
                -f batch-byte-compile $(THEFILE)
+endif
 
 # Files MUST be compiled one by one. If we compile several files in a
 # row (i.e., in the same instance of Emacs) we can't make sure that
@@ -275,8 +292,14 @@ $(THEFILE)c:
 
 # An old-fashioned suffix rule, which, according to the GNU Make manual,
 # cannot have prerequisites.
+ifeq ($(HAVE_NATIVE_COMP),yes)
+.el.elc:
+       $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) \
+       -l comp -f batch-byte-native-compile-for-bootstrap $<
+else
 .el.elc:
        $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) -f batch-byte-compile $<
+endif
 
 .PHONY: compile-first compile-main compile compile-always
 
@@ -294,7 +317,13 @@ compile-first: $(COMPILE_FIRST)
 
 .PHONY: compile-targets
 # TARGETS is set dynamically in the recursive call from 'compile-main'.
+# Do not build comp.el unless necessary not to exceed max-specpdl-size and
+# max-lisp-eval-depth in normal builds.
+ifneq ($(HAVE_NATIVE_COMP),yes)
+compile-targets: $(filter-out ./emacs-lisp/comp-cstr.elc,$(filter-out 
./emacs-lisp/comp.elc,$(TARGETS)))
+else
 compile-targets: $(TARGETS)
+endif
 
 # Compile all the Elisp files that need it.  Beware: it approximates
 # 'no-byte-compile', so watch out for false-positives!
@@ -307,9 +336,11 @@ compile-main: gen-lisp compile-clean
              GREP_OPTIONS= grep '^;.*[^a-zA-Z]no-byte-compile: *t' $$el > 
/dev/null && \
              continue;                              \
          echo "$${el}c";                            \
-       done | xargs $(XARGS_LIMIT) echo) |          \
-       while read chunk; do                         \
-         $(MAKE) compile-targets TARGETS="$$chunk"; \
+       done | xargs $(XARGS_LIMIT) echo) |          \
+       while read chunk; do                         \
+         $(MAKE) compile-targets                    \
+                 NATIVE_DISABLED=$(NATIVE_SKIP_NONDUMP) \
+                 TARGETS="$$chunk";                 \
        done
 
 .PHONY: compile-clean
diff --git a/lisp/cus-dep.el b/lisp/cus-dep.el
index c14a45c..31a8960 100644
--- a/lisp/cus-dep.el
+++ b/lisp/cus-dep.el
@@ -109,6 +109,7 @@ Usage: emacs -batch -l ./cus-dep.el -f 
custom-make-dependencies DIRS"
             (string-match "\\`\\(.*\\)\\.el\\'" file)
             (let ((name (or generated-autoload-load-name ; see bug#5277
                             (file-name-nondirectory (match-string 1 file))))
+                  (load-true-file-name file)
                   (load-file-name file))
               (if (save-excursion
                     (re-search-forward
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el
index b9a3a32..dc8636f 100644
--- a/lisp/emacs-lisp/advice.el
+++ b/lisp/emacs-lisp/advice.el
@@ -2051,6 +2051,8 @@ in that CLASS."
                 function class name)))
     (error "ad-remove-advice: `%s' is not advised" function)))
 
+(declare-function comp-subr-trampoline-install "comp")
+
 ;;;###autoload
 (defun ad-add-advice (function advice class position)
   "Add a piece of ADVICE to FUNCTION's list of advices in CLASS.
@@ -2074,6 +2076,9 @@ mapped to the closest extremal position).
 If FUNCTION was not advised already, its advice info will be
 initialized.  Redefining a piece of advice whose name is part of
 the cache-id will clear the cache."
+  (when (and (featurep 'nativecomp)
+             (subr-primitive-p (symbol-function function)))
+    (comp-subr-trampoline-install function))
   (cond ((not (ad-is-advised function))
          (ad-initialize-advice-info function)
         (ad-set-advice-info-field
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el
index ae17039..b45984b 100644
--- a/lisp/emacs-lisp/autoload.el
+++ b/lisp/emacs-lisp/autoload.el
@@ -170,7 +170,9 @@ expression, in which case we want to handle forms 
differently."
                        define-inline cl-defun cl-defmacro cl-defgeneric
                        cl-defstruct pcase-defmacro))
            (macrop car)
-          (setq expand (let ((load-file-name file)) (macroexpand form)))
+          (setq expand (let ((load-true-file-name file)
+                              (load-file-name file))
+                          (macroexpand form)))
           (memq (car expand) '(progn prog1 defalias)))
       (make-autoload expand file 'expansion)) ;Recurse on the expansion.
 
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 43e9395..33b4d4b 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -2356,6 +2356,7 @@ If FOR-EFFECT is non-nil, the return value is assumed to 
be of no importance."
 ;;
 (eval-when-compile
  (or (byte-code-function-p (symbol-function 'byte-optimize-form))
+     (subr-native-elisp-p (symbol-function 'byte-optimize-form))
      (assq 'byte-code (symbol-function 'byte-optimize-form))
      (let ((byte-optimize nil)
           (byte-compile-warnings nil))
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index 119d397..aca5dcb 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -146,6 +146,11 @@ The return value of this function is not used."
       (list 'function-put (list 'quote f)
             ''lisp-indent-function (list 'quote val))))
 
+(defalias 'byte-run--set-speed
+  #'(lambda (f _args val)
+      (list 'function-put (list 'quote f)
+            ''speed (list 'quote val))))
+
 (defalias 'byte-run--set-completion
   #'(lambda (f _args val)
       (list 'function-put (list 'quote f)
@@ -173,6 +178,7 @@ If `error-free', drop calls even if 
`byte-compile-delete-errors' is nil.")
    (list 'compiler-macro #'byte-run--set-compiler-macro)
    (list 'doc-string #'byte-run--set-doc-string)
    (list 'indent #'byte-run--set-indent)
+   (list 'speed #'byte-run--set-speed)
    (list 'completion #'byte-run--set-completion)
    (list 'modes #'byte-run--set-modes))
   "List associating function properties to their macro expansion.
@@ -381,6 +387,10 @@ You don't need this.  (See bytecomp.el commentary for more 
details.)
   `(prog1
        (defun ,name ,arglist ,@body)
      (eval-and-compile
+       ;; Never native-compile defsubsts as we need the byte
+       ;; definition in `byte-compile-unfold-bcf' to perform the
+       ;; inlining (Bug#42664, Bug#43280, Bug#44209).
+       ,(byte-run--set-speed name nil -1)
        (put ',name 'byte-optimizer 'byte-compile-inline-expand))))
 
 (defvar advertised-signature-table (make-hash-table :test 'eq :weakness 'key))
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 4f91f0d..9be54ca 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -577,6 +577,50 @@ Each element is (INDEX . VALUE)")
 (defvar byte-compile-depth 0 "Current depth of execution stack.")
 (defvar byte-compile-maxdepth 0 "Maximum depth of execution stack.")
 
+;; The following is used by comp.el to spill data out of here.
+;;
+;; Spilling is done in 3 places:
+;;
+;; - `byte-compile-lapcode' to obtain the map bytecode -> LAP for any
+;;   code assembled.
+;;
+;; - `byte-compile-lambda' to obtain arglist doc and interactive spec
+;;   af any lambda compiled (including anonymous).
+;;
+;; - `byte-compile-file-form-defmumble' to obtain the list of
+;;   top-level forms as they would be outputted in the .elc file.
+;;
+
+(cl-defstruct byte-to-native-lambda
+  byte-func lap)
+
+;; Top level forms:
+(cl-defstruct byte-to-native-func-def
+  "Named function defined at top-level."
+  name c-name byte-func)
+(cl-defstruct byte-to-native-top-level
+  "All other top-level forms."
+  form lexical)
+
+(defvar byte-native-compiling nil
+  "Non nil while native compiling.")
+(defvar byte-native-qualities nil
+  "To spill default qualities from the compiled file.")
+(defvar byte-native-for-bootstrap nil
+  "Non nil while compiling for bootstrap."
+  ;; During bootstrap we produce both the .eln and the .elc together.
+  ;; Because the make target is the later this has to be produced as
+  ;; last to be resilient against build interruptions.
+)
+(defvar byte-to-native-lambdas-h nil
+  "Hash byte-code -> byte-to-native-lambda.")
+(defvar byte-to-native-top-level-forms nil
+  "List of top level forms.")
+(defvar byte-to-native-output-file nil
+  "Temporary file containing the byte-compilation output.")
+(defvar byte-to-native-plist-environment nil
+  "To spill `overriding-plist-environment'.")
+
 
 ;;; The byte codes; this information is duplicated in bytecomp.c
 
@@ -973,7 +1017,12 @@ CONST2 may be evaluated multiple times."
                    ;; it within 2 bytes in the byte string).
                    (puthash value pc hash-table))
                hash-table))
-    (apply 'unibyte-string (nreverse bytes))))
+    (let ((bytecode (apply 'unibyte-string (nreverse bytes))))
+      (when byte-native-compiling
+        ;; Spill LAP for the native compiler here.
+        (puthash bytecode (make-byte-to-native-lambda :lap lap)
+                 byte-to-native-lambdas-h))
+      bytecode)))
 
 
 ;;; compile-time evaluation
@@ -1702,7 +1751,11 @@ It is too wide if it has any lines longer than the 
largest of
          ;; cause macro calls in B to think they come from A.
          (current-load-list (list nil))
          )
-     ,@body))
+     (prog1
+         (progn ,@body)
+       (when byte-native-compiling
+         (setq byte-to-native-plist-environment
+               overriding-plist-environment)))))
 
 (defmacro displaying-byte-compile-warnings (&rest body)
   (declare (debug t))
@@ -2018,15 +2071,16 @@ See also `emacs-lisp-byte-compile-and-load'."
          (insert "\n")                 ; aaah, unix.
          (cond
           ((null target-file) nil)     ;We only wanted the warnings!
-          ((and (file-writable-p target-file)
-                 ;; We attempt to create a temporary file in the
-                 ;; target directory, so the target directory must be
-                 ;; writable.
-                 (file-writable-p
-                  (file-name-directory
-                   ;; Need to expand in case TARGET-FILE doesn't
-                   ;; include a directory (Bug#45287).
-                   (expand-file-name target-file))))
+          ((or byte-native-compiling
+               (and (file-writable-p target-file)
+                    ;; We attempt to create a temporary file in the
+                    ;; target directory, so the target directory must be
+                    ;; writable.
+                    (file-writable-p
+                     (file-name-directory
+                      ;; Need to expand in case TARGET-FILE doesn't
+                      ;; include a directory (Bug#45287).
+                      (expand-file-name target-file)))))
            ;; We must disable any code conversion here.
            (let* ((coding-system-for-write 'no-conversion)
                   ;; Write to a tempfile so that if another Emacs
@@ -2034,7 +2088,8 @@ See also `emacs-lisp-byte-compile-and-load'."
                   ;; parallel bootstrap), it does not risk getting a
                   ;; half-finished file.  (Bug#4196)
                   (tempfile
-                   (make-temp-file (expand-file-name target-file)))
+                   (make-temp-file (when (file-writable-p target-file)
+                                      (expand-file-name target-file))))
                   (default-modes (default-file-modes))
                   (temp-modes (logand default-modes #o600))
                   (desired-modes (logand default-modes #o666))
@@ -2053,8 +2108,16 @@ See also `emacs-lisp-byte-compile-and-load'."
              ;; emacs-lisp files in the build tree are
              ;; recompiled).  Previously this was accomplished by
              ;; deleting target-file before writing it.
-             (rename-file tempfile target-file t))
-           (or noninteractive (message "Wrote %s" target-file)))
+             (if byte-native-compiling
+                  (if byte-native-for-bootstrap
+                      ;; Defer elc final renaming.
+                      (setf byte-to-native-output-file
+                            (cons tempfile target-file))
+                    (delete-file tempfile))
+                (rename-file tempfile target-file t)))
+           (or noninteractive
+               byte-native-compiling
+               (message "Wrote %s" target-file)))
            ((file-writable-p target-file)
             ;; In case the target directory isn't writable (see e.g. 
Bug#44631),
             ;; try writing to the output file directly.  We must disable any
@@ -2174,6 +2237,17 @@ With argument ARG, insert value in current buffer after 
the form."
        (setq byte-compile-unresolved-functions nil)
         (setq byte-compile-noruntime-functions nil)
         (setq byte-compile-new-defuns nil)
+        (when byte-native-compiling
+          (defvar comp-speed)
+          (push `(comp-speed . ,comp-speed) byte-native-qualities)
+          (defvar comp-debug)
+          (push `(comp-debug . ,comp-debug) byte-native-qualities)
+          (defvar comp-native-driver-options)
+          (push `(comp-native-driver-options . ,comp-native-driver-options)
+                byte-native-qualities)
+          (defvar no-native-compile)
+          (push `(no-native-compile . ,no-native-compile)
+                byte-native-qualities))
 
        ;; Compile the forms from the input buffer.
        (while (progn
@@ -2246,6 +2320,10 @@ Call from the source buffer."
   ;; defalias calls are output directly by byte-compile-file-form-defmumble;
   ;; it does not pay to first build the defalias in defmumble and then parse
   ;; it here.
+  (when byte-native-compiling
+    ;; Spill output for the native compiler here
+    (push (make-byte-to-native-top-level :form form :lexical lexical-binding)
+          byte-to-native-top-level-forms))
   (let ((print-escape-newlines t)
         (print-length nil)
         (print-level nil)
@@ -2689,6 +2767,15 @@ not to take responsibility for the actual compilation of 
the code."
                  ;; If there's no doc string, provide -1 as the "doc string
                  ;; index" so that no element will be treated as a doc string.
                  (if (not (stringp (documentation code t))) -1 4)))
+            (when byte-native-compiling
+              ;; Spill output for the native compiler here.
+              (push (if macro
+                        (make-byte-to-native-top-level
+                         :form `(defalias ',name '(macro . ,code) nil)
+                         :lexical lexical-binding)
+                      (make-byte-to-native-func-def :name name
+                                                    :byte-func code))
+                    byte-to-native-top-level-forms))
             ;; Output the form by hand, that's much simpler than having
             ;; b-c-output-file-form analyze the defalias.
             (byte-compile-output-docform
@@ -2966,30 +3053,37 @@ for symbols generated by the byte compiler itself."
                                    reserved-csts)))
       ;; Build the actual byte-coded function.
       (cl-assert (eq 'byte-code (car-safe compiled)))
-      (apply #'make-byte-code
-             (if lexical-binding
-                 (byte-compile-make-args-desc arglist)
-               arglist)
-             (append
-              ;; byte-string, constants-vector, stack depth
-              (cdr compiled)
-              ;; optionally, the doc string.
-              (cond ((and lexical-binding arglist)
-                     ;; byte-compile-make-args-desc lost the args's names,
-                     ;; so preserve them in the docstring.
-                     (list (help-add-fundoc-usage doc arglist)))
-                    ((or doc int)
-                     (list doc)))
-              ;; optionally, the interactive spec (and the modes the
-              ;; command applies to).
-              (cond
-               ;; We have some command modes, so use the vector form.
-               (command-modes
-                (list (vector (nth 1 int) command-modes)))
-               ;; No command modes, use the simple form with just the
-               ;; interactive spec.
-               (int
-                (list (nth 1 int)))))))))
+      (let ((out
+            (apply #'make-byte-code
+                   (if lexical-binding
+                       (byte-compile-make-args-desc arglist)
+                     arglist)
+                   (append
+                    ;; byte-string, constants-vector, stack depth
+                    (cdr compiled)
+                    ;; optionally, the doc string.
+                    (cond ((and lexical-binding arglist)
+                           ;; byte-compile-make-args-desc lost the args's 
names,
+                           ;; so preserve them in the docstring.
+                           (list (help-add-fundoc-usage doc arglist)))
+                          ((or doc int)
+                           (list doc)))
+                    ;; optionally, the interactive spec (and the modes the
+                    ;; command applies to).
+                    (cond
+                     ;; We have some command modes, so use the vector form.
+                     (command-modes
+                       (list (vector (nth 1 int) command-modes)))
+                     ;; No command modes, use the simple form with just the
+                     ;; interactive spec.
+                     (int
+                       (list (nth 1 int))))))))
+       (when byte-native-compiling
+          (setf (byte-to-native-lambda-byte-func
+                 (gethash (cadr compiled)
+                          byte-to-native-lambdas-h))
+                out))
+       out))))
 
 (defvar byte-compile-reserved-constants 0)
 
@@ -5232,7 +5326,7 @@ Use with caution."
                    ;; so it can cause recompilation to fail.
                    (not (member (file-name-nondirectory f)
                                 '("pcase.el" "bytecomp.el" "macroexp.el"
-                                  "cconv.el" "byte-opt.el"))))
+                                  "cconv.el" "byte-opt.el" "comp.el"))))
           (message "Reloading stale %s" (file-name-nondirectory f))
           (condition-case nil
               (load f 'noerror nil 'nosuffix)
@@ -5313,13 +5407,15 @@ and corresponding effects."
 ;;
 (eval-when-compile
   (or (byte-code-function-p (symbol-function 'byte-compile-form))
+      (subr-native-elisp-p (symbol-function 'byte-compile-form))
       (assq 'byte-code (symbol-function 'byte-compile-form))
       (let ((byte-optimize nil)                ; do it fast
            (byte-compile-warnings nil))
        (mapc (lambda (x)
-               (or noninteractive (message "compiling %s..." x))
-               (byte-compile x)
-               (or noninteractive (message "compiling %s...done" x)))
+                (unless (subr-native-elisp-p x)
+                 (or noninteractive (message "compiling %s..." x))
+                 (byte-compile x)
+                 (or noninteractive (message "compiling %s...done" x))))
              '(byte-compile-normal-call
                byte-compile-form
                byte-compile-body
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index b7e5be9..d7e6c30 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2477,6 +2477,14 @@ values.  For compatibility, (cl-values A B C) is a 
synonym for (list A B C).
 (defmacro cl-the (type form)
   "Return FORM.  If type-checking is enabled, assert that it is of TYPE."
   (declare (indent 1) (debug (cl-type-spec form)))
+  ;; When native compiling possibly add the appropriate type hint.
+  (when (and (boundp 'byte-native-compiling)
+             byte-native-compiling)
+    (setf form
+          (cl-case type
+            (fixnum `(comp-hint-fixnum ,form))
+            (cons `(comp-hint-cons ,form))
+            (otherwise form))))
   (if (not (or (not (macroexp-compiling-p))
                (< cl--optimize-speed 3)
                (= cl--optimize-safety 3)))
@@ -2487,6 +2495,28 @@ values.  For compatibility, (cl-values A B C) is a 
synonym for (list A B C).
                         (list ',type ,temp ',form)))
               ,temp))))
 
+;;;###autoload
+(or (assq 'cl-optimize defun-declarations-alist)
+    (let ((x (list 'cl-optimize #'cl--optimize)))
+      (push x macro-declarations-alist)
+      (push x defun-declarations-alist)))
+
+(defun cl--optimize (f _args &rest qualities)
+  "Serve 'cl-optimize' in function declarations.
+Example:
+(defun foo (x)
+  (declare (cl-optimize (speed 3) (safety 0)))
+  x)"
+  ;; FIXME this should make use of `cl--declare-stack' but I suspect
+  ;; this mechanism should be reviewed first.
+  (cl-loop for (qly val) in qualities
+           do (cl-ecase qly
+                (speed
+                 (setf cl--optimize-speed val)
+                 (byte-run--set-speed f nil val))
+                (safety
+                 (setf cl--optimize-safety val)))))
+
 (defvar cl--proclaim-history t)    ; for future compilers
 (defvar cl--declare-stack t)       ; for future compilers
 
@@ -3556,6 +3586,10 @@ The type name can then be used in `cl-typecase', 
`cl-check-type', etc."
           (cl-function (lambda (&cl-defs ('*) ,@arglist) ,@body)))))
 
 (cl-deftype extended-char () '(and character (not base-char)))
+;; Define fixnum so `cl-typep' recognize it and the type check emitted
+;; by `cl-the' is effective.
+(cl-deftype fixnum () 'fixnump)
+(cl-deftype bignum () 'bignump)
 
 ;;; Additional functions that we can now define because we've defined
 ;;; `cl-defsubst' and `cl-typep'.
diff --git a/lisp/emacs-lisp/comp-cstr.el b/lisp/emacs-lisp/comp-cstr.el
new file mode 100644
index 0000000..2a50ceb
--- /dev/null
+++ b/lisp/emacs-lisp/comp-cstr.el
@@ -0,0 +1,1190 @@
+;;; comp-cstr.el --- native compiler constraint library -*- lexical-binding: t 
-*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.com>
+;; Keywords: lisp
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Constraint library in use by the native compiler.
+
+;; In LIMPLE each non immediate value is represented by a `comp-mvar'.
+;; The part concerning the set of all values the `comp-mvar' can
+;; assume is described into its constraint `comp-cstr'.  Each
+;; constraint consists in a triplet: type-set, value-set, range-set.
+;; This file provide set operations between constraints (union
+;; intersection and negation) plus routines to convert from and to a
+;; CL like type specifier.
+
+;;; Code:
+
+(require 'cl-lib)
+
+(defconst comp--typeof-types (mapcar (lambda (x)
+                                       (append x '(t)))
+                                     cl--typeof-types)
+  ;; TODO can we just add t in `cl--typeof-types'?
+  "Like `cl--typeof-types' but with t as common supertype.")
+
+(defconst comp--all-builtin-types
+  (append cl--all-builtin-types '(t))
+  "Likewise like `cl--all-builtin-types' but with t as common supertype.")
+
+(cl-defstruct (comp-cstr (:constructor comp-type-to-cstr
+                                       (type &aux
+                                            (null (eq type 'null))
+                                             (integer (eq type 'integer))
+                                            (typeset (if (or null integer)
+                                                         nil
+                                                       (list type)))
+                                            (valset (when null
+                                                      '(nil)))
+                                             (range (when integer
+                                                      '((- . +))))))
+                         (:constructor comp-value-to-cstr
+                                       (value &aux
+                                              (integer (integerp value))
+                                              (valset (unless integer
+                                                        (list value)))
+                                              (range (when integer
+                                                       `((,value . ,value))))
+                                              (typeset ())))
+                         (:constructor comp-irange-to-cstr
+                                       (irange &aux
+                                               (range (list irange))
+                                               (typeset ())))
+                         (:copier comp-cstr-shallow-copy))
+  "Internal representation of a type/value constraint."
+  (typeset '(t) :type list
+           :documentation "List of possible types the mvar can assume.
+Each element cannot be a subtype of any other element of this slot.")
+  (valset () :type list
+          :documentation "List of possible values the mvar can assume.
+Integer values are handled in the `range' slot.")
+  (range () :type list
+         :documentation "Integer interval.")
+  (neg nil :type boolean
+       :documentation "Non-nil if the constraint is negated"))
+
+(cl-defstruct comp-cstr-f
+  "Internal constraint representation for a function."
+  (args () :type list
+        :documentation "List of `comp-cstr' for its arguments.")
+  (ret nil :type (or comp-cstr comp-cstr-f)
+       :documentation "Returned value."))
+
+(cl-defstruct comp-cstr-ctxt
+  (union-typesets-mem (make-hash-table :test #'equal) :type hash-table
+                      :documentation "Serve memoization for
+`comp-union-typesets'.")
+  ;; TODO we should be able to just cons hash this.
+  (common-supertype-mem (make-hash-table :test #'equal) :type hash-table
+                        :documentation "Serve memoization for
+`comp-common-supertype'.")
+  (subtype-p-mem (make-hash-table :test #'equal) :type hash-table
+                 :documentation "Serve memoization for
+`comp-subtype-p-mem'.")
+  (union-1-mem-no-range (make-hash-table :test #'equal) :type hash-table
+                        :documentation "Serve memoization for
+`comp-cstr-union-1'.")
+  (union-1-mem-range (make-hash-table :test #'equal) :type hash-table
+                     :documentation "Serve memoization for
+`comp-cstr-union-1'.")
+  (intersection-mem (make-hash-table :test #'equal) :type hash-table
+                    :documentation "Serve memoization for
+`intersection-mem'."))
+
+(defmacro with-comp-cstr-accessors (&rest body)
+  "Define some quick accessor to reduce code vergosity in BODY."
+  (declare (debug (form body))
+           (indent defun))
+  `(cl-macrolet ((typeset (x)
+                          `(comp-cstr-typeset ,x))
+                 (valset (x)
+                         `(comp-cstr-valset ,x))
+                 (range (x)
+                        `(comp-cstr-range ,x))
+                 (neg (x)
+                      `(comp-cstr-neg ,x)))
+     ,@body))
+
+(defun comp-cstr-copy (cstr)
+  "Return a deep copy of CSTR."
+  (with-comp-cstr-accessors
+    (make-comp-cstr :typeset (copy-sequence (typeset cstr))
+                    :valset (copy-sequence (valset cstr))
+                    :range (copy-tree (range cstr))
+                    :neg (neg cstr))))
+
+(defsubst comp-cstr-empty-p (cstr)
+  "Return t if CSTR is equivalent to the `nil' type specifier or nil 
otherwise."
+  (with-comp-cstr-accessors
+    (and (null (typeset cstr))
+         (null (valset cstr))
+         (null (range cstr))
+         (null (neg cstr)))))
+
+(defsubst comp-cstr-null-p (cstr)
+  "Return t if CSTR is equivalent to the `null' type specifier, nil otherwise."
+  (with-comp-cstr-accessors
+    (and (null (typeset cstr))
+         (null (range cstr))
+         (null (neg cstr))
+         (equal (valset cstr) '(nil)))))
+
+(defun comp-cstrs-homogeneous (cstrs)
+  "Check if constraints CSTRS are all homogeneously negated or non-negated.
+Return `pos' if they are all positive, `neg' if they are all
+negated or nil othewise."
+  (cl-loop
+   for cstr in cstrs
+   unless (comp-cstr-neg cstr)
+     count t into n-pos
+   else
+     count t into n-neg
+   finally
+   (cond
+    ((zerop n-neg) (cl-return 'pos))
+    ((zerop n-pos) (cl-return 'neg)))))
+
+(defun comp-split-pos-neg (cstrs)
+  "Split constraints CSTRS into non-negated and negated.
+Return them as multiple value."
+  (cl-loop
+   for cstr in cstrs
+   if (comp-cstr-neg cstr)
+     collect cstr into negatives
+   else
+     collect cstr into positives
+   finally return (cl-values positives negatives)))
+
+;; So we can load comp-cstr.el and comp.el in non native compiled
+;; builds.
+(defvar comp-ctxt nil)
+
+(defvar comp-cstr-one (comp-value-to-cstr 1)
+  "Represent the integer immediate one.")
+
+(defvar comp-cstr-t (comp-type-to-cstr t)
+  "Represent the superclass t.")
+
+
+;;; Value handling.
+
+(defun comp-normalize-valset (valset)
+  "Sort and remove duplicates from VALSET then return it."
+  (cl-remove-duplicates
+   (cl-sort valset (lambda (x y)
+                     ;; We might want to use `sxhash-eql' for speed but
+                     ;; this is safer to keep tests stable.
+                     (< (sxhash-equal x)
+                       (sxhash-equal y))))
+   :test #'eq))
+
+(defun comp-union-valsets (&rest valsets)
+  "Union values present into VALSETS."
+  (comp-normalize-valset (cl-reduce #'cl-union valsets)))
+
+(defun comp-intersection-valsets (&rest valsets)
+  "Union values present into VALSETS."
+  (comp-normalize-valset (cl-reduce #'cl-intersection valsets)))
+
+
+;;; Type handling.
+
+(defun comp-normalize-typeset (typeset)
+  "Sort TYPESET and return it."
+  (cl-sort (cl-remove-duplicates typeset)
+           (lambda (x y)
+             (string-lessp (symbol-name x)
+                           (symbol-name y)))))
+
+(defun comp-supertypes (type)
+  "Return a list of pairs (supertype . hierarchy-level) for TYPE."
+  (cl-loop
+   named outer
+   with found = nil
+   for l in comp--typeof-types
+   do (cl-loop
+       for x in l
+       for i from (length l) downto 0
+       when (eq type x)
+         do (setf found t)
+       when found
+         collect `(,x . ,i) into res
+       finally (when found
+                 (cl-return-from outer res)))))
+
+(defun comp-common-supertype-2 (type1 type2)
+  "Return the first common supertype of TYPE1 TYPE2."
+  (when-let ((types (cl-intersection
+                     (comp-supertypes type1)
+                     (comp-supertypes type2)
+                     :key #'car)))
+    (car (cl-reduce (lambda (x y)
+                      (if (> (cdr x) (cdr y)) x y))
+                    types))))
+
+(defun comp-common-supertype (&rest types)
+  "Return the first common supertype of TYPES."
+  (or (gethash types (comp-cstr-ctxt-common-supertype-mem comp-ctxt))
+      (puthash types
+               (cl-reduce #'comp-common-supertype-2 types)
+               (comp-cstr-ctxt-common-supertype-mem comp-ctxt))))
+
+(defsubst comp-subtype-p (type1 type2)
+  "Return t if TYPE1 is a subtype of TYPE2 or nil otherwise."
+  (let ((types (cons type1 type2)))
+    (or (gethash types (comp-cstr-ctxt-subtype-p-mem comp-ctxt))
+        (puthash types
+                 (eq (comp-common-supertype-2 type1 type2) type2)
+                 (comp-cstr-ctxt-subtype-p-mem comp-ctxt)))))
+
+(defun comp-union-typesets (&rest typesets)
+  "Union types present into TYPESETS."
+  (or (gethash typesets (comp-cstr-ctxt-union-typesets-mem comp-ctxt))
+      (puthash typesets
+               (cl-loop
+                with types = (apply #'append typesets)
+                with res = '()
+                for lane in comp--typeof-types
+                do (cl-loop
+                    with last = nil
+                    for x in lane
+                    when (memq x types)
+                      do (setf last x)
+                    finally (when last
+                              (push last res)))
+                finally return (comp-normalize-typeset res))
+               (comp-cstr-ctxt-union-typesets-mem comp-ctxt))))
+
+(defun comp-intersect-two-typesets (t1 t2)
+  "Intersect typesets T1 and T2."
+  (with-comp-cstr-accessors
+    (cl-loop
+     for types in (list t1 t2)
+     for other-types in (list t2 t1)
+     append
+     (cl-loop
+      for type in types
+      when (cl-some (lambda (x)
+                      (comp-subtype-p type x))
+                    other-types)
+      collect type))))
+
+(defun comp-intersect-typesets (&rest typesets)
+  "Intersect types present into TYPESETS."
+  (unless (cl-some #'null typesets)
+    (if (length= typesets 1)
+        (car typesets)
+      (comp-normalize-typeset
+       (cl-reduce #'comp-intersect-two-typesets typesets)))))
+
+
+;;; Integer range handling
+
+(defsubst comp-star-or-num-p (x)
+  (or (numberp x) (eq '* x)))
+
+(defsubst comp-range-1+ (x)
+  (if (symbolp x)
+      x
+    (1+ x)))
+
+(defsubst comp-range-1- (x)
+  (if (symbolp x)
+      x
+    (1- x)))
+
+(defsubst comp-range-+ (x y)
+  (pcase (cons x y)
+    ((or '(+ . -) '(- . +)) '??)
+    ((or `(- . ,_) `(,_ . -)) '-)
+    ((or `(+ . ,_) `(,_ . +)) '+)
+    (_ (+ x y))))
+
+(defsubst comp-range-- (x y)
+  (pcase (cons x y)
+    ((or '(+ . +) '(- . -)) '??)
+    ('(+ . -) '+)
+    ('(- . +) '-)
+    ((or `(+ . ,_) `(,_ . -)) '+)
+    ((or `(- . ,_) `(,_ . +)) '-)
+    (_ (- x y))))
+
+(defsubst comp-range-< (x y)
+  (cond
+   ((eq x '+) nil)
+   ((eq x '-) t)
+   ((eq y '+) t)
+   ((eq y '-) nil)
+   (t (< x y))))
+
+(defsubst comp-cstr-smallest-in-range (range)
+  "Smallest entry in RANGE."
+  (caar range))
+
+(defsubst comp-cstr-greatest-in-range (range)
+  "Greater entry in RANGE."
+  (cdar (last range)))
+
+(defun comp-range-union (&rest ranges)
+  "Combine integer intervals RANGES by union set operation."
+  (cl-loop
+   with all-ranges = (apply #'append ranges)
+   with lows = (mapcar (lambda (x)
+                         (cons (comp-range-1- (car x)) 'l))
+                       all-ranges)
+   with highs = (mapcar (lambda (x)
+                          (cons (cdr x) 'h))
+                        all-ranges)
+   with nest = 0
+   with low = nil
+   with res = ()
+   for (i . x) in (cl-sort (nconc lows highs) #'comp-range-< :key #'car)
+   if (eq x 'l)
+   do
+   (when (zerop nest)
+     (setf low i))
+   (cl-incf nest)
+   else
+   do
+   (when (= nest 1)
+     (push `(,(comp-range-1+ low) . ,i) res))
+   (cl-decf nest)
+   finally return (reverse res)))
+
+(defun comp-range-intersection (&rest ranges)
+  "Combine integer intervals RANGES by intersecting."
+  (cl-loop
+   with all-ranges = (apply #'append ranges)
+   with n-ranges = (length ranges)
+   with lows = (mapcar (lambda (x)
+                         (cons (car x) 'l))
+                       all-ranges)
+   with highs = (mapcar (lambda (x)
+                          (cons (cdr x) 'h))
+                        all-ranges)
+   with nest = 0
+   with low = nil
+   with res = ()
+   for (i . x) in (cl-sort (nconc lows highs) #'comp-range-< :key #'car)
+   initially (when (cl-some #'null ranges)
+               ;; Intersecting with a null range always results in a
+               ;; null range.
+               (cl-return '()))
+   if (eq x 'l)
+   do
+   (cl-incf nest)
+   (when (= nest n-ranges)
+     (setf low i))
+   else
+   do
+   (when (= nest n-ranges)
+     (push `(,low . ,i)
+           res))
+   (cl-decf nest)
+   finally return (reverse res)))
+
+(defun comp-range-negation (range)
+  "Negate range RANGE."
+  (if (null range)
+      '((- . +))
+    (cl-loop
+     with res = ()
+     with last-h = '-
+     for (l . h) in range
+     unless (eq l '-)
+     do (push `(,(comp-range-1+ last-h) . ,(1- l)) res)
+     do (setf last-h h)
+     finally
+     (unless (eq '+ last-h)
+       (push `(,(1+ last-h) . +) res))
+     (cl-return (reverse res)))))
+
+(defsubst comp-cstr-set-cmp-range (dst old-dst ext-range)
+  "Support range comparison functions."
+  (with-comp-cstr-accessors
+    (if ext-range
+        (setf (typeset dst) (when (cl-some (lambda (x)
+                                             (comp-subtype-p 'float x))
+                                           (typeset old-dst))
+                                '(float))
+              (valset dst) ()
+              (range dst) (if (range old-dst)
+                              (comp-range-intersection (range old-dst)
+                                                       ext-range)
+                            ext-range)
+              (neg dst) nil)
+      (setf (typeset dst) (typeset old-dst)
+            (valset dst) (valset old-dst)
+            (range dst) (range old-dst)
+            (neg dst) (neg old-dst)))))
+
+(defmacro comp-cstr-set-range-for-arithm (dst src1 src2 &rest range-body)
+  ;; Prevent some code duplication for `comp-cstr-add-2'
+  ;; `comp-cstr-sub-2'.
+  (declare (debug (range-body))
+           (indent defun))
+  `(with-comp-cstr-accessors
+     (when-let ((r1 (range ,src1))
+                (r2 (range ,src2)))
+       (let* ((l1 (comp-cstr-smallest-in-range r1))
+              (l2 (comp-cstr-smallest-in-range r2))
+              (h1 (comp-cstr-greatest-in-range r1))
+              (h2 (comp-cstr-greatest-in-range r2)))
+         (setf (typeset ,dst) (when (cl-some (lambda (x)
+                                               (comp-subtype-p 'float x))
+                                             (append (typeset src1)
+                                                     (typeset src2)))
+                                '(float))
+               (range ,dst) ,@range-body)))))
+
+(defun comp-cstr-add-2 (dst src1 src2)
+  "Sum SRC1 and SRC2 into DST."
+  (comp-cstr-set-range-for-arithm dst src1 src2
+    `((,(comp-range-+ l1 l2) . ,(comp-range-+ h1 h2)))))
+
+(defun comp-cstr-sub-2 (dst src1 src2)
+  "Subtract SRC1 and SRC2 into DST."
+  (comp-cstr-set-range-for-arithm dst src1 src2
+    (let ((l (comp-range-- l1 h2))
+          (h (comp-range-- h1 l2)))
+      (if (or (eq l '??) (eq h '??))
+          '((- . +))
+        `((,l . ,h))))))
+
+
+;;; Union specific code.
+
+(defun comp-cstr-union-homogeneous-no-range (dst &rest srcs)
+  "As `comp-cstr-union' but escluding the irange component.
+All SRCS constraints must be homogeneously negated or non-negated."
+
+  ;; Type propagation.
+  (setf (comp-cstr-typeset dst)
+        (apply #'comp-union-typesets (mapcar #'comp-cstr-typeset srcs)))
+
+  ;; Value propagation.
+  (setf (comp-cstr-valset dst)
+        (comp-normalize-valset
+         (cl-loop
+          with values = (mapcar #'comp-cstr-valset srcs)
+          ;; TODO sort.
+          for v in (cl-remove-duplicates (apply #'append values)
+                                         :test #'equal)
+          ;; We propagate only values those types are not already
+          ;; into typeset.
+          when (cl-notany (lambda (x)
+                            (comp-subtype-p (type-of v) x))
+                          (comp-cstr-typeset dst))
+          collect v)))
+
+  dst)
+
+(defun comp-cstr-union-homogeneous (range dst &rest srcs)
+  "Combine SRCS by union set operation setting the result in DST.
+Do range propagation when RANGE is non-nil.
+All SRCS constraints must be homogeneously negated or non-negated.
+DST is returned."
+  (apply #'comp-cstr-union-homogeneous-no-range dst srcs)
+  ;; Range propagation.
+  (setf (comp-cstr-neg dst)
+        (when srcs
+          (comp-cstr-neg (car srcs)))
+
+        (comp-cstr-range dst)
+        (when (cl-notany (lambda (x)
+                           (comp-subtype-p 'integer x))
+                         (comp-cstr-typeset dst))
+          (if range
+              (apply #'comp-range-union
+                     (mapcar #'comp-cstr-range srcs))
+            '((- . +)))))
+  dst)
+
+(cl-defun comp-cstr-union-1-no-mem (range &rest srcs)
+  "Combine SRCS by union set operation setting the result in DST.
+Do range propagation when RANGE is non-nil.
+Non memoized version of `comp-cstr-union-1'.
+DST is returned."
+  (with-comp-cstr-accessors
+    (let ((dst (make-comp-cstr)))
+      (cl-flet ((give-up ()
+                         (setf (typeset dst) '(t)
+                               (valset dst) ()
+                               (range dst) ()
+                               (neg dst) nil)
+                         (cl-return-from comp-cstr-union-1-no-mem dst)))
+
+        ;; Check first if we are in the simple case of all input non-negate
+        ;; or negated so we don't have to cons.
+        (when-let ((res (comp-cstrs-homogeneous srcs)))
+          (apply #'comp-cstr-union-homogeneous range dst srcs)
+          (cl-return-from comp-cstr-union-1-no-mem dst))
+
+        ;; Some are negated and some are not
+        (cl-multiple-value-bind (positives negatives) (comp-split-pos-neg srcs)
+          (let* ((pos (apply #'comp-cstr-union-homogeneous range
+                             (make-comp-cstr) positives))
+                 ;; We'll always use neg as result as this is almost
+                 ;; always necessary for describing open intervals
+                 ;; resulting from negated constraints.
+                 (neg (apply #'comp-cstr-union-homogeneous range
+                             (make-comp-cstr :neg t) negatives)))
+            ;; Type propagation.
+            (when (and (typeset pos)
+                       ;; When every pos type is a subtype of some neg ones.
+                       (cl-every (lambda (x)
+                                   (cl-some (lambda (y)
+                                              (comp-subtype-p x y))
+                                            (append (typeset neg)
+                                                    (when (range neg)
+                                                      '(integer)))))
+                                 (typeset pos)))
+              ;; This is a conservative choice, ATM we can't represent such
+              ;; a disjoint set of types unless we decide to add a new slot
+              ;; into `comp-cstr' or adopt something like
+              ;; `intersection-type' `union-type' in SBCL.  Keep it
+              ;; "simple" for now.
+              (give-up))
+
+            ;; When every neg type is a subtype of some pos one.
+            ;; In case return pos.
+            (when (and (typeset neg)
+                       (cl-every (lambda (x)
+                                   (cl-some (lambda (y)
+                                              (comp-subtype-p x y))
+                                            (append (typeset pos)
+                                                    (when (range pos)
+                                                      '(integer)))))
+                                 (typeset neg)))
+              (setf (typeset dst) (typeset pos)
+                    (valset dst) (valset pos)
+                    (range dst) (range pos)
+                    (neg dst) nil)
+              (cl-return-from comp-cstr-union-1-no-mem dst))
+
+            ;; Verify disjoint condition between positive types and
+            ;; negative types coming from values, in case give-up.
+            (let ((neg-value-types (nconc (mapcar #'type-of (valset neg))
+                                          (when (range neg)
+                                            '(integer)))))
+              (when (cl-some (lambda (x)
+                               (cl-some (lambda (y)
+                                          (and (not (eq y x))
+                                               (comp-subtype-p y x)))
+                                        neg-value-types))
+                             (typeset pos))
+                (give-up)))
+
+            ;; Value propagation.
+            (cond
+             ((and (valset pos) (valset neg)
+                   (equal (comp-union-valsets (valset pos) (valset neg))
+                          (valset pos)))
+              ;; Pos is a superset of neg.
+              (give-up))
+             ((cl-some (lambda (x)
+                         (cl-some (lambda (y)
+                                    (comp-subtype-p y x))
+                                  (mapcar #'type-of (valset pos))))
+                       (typeset neg))
+              (give-up))
+             (t
+              ;; pos is a subset or eq to neg
+              (setf (valset neg)
+                    (cl-nset-difference (valset neg) (valset pos)))))
+
+            ;; Range propagation
+            (when range
+              ;; Handle apart (or (integer 1 1) (not (integer 1 1)))
+              ;; like cases.
+              (if (and (range pos) (range neg)
+                       (equal (range pos) (range neg)))
+                  (give-up)
+                (setf (range neg)
+                      (comp-range-negation
+                       (comp-range-union
+                        (comp-range-negation (range neg))
+                        (range pos))))))
+
+            (if (comp-cstr-empty-p neg)
+                (setf (typeset dst) (typeset pos)
+                      (valset dst) (valset pos)
+                      (range dst) (range pos)
+                      (neg dst) nil)
+              (setf (typeset dst) (typeset neg)
+                    (valset dst) (valset neg)
+                    (range dst) (range neg)
+                    (neg dst) (neg neg)))))
+
+        ;; (not null) => t
+        (when (and (neg dst)
+                   (null (typeset dst))
+                   (null (valset dst))
+                   (null (range dst)))
+          (give-up)))
+
+      dst)))
+
+(defun comp-cstr-union-1 (range dst &rest srcs)
+  "Combine SRCS by union set operation setting the result in DST.
+Do range propagation when RANGE is non-nil.
+DST is returned."
+  (with-comp-cstr-accessors
+    (let* ((mem-h (if range
+                      (comp-cstr-ctxt-union-1-mem-range comp-ctxt)
+                    (comp-cstr-ctxt-union-1-mem-no-range comp-ctxt)))
+           (res (or (gethash srcs mem-h)
+                    (puthash
+                     (mapcar #'comp-cstr-copy srcs)
+                     (apply #'comp-cstr-union-1-no-mem range srcs)
+                     mem-h))))
+      (setf (typeset dst) (typeset res)
+            (valset dst) (valset res)
+            (range dst) (range res)
+            (neg dst) (neg res))
+      res)))
+
+(cl-defun comp-cstr-intersection-homogeneous (dst &rest srcs)
+  "Combine SRCS by intersection set operation setting the result in DST.
+All SRCS constraints must be homogeneously negated or non-negated.
+DST is returned."
+
+  (with-comp-cstr-accessors
+    (when (cl-some #'comp-cstr-empty-p srcs)
+      (setf (valset dst) nil
+            (range dst) nil
+            (typeset dst) nil)
+      (cl-return-from comp-cstr-intersection-homogeneous dst))
+
+    (setf (neg dst) (when srcs
+                      (neg (car srcs))))
+
+    ;; Type propagation.
+    (setf (typeset dst)
+          (apply #'comp-intersect-typesets
+                 (mapcar #'comp-cstr-typeset srcs)))
+
+    ;; Value propagation.
+    (setf (valset dst)
+          (comp-normalize-valset
+           (cl-loop
+            for src in srcs
+            append
+            (cl-loop
+             for val in (valset src)
+             ;; If (member value) is subtypep of all other sources then
+             ;; is good to be colleted.
+             when (cl-every (lambda (s)
+                              (or (memql val (valset s))
+                                  (cl-some (lambda (type)
+                                             (cl-typep val type))
+                                           (typeset s))))
+                            (remq src srcs))
+             collect val))))
+
+    ;; Range propagation.
+    (setf (range dst)
+          ;; Do range propagation only if the destination typeset
+          ;; doesn't cover it already.
+          (unless (cl-some (lambda (type)
+                             (comp-subtype-p 'integer type))
+                           (typeset dst))
+            (apply #'comp-range-intersection
+                   (cl-loop
+                    for src in srcs
+                    ;; Collect effective ranges.
+                    collect (or (range src)
+                                (when (cl-some (lambda (s)
+                                                 (comp-subtype-p 'integer s))
+                                               (typeset src))
+                                  '((- . +))))))))
+
+    dst))
+
+(cl-defun comp-cstr-intersection-no-mem (&rest srcs)
+  "Combine SRCS by intersection set operation.
+Non memoized version of `comp-cstr-intersection-no-mem'."
+  (let ((dst (make-comp-cstr)))
+    (with-comp-cstr-accessors
+      (cl-flet ((return-empty ()
+                              (setf (typeset dst) ()
+                                    (valset dst) ()
+                                    (range dst) ()
+                                    (neg dst) nil)
+                              (cl-return-from comp-cstr-intersection-no-mem 
dst)))
+        (when-let ((res (comp-cstrs-homogeneous srcs)))
+          (if (eq res 'neg)
+              (apply #'comp-cstr-union-homogeneous t dst srcs)
+            (apply #'comp-cstr-intersection-homogeneous dst srcs))
+          (cl-return-from comp-cstr-intersection-no-mem dst))
+
+        ;; Some are negated and some are not
+        (cl-multiple-value-bind (positives negatives) (comp-split-pos-neg srcs)
+          (let* ((pos (apply #'comp-cstr-intersection-homogeneous
+                             (make-comp-cstr) positives))
+                 (neg (apply #'comp-cstr-intersection-homogeneous
+                             (make-comp-cstr) negatives)))
+
+            ;; In case pos is not relevant return directly the content
+            ;; of neg.
+            (when (equal (typeset pos) '(t))
+              (setf (typeset dst) (typeset neg)
+                    (valset dst) (valset neg)
+                    (range dst) (range neg)
+                    (neg dst) t)
+
+              ;; (not t) => nil
+              (when (and (null (valset dst))
+                         (null (range dst))
+                         (neg dst)
+                         (equal '(t) (typeset dst)))
+                (setf (typeset dst) ()
+                      (neg dst) nil))
+
+              (cl-return-from comp-cstr-intersection-no-mem dst))
+
+            (when (cl-some
+                   (lambda (ty)
+                     (memq ty (typeset neg)))
+                   (typeset pos))
+              (return-empty))
+
+            ;; Some negated types are subtypes of some non-negated one.
+            ;; Transform the corresponding set of types from neg to pos.
+            (cl-loop
+             for neg-type in (typeset neg)
+             do (cl-loop
+                 for pos-type in (copy-sequence (typeset pos))
+                 when (and (not (eq neg-type pos-type))
+                           (comp-subtype-p neg-type pos-type))
+                   do (cl-loop
+                       with found
+                       for (type . _) in (comp-supertypes neg-type)
+                       when found
+                         collect type into res
+                       when (eq type pos-type)
+                         do (setf (typeset pos) (cl-union (typeset pos) res))
+                            (cl-return)
+                       when (eq type neg-type)
+                         do (setf found t))))
+
+            (setf (range pos)
+                  (comp-range-intersection (range pos)
+                                           (comp-range-negation (range neg))))
+
+            ;; Return a non negated form.
+            (setf (typeset dst) (typeset pos)
+                  (valset dst) (valset pos)
+                  (range dst) (range pos)
+                  (neg dst) nil)))
+        dst))))
+
+
+;;; Entry points.
+
+(defun comp-cstr-imm-vld-p (cstr)
+  "Return t if one and only one immediate value can be extracted from CSTR."
+  (with-comp-cstr-accessors
+    (when (and (null (typeset cstr))
+               (null (neg cstr)))
+      (let* ((v (valset cstr))
+             (r (range cstr))
+             (valset-len (length v))
+             (range-len (length r)))
+        (if (and (= valset-len 1)
+                 (= range-len 0))
+            t
+          (when (and (= valset-len 0)
+                     (= range-len 1))
+            (let* ((low (caar r))
+                   (high (cdar r)))
+              (and (integerp low)
+                   (integerp high)
+                   (= low high)))))))))
+
+(defun comp-cstr-imm (cstr)
+  "Return the immediate value of CSTR.
+`comp-cstr-imm-vld-p' *must* be satisfied before calling
+`comp-cstr-imm'."
+  (declare (gv-setter
+            (lambda (val)
+              `(with-comp-cstr-accessors
+                 (if (integerp ,val)
+                     (setf (typeset ,cstr) nil
+                           (range ,cstr) (list (cons ,val ,val)))
+                   (setf (typeset ,cstr) nil
+                         (valset ,cstr) (list ,val)))))))
+  (with-comp-cstr-accessors
+    (let ((v (valset cstr)))
+      (if (length= v 1)
+          (car v)
+        (caar (range cstr))))))
+
+(defun comp-cstr-fixnum-p (cstr)
+  "Return t if CSTR is certainly a fixnum."
+  (with-comp-cstr-accessors
+    (when (null (neg cstr))
+      (when-let (range (range cstr))
+        (let* ((low (caar range))
+               (high (cdar (last range))))
+          (unless (or (eq low '-)
+                      (< low most-negative-fixnum)
+                      (eq high '+)
+                      (> high most-positive-fixnum))
+            t))))))
+
+(defun comp-cstr-symbol-p (cstr)
+  "Return t if CSTR is certainly a symbol."
+  (with-comp-cstr-accessors
+    (and (null (range cstr))
+         (null (neg cstr))
+         (or (and (null (valset cstr))
+                  (equal (typeset cstr) '(symbol)))
+             (and (or (null (typeset cstr))
+                      (equal (typeset cstr) '(symbol)))
+                  (cl-every #'symbolp (valset cstr)))))))
+
+(defsubst comp-cstr-cons-p (cstr)
+  "Return t if CSTR is certainly a cons."
+  (with-comp-cstr-accessors
+    (and (null (valset cstr))
+         (null (range cstr))
+         (null (neg cstr))
+         (equal (typeset cstr) '(cons)))))
+
+(defun comp-cstr-= (dst op1 op2)
+  "Constraint OP1 being = OP2 setting the result into DST."
+  (with-comp-cstr-accessors
+    (cl-flet ((relax-cstr (cstr)
+                (setf cstr (comp-cstr-shallow-copy cstr))
+                ;; If can be any float extend it to all integers.
+                (when (memq 'float (typeset cstr))
+                  (setf (range cstr) '((- . +))))
+                ;; For each float value that can be represented
+                ;; precisely as an integer add the integer as well.
+                (cl-loop
+                 for v in (valset cstr)
+                 do
+                 (when-let* ((ok (floatp v))
+                             (truncated (ignore-error overflow-error
+                                          (truncate v)))
+                             (ok (= v truncated)))
+                   (push (cons truncated truncated) (range cstr))))
+                (cl-loop
+                 with vals-to-add
+                 for (l . h) in (range cstr)
+                 ;; If an integer range reduces to single value add
+                 ;; its float value too.
+                 if (eql l h)
+                   do (push (float l) vals-to-add)
+                 ;; Otherwise can be any float.
+                 else
+                   do (cl-pushnew 'float (typeset cstr))
+                      (cl-return cstr)
+                 finally (setf (valset cstr)
+                               (append vals-to-add (valset cstr))))
+                (when (memql 0.0 (valset cstr))
+                  (cl-pushnew -0.0 (valset cstr)))
+                (when (memql -0.0 (valset cstr))
+                  (cl-pushnew 0.0 (valset cstr)))
+                cstr))
+      (comp-cstr-intersection dst (relax-cstr op1) (relax-cstr op2)))))
+
+(defun comp-cstr-> (dst old-dst src)
+  "Constraint DST being > than SRC.
+SRC can be either a comp-cstr or an integer."
+  (with-comp-cstr-accessors
+    (let ((ext-range
+           (if (integerp src)
+               `((,(1+ src) . +))
+             (when-let* ((range (range src))
+                         (low (comp-cstr-smallest-in-range range))
+                         (okay (integerp low)))
+               `((,(1+ low) . +))))))
+      (comp-cstr-set-cmp-range dst old-dst ext-range))))
+
+(defun comp-cstr->= (dst old-dst src)
+  "Constraint DST being >= than SRC.
+SRC can be either a comp-cstr or an integer."
+  (with-comp-cstr-accessors
+    (let ((ext-range
+           (if (integerp src)
+               `((,src . +))
+             (when-let* ((range (range src))
+                         (low (comp-cstr-smallest-in-range range))
+                         (okay (integerp low)))
+               `((,low . +))))))
+      (comp-cstr-set-cmp-range dst old-dst ext-range))))
+
+(defun comp-cstr-< (dst old-dst src)
+  "Constraint DST being < than SRC.
+SRC can be either a comp-cstr or an integer."
+  (with-comp-cstr-accessors
+    (let ((ext-range
+           (if (integerp src)
+               `((- . ,(1- src)))
+             (when-let* ((range (range src))
+                         (low (comp-cstr-greatest-in-range range))
+                         (okay (integerp low)))
+               `((- . ,(1- low)))))))
+      (comp-cstr-set-cmp-range dst old-dst ext-range))))
+
+(defun comp-cstr-<= (dst old-dst src)
+  "Constraint DST being > than SRC.
+SRC can be either a comp-cstr or an integer."
+  (with-comp-cstr-accessors
+    (let ((ext-range
+           (if (integerp src)
+               `((- . ,src))
+             (when-let* ((range (range src))
+                         (low (comp-cstr-greatest-in-range range))
+                         (okay (integerp low)))
+               `((- . ,low))))))
+      (comp-cstr-set-cmp-range dst old-dst ext-range))))
+
+(defun comp-cstr-add (dst srcs)
+  "Sum SRCS into DST."
+  (comp-cstr-add-2 dst (cl-first srcs) (cl-second srcs))
+  (cl-loop
+   for src in (nthcdr 2 srcs)
+   do (comp-cstr-add-2 dst dst src)))
+
+(defun comp-cstr-sub (dst srcs)
+  "Subtract SRCS into DST."
+  (comp-cstr-sub-2 dst (cl-first srcs) (cl-second srcs))
+  (cl-loop
+   for src in (nthcdr 2 srcs)
+   do (comp-cstr-sub-2 dst dst src)))
+
+(defun comp-cstr-union-no-range (dst &rest srcs)
+  "Combine SRCS by union set operation setting the result in DST.
+Do not propagate the range component.
+DST is returned."
+  (apply #'comp-cstr-union-1 nil dst srcs))
+
+(defun comp-cstr-union (dst &rest srcs)
+  "Combine SRCS by union set operation setting the result in DST.
+DST is returned."
+  (apply #'comp-cstr-union-1 t dst srcs))
+
+(defun comp-cstr-union-make (&rest srcs)
+  "Combine SRCS by union set operation and return a new constraint."
+  (apply #'comp-cstr-union (make-comp-cstr) srcs))
+
+(defun comp-cstr-intersection (dst &rest srcs)
+  "Combine SRCS by intersection set operation setting the result in DST.
+DST is returned."
+  (with-comp-cstr-accessors
+    (let* ((mem-h (comp-cstr-ctxt-intersection-mem comp-ctxt))
+           (res (or (gethash srcs mem-h)
+                    (puthash
+                     (mapcar #'comp-cstr-copy srcs)
+                     (apply #'comp-cstr-intersection-no-mem srcs)
+                     mem-h))))
+      (setf (typeset dst) (typeset res)
+            (valset dst) (valset res)
+            (range dst) (range res)
+            (neg dst) (neg res))
+      res)))
+
+(defun comp-cstr-intersection-no-hashcons (dst &rest srcs)
+  "Combine SRCS by intersection set operation setting the result in DST.
+Non hash consed values are not propagated as values but rather
+promoted to their types.
+DST is returned."
+  (with-comp-cstr-accessors
+    (apply #'comp-cstr-intersection dst srcs)
+    (if (and (neg dst)
+             (valset dst)
+             (cl-notevery #'symbolp (valset dst)))
+        (setf (valset dst) ()
+              (typeset dst) '(t)
+              (range dst) ()
+              (neg dst) nil)
+      (let (strip-values strip-types)
+        (cl-loop for v in (valset dst)
+                 unless (symbolp v)
+                   do (push v strip-values)
+                      (push (type-of v) strip-types))
+        (when strip-values
+          (setf (typeset dst) (comp-union-typesets (typeset dst) strip-types)
+                (valset dst) (cl-set-difference (valset dst) strip-values)))
+        (cl-loop for (l . h) in (range dst)
+                 when (or (bignump l) (bignump h))
+                 do (setf (range dst) '((- . +)))
+                    (cl-return))))
+    dst))
+
+(defun comp-cstr-intersection-make (&rest srcs)
+  "Combine SRCS by intersection set operation and return a new constraint."
+  (apply #'comp-cstr-intersection (make-comp-cstr) srcs))
+
+(defun comp-cstr-negation (dst src)
+  "Negate SRC setting the result in DST.
+DST is returned."
+  (with-comp-cstr-accessors
+    (cond
+     ((and (null (valset src))
+           (null (range src))
+           (null (neg src))
+           (equal (typeset src) '(t)))
+      (setf (typeset dst) ()
+            (valset dst) ()
+            (range dst) nil
+            (neg dst) nil))
+     ((and (null (valset src))
+           (null (range src))
+           (null (neg src))
+           (null (typeset src)))
+      (setf (typeset dst) '(t)
+            (valset dst) ()
+            (range dst) nil
+            (neg dst) nil))
+     (t (setf (typeset dst) (typeset src)
+              (valset dst) (valset src)
+              (range dst) (range src)
+              (neg dst) (not (neg src)))))
+    dst))
+
+(defun comp-cstr-value-negation (dst src)
+  "Negate values in SRC setting the result in DST.
+DST is returned."
+  (with-comp-cstr-accessors
+    (if (or (valset src) (range src))
+        (setf (typeset dst) ()
+              (valset dst) (valset src)
+              (range dst) (range src)
+              (neg dst) (not (neg src)))
+      (setf (typeset dst) (typeset src)
+            (valset dst) ()
+            (range dst) ()))
+    dst))
+
+(defun comp-cstr-negation-make (src)
+  "Negate SRC and return a new constraint."
+  (comp-cstr-negation (make-comp-cstr) src))
+
+(defun comp-type-spec-to-cstr (type-spec &optional fn)
+  "Convert a type specifier TYPE-SPEC into a `comp-cstr'.
+FN non-nil indicates we are parsing a function lambda list."
+  (pcase type-spec
+    ((and (or '&optional '&rest) x)
+     (if fn
+         x
+       (error "Invalid `%s` in type specifier" x)))
+    ('nil
+     (make-comp-cstr :typeset ()))
+    ('fixnum
+     (comp-irange-to-cstr `(,most-negative-fixnum . ,most-positive-fixnum)))
+    ('boolean
+     (comp-type-spec-to-cstr '(member t nil)))
+    ('integer
+     (comp-irange-to-cstr '(- . +)))
+    ('null (comp-value-to-cstr nil))
+    ((pred atom)
+     (comp-type-to-cstr type-spec))
+    (`(or . ,rest)
+     (apply #'comp-cstr-union-make
+            (mapcar #'comp-type-spec-to-cstr rest)))
+    (`(and . ,rest)
+     (apply #'comp-cstr-intersection-make
+            (mapcar #'comp-type-spec-to-cstr rest)))
+    (`(not  ,cstr)
+     (comp-cstr-negation-make (comp-type-spec-to-cstr cstr)))
+    (`(integer ,(and (pred integerp) l) ,(and (pred integerp) h))
+     (comp-irange-to-cstr `(,l . ,h)))
+    (`(integer * ,(and (pred integerp) h))
+     (comp-irange-to-cstr `(- . ,h)))
+    (`(integer ,(and (pred integerp) l) *)
+     (comp-irange-to-cstr `(,l . +)))
+    (`(float ,(pred comp-star-or-num-p) ,(pred comp-star-or-num-p))
+     ;; No float range support :/
+     (comp-type-to-cstr 'float))
+    (`(member . ,rest)
+     (apply #'comp-cstr-union-make (mapcar #'comp-value-to-cstr rest)))
+    (`(function ,args ,ret)
+     (make-comp-cstr-f
+      :args (mapcar (lambda (x)
+                      (comp-type-spec-to-cstr x t))
+                    args)
+      :ret (comp-type-spec-to-cstr ret)))
+    (_ (error "Invalid type specifier"))))
+
+(defun comp-cstr-to-type-spec (cstr)
+  "Given CSTR return its type specifier."
+  (let ((valset (comp-cstr-valset cstr))
+        (typeset (comp-cstr-typeset cstr))
+        (range (comp-cstr-range cstr))
+        (negated (comp-cstr-neg cstr)))
+
+    (when valset
+      (when (memq nil valset)
+        (if (memq t valset)
+            (progn
+              ;; t and nil are values, convert into `boolean'.
+              (push 'boolean typeset)
+              (setf valset (remove t (remove nil valset))))
+          ;; Only nil is a value, convert it into a `null' type specifier.
+          (setf valset (remove nil valset))
+          (push 'null typeset))))
+
+    ;; Form proper integer type specifiers.
+    (setf range (cl-loop for (l . h) in range
+                         for low = (if (integerp l) l '*)
+                         for high = (if (integerp h) h '*)
+                         if (and (eq low '*) (eq high '*))
+                           collect 'integer
+                         else
+                           collect `(integer ,low , high))
+          valset (cl-remove-duplicates valset))
+
+    ;; Form the final type specifier.
+    (let* ((types-ints (append typeset range))
+           (res (cond
+                 ((and types-ints valset)
+                  `((member ,@valset) ,@types-ints))
+                 (types-ints types-ints)
+                 (valset `(member ,@valset))
+                 (t
+                  ;; Empty type specifier
+                  nil)))
+           (final
+            (pcase res
+              ((or `(member . ,rest)
+                   `(integer ,(pred comp-star-or-num-p)
+                             ,(pred comp-star-or-num-p)))
+               (if rest
+                   res
+                 (car res)))
+              ((pred atom) res)
+              (`(,_first . ,rest)
+               (if rest
+                   `(or ,@res)
+                 (car res))))))
+      (if negated
+          `(not ,final)
+        final))))
+
+(provide 'comp-cstr)
+
+;;; comp-cstr.el ends here
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
new file mode 100644
index 0000000..5b2dbe1
--- /dev/null
+++ b/lisp/emacs-lisp/comp.el
@@ -0,0 +1,4207 @@
+;;; comp.el --- compilation of Lisp code into native code -*- lexical-binding: 
t -*-
+
+;; Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.com>
+;; Keywords: lisp
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This code is an attempt to make the pig fly.
+;; Or, to put it another way to make a 911 out of a turbocharged VW Bug.
+
+;;; Code:
+
+(require 'bytecomp)
+(require 'cl-extra)
+(require 'cl-lib)
+(require 'cl-macs)
+(require 'cl-seq)
+(require 'gv)
+(require 'rx)
+(require 'subr-x)
+(require 'warnings)
+(require 'comp-cstr)
+
+(defgroup comp nil
+  "Emacs Lisp native compiler."
+  :group 'lisp)
+
+(defcustom comp-speed 2
+  "Optimization level for native compilation, a number between -1 and 3.
+ -1 functions are kept in bytecode form and no native compilation is performed.
+  0 native compilation is performed with no optimizations.
+  1 light optimizations.
+  2 max optimization level fully adherent to the language semantic.
+  3 max optimization level, to be used only when necessary.
+    Warning: with 3, the compiler is free to perform dangerous optimizations."
+  :type 'integer
+  :safe #'integerp
+  :version "28.1")
+
+(defcustom comp-debug (if (eq 'windows-nt system-type) 1 0)
+  "Debug level for native compilation, a number between 0 and 3.
+This is intended for debugging the compiler itself.
+  0 no debug output.
+  1 emit debug symbols.
+  2 emit debug symbols and dump pseudo C code.
+  3 emit debug symbols and dump: pseudo C code, GCC intermediate
+  passes and libgccjit log file."
+  :type 'integer
+  :safe #'natnump
+  :version "28.1")
+
+(defcustom comp-verbose 0
+  "Compiler verbosity for native compilation, a number between 0 and 3.
+This is intended for debugging the compiler itself.
+  0 no logging.
+  1 final LIMPLE is logged.
+  2 LAP, final LIMPLE, and some pass info are logged.
+  3 max verbosity."
+  :type 'integer
+  :risky t
+  :version "28.1")
+
+(defcustom comp-always-compile nil
+  "Non-nil means unconditionally (re-)compile all files."
+  :type 'boolean
+  :version "28.1")
+
+(defcustom comp-deferred-compilation-deny-list
+  '()
+  "List of regexps to exclude matching files from deferred native compilation.
+Files whose names match any regexp are excluded from native compilation."
+  :type '(repeat regexp)
+  :version "28.1")
+
+(defcustom comp-bootstrap-deny-list
+  '()
+  "List of regexps to exclude files from native compilation during bootstrap.
+Files whose names match any regexp are excluded from native compilation
+during bootstrap."
+  :type '(repeat regexp)
+  :version "28.1")
+
+(defcustom comp-never-optimize-functions
+  '(;; The following two are mandatory for Emacs to be working
+    ;; correctly (see comment in `advice--add-function'). DO NOT
+    ;; REMOVE.
+    macroexpand rename-buffer)
+  "Primitive functions to exclude from trampoline optimization."
+  :type '(repeat symbol)
+  :version "28.1")
+
+(defcustom comp-async-jobs-number 0
+  "Default number of subprocesses used for async native compilation.
+Value of zero means to use half the number of the CPU's execution units,
+or one if there's just one execution unit."
+  :type 'integer
+  :risky t
+  :version "28.1")
+
+(defcustom comp-async-cu-done-functions nil
+  "List of functions to call after asynchronously compiling one compilation 
unit.
+Called with one argument FILE, the filename used as input to
+compilation."
+  :type 'hook
+  :version "28.1")
+
+(defcustom comp-async-all-done-hook nil
+  "Hook run after completing asynchronous compilation of all input files."
+  :type 'hook
+  :version "28.1")
+
+(defcustom comp-async-env-modifier-form nil
+  "Form evaluated before compilation by each asynchronous compilation 
subprocess.
+Used to modify the compiler environment."
+  :type 'sexp
+  :risky t
+  :version "28.1")
+
+(defcustom comp-async-report-warnings-errors t
+  "Whether to report warnings and errors from asynchronous native compilation.
+
+When native compilation happens asynchronously, it can produce
+warnings and errors, some of which might not be emitted by a
+byte-compilation.  The typical case for that is native-compiling
+a file that is missing some `require' of a necessary feature,
+while having it already loaded into the environment when
+byte-compiling.
+
+As asynchronous native compilation always starts from a pristine
+environment, it is more sensitive to such omissions, and might be
+unable to compile such Lisp source files correctly.
+
+Set this variable to nil if these warnings annoy you."
+  :type 'boolean
+  :version "28.1")
+
+(defcustom comp-async-query-on-exit nil
+  "Whether to query the user about killing async compilations when exiting.
+If this is non-nil, Emacs will ask for confirmation to exit and kill the
+asynchronous native compilations if any are running.  If nil, when you
+exit Emacs, it will silently kill those asynchronous compilations even
+if `confirm-kill-processes' is non-nil."
+  :type 'boolean
+  :version "28.1")
+
+(defcustom comp-native-driver-options nil
+  "Options passed verbatim to the native compiler's backend driver.
+Note that not all options are meaningful; typically only the options
+affecting the assembler and linker are likely to be useful.
+
+Passing these options is only available in libgccjit version 9
+and above."
+  :type '(repeat string)                ; FIXME is this right?
+  :version "28.1")
+
+(defcustom comp-libgccjit-reproducer nil
+  "When non-nil produce a libgccjit reproducer.
+The reproducer is a file ELNFILENAME_libgccjit_repro.c deposed in
+the .eln output directory."
+  :type 'boolean
+  :version "28.1")
+
+(defcustom comp-warning-on-missing-source t
+  "Emit a warning if a byte-code file being loaded has no corresponding source.
+The source file is necessary for native code file look-up and deferred
+compilation mechanism."
+  :type 'boolean
+  :version "28.1")
+
+(defvar no-native-compile nil
+  "Non-nil to prevent native-compiling of Emacs Lisp code.
+Note that when `no-byte-compile' is set to non-nil it overrides the value of
+`no-native-compile'.
+This is normally set in local file variables at the end of the elisp file:
+
+\;; Local Variables:\n;; no-native-compile: t\n;; End:")
+;;;###autoload(put 'no-native-compile 'safe-local-variable 'booleanp)
+
+(defvar comp-log-time-report nil
+  "If non-nil, log a time report for each pass.")
+
+(defvar comp-dry-run nil
+  "If non-nil, run everything but the C back-end.")
+
+(defconst comp-valid-source-re (rx ".el" (? ".gz") eos)
+  "Regexp to match filename of valid input source files.")
+
+(defconst comp-log-buffer-name "*Native-compile-Log*"
+  "Name of the native-compiler log buffer.")
+
+(defconst comp-async-buffer-name "*Async-native-compile-log*"
+  "Name of the async compilation buffer log.")
+
+(defvar comp-native-compiling nil
+  "This gets bound to t during native compilation.
+Intended to be used by code that needs to work differently when
+native compilation runs.")
+
+(defvar comp-pass nil
+  "Every native-compilation pass can bind this to whatever it likes.")
+
+(defvar comp-curr-allocation-class 'd-default
+  "Current allocation class.
+Can be one of: 'd-default', 'd-impure' or 'd-ephemeral'.  See `comp-ctxt'.")
+
+(defconst comp-passes '(comp-spill-lap
+                        comp-limplify
+                        comp-fwprop
+                        comp-call-optim
+                        comp-ipa-pure
+                        comp-add-cstrs
+                        comp-fwprop
+                        comp-tco
+                        comp-fwprop
+                        comp-remove-type-hints
+                        comp-final)
+  "Passes to be executed in order.")
+
+(defvar comp-disabled-passes '()
+  "List of disabled passes.
+For internal use only by the testsuite.")
+
+(defvar comp-post-pass-hooks '()
+  "Alist whose elements are of the form (PASS FUNCTIONS...).
+Each function in FUNCTIONS is run after PASS.
+Useful to hook into pass checkers.")
+
+;; FIXME this probably should not be here but... good for now.
+(defconst comp-known-type-specifiers
+  `(
+    ;; Functions we can trust not to be or if redefined should expose
+    ;; the same type.  Vast majority of these is either pure or
+    ;; pritive, the original list is the union of pure +
+    ;; side-effect-free-fns + side-effect-and-error-free-fns:
+    (% (function ((or number marker) (or number marker)) number))
+    (* (function (&rest (or number marker)) number))
+    (+ (function (&rest (or number marker)) number))
+    (- (function (&rest (or number marker)) number))
+    (/ (function ((or number marker) &rest (or number marker)) number))
+    (/= (function ((or number marker) (or number marker)) boolean))
+    (1+ (function ((or number marker)) number))
+    (1- (function ((or number marker)) number))
+    (< (function ((or number marker) &rest (or number marker)) boolean))
+    (<= (function ((or number marker) &rest (or number marker)) boolean))
+    (= (function ((or number marker) &rest (or number marker)) boolean))
+    (> (function ((or number marker) &rest (or number marker)) boolean))
+    (>= (function ((or number marker) &rest (or number marker)) boolean))
+    (abs (function (number) number))
+    (acos (function (number) float))
+    (append (function (&rest t) t))
+    (aref (function (t fixnum) t))
+    (arrayp (function (t) boolean))
+    (ash (function (integer integer) integer))
+    (asin (function (number) float))
+    (assq (function (t list) list))
+    (atan (function (number &optional number) float))
+    (atom (function (t) boolean))
+    (bignump (function (t) boolean))
+    (bobp (function () boolean))
+    (bolp (function () boolean))
+    (bool-vector-count-consecutive (function (bool-vector boolean integer) 
fixnum))
+    (bool-vector-count-population (function (bool-vector) fixnum))
+    (bool-vector-not (function (bool-vector &optional bool-vector) 
bool-vector))
+    (bool-vector-p (function (t) boolean))
+    (bool-vector-subsetp (function (bool-vector bool-vector) boolean))
+    (boundp (function (symbol) boolean))
+    (buffer-end (function ((or number marker)) integer))
+    (buffer-file-name (function (&optional buffer) string))
+    (buffer-list (function (&optional frame) list))
+    (buffer-local-variables (function (&optional buffer) list))
+    (buffer-modified-p (function (&optional buffer) boolean))
+    (buffer-size (function (&optional buffer) integer))
+    (buffer-string (function () string))
+    (buffer-substring (function ((or integer marker) (or integer marker)) 
string))
+    (bufferp (function (t) boolean))
+    (byte-code-function-p (function (t) boolean))
+    (capitalize (function (or integer string) (or integer string)))
+    (car (function (list) t))
+    (car-less-than-car (function (list list) boolean))
+    (car-safe (function (t) t))
+    (case-table-p (function (t) boolean))
+    (cdr (function (list) t))
+    (cdr-safe (function (t) t))
+    (ceiling (function (number &optional number) integer))
+    (char-after (function (&optional (or marker integer)) fixnum))
+    (char-before (function (&optional (or marker integer)) fixnum))
+    (char-equal (function (integer integer) boolean))
+    (char-or-string-p (function (t) boolean))
+    (char-to-string (function (fixnum) string))
+    (char-width (function (fixnum) fixnum))
+    (characterp (function (t &optional t) boolean))
+    (charsetp (function (t) boolean))
+    (commandp (function (t &optional t) boolean))
+    (compare-strings (function (string (or integer marker null) (or integer 
marker null) string (or integer marker null) (or integer marker null) &optional 
t) (or (member t) fixnum)))
+    (concat (function (&rest sequence) string))
+    (cons (function (t t) cons))
+    (consp (function (t) boolean))
+    (coordinates-in-window-p (function (cons window) boolean))
+    (copy-alist (function (list) list))
+    (copy-marker (function (&optional (or integer marker) boolean) marker))
+    (copy-sequence (function (sequence) sequence))
+    (copysign (function (float float) float))
+    (cos (function (number) float))
+    (count-lines (function ((or integer marker) (or integer marker) &optional 
t) integer))
+    (current-buffer (function () buffer))
+    (current-global-map (function () cons))
+    (current-indentation (function () integer))
+    (current-local-map (function () cons))
+    (current-minor-mode-maps (function () cons))
+    (current-time (function () cons))
+    (current-time-string (function (&optional string boolean) string))
+    (current-time-zone (function (&optional string boolean) cons))
+    (custom-variable-p (function (symbol) boolean))
+    (decode-char (function (cons t) (or fixnum null)))
+    (decode-time (function (&optional string symbol symbol) cons))
+    (default-boundp (function (symbol) boolean))
+    (default-value (function (symbol) t))
+    (degrees-to-radians (function (number) float))
+    (documentation (function ((or function symbol subr) &optional t) (or null 
string)))
+    (downcase (function ((or fixnum string)) (or fixnum string)))
+    (elt (function (sequence integer) t))
+    (encode-char (function (fixnum symbol) (or fixnum null)))
+    (encode-time (function (cons &rest t) cons))
+    (eobp (function () boolean))
+    (eolp (function () boolean))
+    (eq (function (t t) boolean))
+    (eql (function (t t) boolean))
+    (equal (function (t t) boolean))
+    (error-message-string (function (list) string))
+    (eventp (function (t) boolean))
+    (exp (function (number) float))
+    (expt (function (number number) float))
+    (fboundp (function (symbol) boolean))
+    (fceiling (function (float) float))
+    (featurep (function (symbol &optional symbol) boolean))
+    (ffloor (function (float) float))
+    (file-directory-p (function (string) boolean))
+    (file-exists-p (function (string) boolean))
+    (file-locked-p (function (string) boolean))
+    (file-name-absolute-p (function (string) boolean))
+    (file-newer-than-file-p (function (string string) boolean))
+    (file-readable-p (function (string) boolean))
+    (file-symlink-p (function (string) boolean))
+    (file-writable-p (function (string) boolean))
+    (fixnump (function (t) boolean))
+    (float (function (number) float))
+    (float-time (function (&optional cons) float))
+    (floatp (function (t) boolean))
+    (floor (function (number &optional number) integer))
+    (following-char (function () fixnum))
+    (format (function (string &rest t) string))
+    (format-time-string (function (string &optional cons symbol) string))
+    (frame-first-window (function ((or frame window)) window))
+    (frame-root-window (function (&optional (or frame window)) window))
+    (frame-selected-window (function (&optional (or frame window)) window))
+    (frame-visible-p (function (frame) boolean))
+    (framep (function (t) boolean))
+    (fround (function (float) float))
+    (ftruncate (function (float) float))
+    (get (function (symbol symbol) t))
+    (get-buffer (function ((or buffer string)) (or buffer null)))
+    (get-buffer-window (function (&optional (or buffer string) (or symbol 
(integer 0 0))) (or null window)))
+    (get-file-buffer (function (string) (or null buffer)))
+    (get-largest-window (function (&optional t t t) window))
+    (get-lru-window (function (&optional t t t) window))
+    (getenv (function (string &optional frame) (or null string)))
+    (gethash (function (t hash-table &optional t) t))
+    (hash-table-count (function (hash-table) integer))
+    (hash-table-p (function (t) boolean))
+    (identity (function (t) t))
+    (ignore (function (&rest t) null))
+    (int-to-string (function (number) string))
+    (integer-or-marker-p (function (t) boolean))
+    (integerp (function (t) boolean))
+    (interactive-p (function () boolean))
+    (intern-soft (function ((or string symbol) &optional vector) symbol))
+    (invocation-directory (function () string))
+    (invocation-name (function () string))
+    (isnan (function (float) boolean))
+    (keymap-parent (function (cons) (or cons null)))
+    (keymapp (function (t) boolean))
+    (keywordp (function (t) boolean))
+    (last (function (list &optional integer) list))
+    (lax-plist-get (function (list t) t))
+    (ldexp (function (number integer) float))
+    (length (function (t) (integer 0 *)))
+    (length< (function (sequence fixnum) boolean))
+    (length= (function (sequence fixnum) boolean))
+    (length> (function (sequence fixnum) boolean))
+    (line-beginning-position (function (&optional integer) integer))
+    (line-end-position (function (&optional integer) integer))
+    (list (function (&rest t) list))
+    (listp (function (t) boolean))
+    (local-variable-if-set-p (function (symbol &optional buffer) boolean))
+    (local-variable-p (function (symbol &optional buffer) boolean))
+    (locale-info (function ((member codeset days months paper)) (or null 
string)))
+    (log (function (number number) float))
+    (log10 (function (number) float))
+    (logand (function (&rest (or integer marker)) integer))
+    (logb (function (number) integer))
+    (logcount (function (integer) integer))
+    (logior (function (&rest (or integer marker)) integer))
+    (lognot (function (integer) integer))
+    (logxor (function (&rest (or integer marker)) integer))
+    ;; (lsh (function ((integer ,most-negative-fixnum *) integer) integer)) ?
+    (lsh (function (integer integer) integer))
+    (make-byte-code (function ((or fixnum list) string vector integer 
&optional string t &rest t) vector))
+    (make-list (function (integer t) list))
+    (make-marker (function () marker))
+    (make-string (function (integer fixnum &optional t) string))
+    (make-symbol (function (string) symbol))
+    (mark (function (&optional t) (or integer null)))
+    (mark-marker (function () marker))
+    (marker-buffer (function (marker) buffer))
+    (markerp (function (t) boolean))
+    (max (function ((or number marker) &rest (or number marker)) number))
+    (max-char (function () fixnum))
+    (member (function (t list) list))
+    (memory-limit (function () integer))
+    (memq (function (t list) list))
+    (memql (function (t list) list))
+    (min (function ((or number marker) &rest (or number marker)) number))
+    (minibuffer-selected-window (function () window))
+    (minibuffer-window (function (&optional frame) window))
+    (mod (function ((or number marker) (or number marker)) (or (integer 0 *) 
(float 0 *))))
+    (mouse-movement-p (function (t) boolean))
+    (multibyte-char-to-unibyte (function (fixnum) fixnum))
+    (natnump (function (t) boolean))
+    (next-window (function (&optional window t t) window))
+    (nlistp (function (t) boolean))
+    (not (function (t) boolean))
+    (nth (function (integer list) t))
+    (nthcdr (function (integer t) t))
+    (null (function (t) boolean))
+    (number-or-marker-p (function (t) boolean))
+    (number-to-string (function (number) string))
+    (numberp (function (t) boolean))
+    (one-window-p (function (&optional t t) boolean))
+    (overlayp (function (t) boolean))
+    (parse-colon-path (function (string) cons))
+    (plist-get (function (list t) t))
+    (plist-member (function (list t) list))
+    (point (function () integer))
+    (point-marker (function () marker))
+    (point-max (function () integer))
+    (point-min (function () integer))
+    (preceding-char (function () fixnum))
+    (previous-window (function (&optional window t t) window))
+    (prin1-to-string (function (t &optional t) string))
+    (processp (function (t) boolean))
+    (proper-list-p (function (t) integer))
+    (propertize (function (string &rest t) string))
+    (radians-to-degrees (function (number) float))
+    (rassoc (function (t list) list))
+    (rassq (function (t list) list))
+    (read-from-string (function (string &optional integer integer) cons))
+    (recent-keys (function (&optional (or cons null)) vector))
+    (recursion-depth (function () integer))
+    (regexp-opt (function (list) string))
+    (regexp-quote (function (string) string))
+    (region-beginning (function () integer))
+    (region-end (function () integer))
+    (reverse (function (sequence) sequence))
+    (round (function (number &optional number) integer))
+    (safe-length (function (t) integer))
+    (selected-frame (function () frame))
+    (selected-window (function () window))
+    (sequencep (function (t) boolean))
+    (sin (function (number) float))
+    (sqrt (function (number) float))
+    (standard-case-table (function () char-table))
+    (standard-syntax-table (function () char-table))
+    (string (function (&rest fixnum) string))
+    (string-as-multibyte (function (string) string))
+    (string-as-unibyte (function (string) string))
+    (string-equal (function ((or string symbol) (or string symbol)) boolean))
+    (string-lessp (function ((or string symbol) (or string symbol)) boolean))
+    (string-make-multibyte (function (string) string))
+    (string-make-unibyte (function (string) string))
+    (string-search (function (string string &optional integer) (or integer 
null)))
+    (string-to-char (function (string) fixnum))
+    (string-to-multibyte (function (string) string))
+    (string-to-number (function (string &optional integer) number))
+    (string-to-syntax (function (string) cons))
+    (string< (function ((or string symbol) (or string symbol)) boolean))
+    (string= (function ((or string symbol) (or string symbol)) boolean))
+    (stringp (function (t) boolean))
+    (subrp (function (t) boolean))
+    (substring (function ((or string vector) &optional integer integer) (or 
string vector)))
+    (sxhash (function (t) integer))
+    (sxhash-eq (function (t) integer))
+    (sxhash-eql (function (t) integer))
+    (sxhash-equal (function (t) integer))
+    (symbol-function (function (symbol) t))
+    (symbol-name (function (symbol) string))
+    (symbol-plist (function (symbol) list))
+    (symbol-value (function (symbol) t))
+    (symbolp (function (t) boolean))
+    (syntax-table (function () char-table))
+    (syntax-table-p (function (t) boolean))
+    (tan (function (number) float))
+    (this-command-keys (function () string))
+    (this-command-keys-vector (function () vector))
+    (this-single-command-keys (function () vector))
+    (this-single-command-raw-keys (function () vector))
+    (time-convert (function (t &optional (or boolean integer)) cons))
+    (truncate (function (number &optional number) integer))
+    (type-of (function (t) symbol))
+    (unibyte-char-to-multibyte (function (fixnum) fixnum)) ;; byte is fixnum
+    (upcase (function ((or fixnum string)) (or fixnum string)))
+    (user-full-name (function (&optional integer) (or string null)))
+    (user-login-name (function (&optional integer) (or string null)))
+    (user-original-login-name (function (&optional integer) (or string null)))
+    (user-real-login-name (function () string))
+    (user-real-uid (function () integer))
+    (user-uid (function () integer))
+    (vconcat (function (&rest sequence) vector))
+    (vector (function (&rest t) vector))
+    (vectorp (function (t) boolean))
+    (visible-frame-list (function () list))
+    (wholenump (function (t) boolean))
+    (window-configuration-p (function (t) boolean))
+    (window-live-p (function (t) boolean))
+    (window-valid-p (function (t) boolean))
+    (windowp (function (t) boolean))
+    (zerop (function (number) boolean))
+    ;; Type hints
+    (comp-hint-fixnum (function (t) fixnum))
+    (comp-hint-cons (function (t) cons))
+    ;; Non returning functions
+    (throw (function (t t) nil))
+    (error (function (string &rest t) nil))
+    (signal (function (symbol t) nil)))
+  "Alist used for type propagation.")
+
+(defconst comp-known-func-cstr-h
+  (cl-loop
+   with comp-ctxt = (make-comp-cstr-ctxt)
+   with h = (make-hash-table :test #'eq)
+   for (f type-spec) in comp-known-type-specifiers
+   for cstr = (comp-type-spec-to-cstr type-spec)
+   do (puthash f cstr h)
+   finally return h)
+  "Hash table function -> `comp-constraint'.")
+
+(defconst comp-known-predicates
+  '((arrayp              . array)
+    (atom               . atom)
+    (characterp          . fixnum)
+    (booleanp            . boolean)
+    (bool-vector-p       . bool-vector)
+    (bufferp             . buffer)
+    (natnump             . (integer 0 *))
+    (char-table-p       . char-table)
+    (hash-table-p       . hash-table)
+    (consp               . cons)
+    (integerp            . integer)
+    (floatp              . float)
+    (functionp           . (or function symbol))
+    (integerp            . integer)
+    (keywordp            . keyword)
+    (listp               . list)
+    (numberp             . number)
+    (null               . null)
+    (numberp             . number)
+    (sequencep           . sequence)
+    (stringp             . string)
+    (symbolp             . symbol)
+    (vectorp             . vector)
+    (integer-or-marker-p . integer-or-marker))
+  "Alist predicate -> matched type specifier.")
+
+(defconst comp-known-predicates-h
+  (cl-loop
+   with comp-ctxt = (make-comp-cstr-ctxt)
+   with h = (make-hash-table :test #'eq)
+   for (pred . type-spec) in comp-known-predicates
+   for cstr = (comp-type-spec-to-cstr type-spec)
+   do (puthash pred cstr h)
+   finally return h)
+  "Hash table function -> `comp-constraint'.")
+
+(defun comp-known-predicate-p (predicate)
+  "Return t if PREDICATE is known."
+  (when (gethash predicate comp-known-predicates-h) t))
+
+(defun comp-pred-to-cstr (predicate)
+  "Given PREDICATE, return the corresponding constraint."
+  (gethash predicate comp-known-predicates-h))
+
+(defconst comp-symbol-values-optimizable '(most-positive-fixnum
+                                           most-negative-fixnum)
+  "Symbol values we can resolve at compile-time.")
+
+(defconst comp-type-hints '(comp-hint-fixnum
+                            comp-hint-cons)
+  "List of fake functions used to give compiler hints.")
+
+(defconst comp-limple-sets '(set
+                             setimm
+                             set-par-to-local
+                             set-args-to-local
+                             set-rest-args-to-local)
+  "Limple set operators.")
+
+(defconst comp-limple-assignments `(assume
+                                    fetch-handler
+                                    ,@comp-limple-sets)
+  "Limple operators that clobber the first m-var argument.")
+
+(defconst comp-limple-calls '(call
+                              callref
+                              direct-call
+                              direct-callref)
+  "Limple operators used to call subrs.")
+
+(defconst comp-limple-branches '(jump cond-jump)
+  "Limple operators used for conditional and unconditional branches.")
+
+(defconst comp-limple-ops `(,@comp-limple-calls
+                            ,@comp-limple-assignments
+                            ,@comp-limple-branches
+                            return)
+  "All limple operators.")
+
+(defvar comp-func nil
+  "Bound to the current function by most passes.")
+
+(defvar comp-block nil
+  "Bound to the current basic block by some passes.")
+
+(define-error 'native-compiler-error-dyn-func
+  "can't native compile a non-lexically-scoped function"
+  'native-compiler-error)
+(define-error 'native-compiler-error-empty-byte
+  "empty byte compiler output"
+  'native-compiler-error)
+
+
+;; Moved early to avoid circularity when comp.el is loaded and
+;; `macroexpand' needs to be advised (bug#47049).
+;;;###autoload
+(defun comp-subr-trampoline-install (subr-name)
+  "Make SUBR-NAME effectively advice-able when called from native code."
+  (unless (or (null comp-enable-subr-trampolines)
+              (memq subr-name comp-never-optimize-functions)
+              (gethash subr-name comp-installed-trampolines-h))
+    (cl-assert (subr-primitive-p (symbol-function subr-name)))
+    (comp--install-trampoline
+     subr-name
+     (or (comp-trampoline-search subr-name)
+         (comp-trampoline-compile subr-name)
+         ;; Should never happen.
+         (cl-assert nil)))))
+
+
+(cl-defstruct (comp-vec (:copier nil))
+  "A re-sizable vector like object."
+  (data (make-hash-table :test #'eql) :type hash-table
+        :documentation "Payload data.")
+  (beg 0 :type integer)
+  (end 0 :type natnum))
+
+(defsubst comp-vec-copy (vec)
+  "Return a copy of VEC."
+  (make-comp-vec :data (copy-hash-table (comp-vec-data vec))
+                 :beg (comp-vec-beg vec)
+                 :end (comp-vec-end vec)))
+
+(defsubst comp-vec-length (vec)
+  "Return the number of elements of VEC."
+  (- (comp-vec-end vec) (comp-vec-beg vec)))
+
+(defsubst comp-vec--verify-idx (vec idx)
+  "Check whether IDX is in bounds for VEC."
+  (cl-assert (and (< idx (comp-vec-end vec))
+                  (>= idx (comp-vec-beg vec)))))
+
+(defsubst comp-vec-aref (vec idx)
+  "Return the element of VEC whose index is IDX."
+  (declare (gv-setter (lambda (val)
+                        `(comp-vec--verify-idx ,vec ,idx)
+                        `(puthash ,idx ,val (comp-vec-data ,vec)))))
+  (comp-vec--verify-idx vec idx)
+  (gethash idx (comp-vec-data vec)))
+
+(defsubst comp-vec-append (vec elt)
+  "Append ELT into VEC.
+Returns ELT."
+  (puthash (comp-vec-end vec) elt (comp-vec-data vec))
+  (cl-incf (comp-vec-end vec))
+  elt)
+
+(defsubst comp-vec-prepend (vec elt)
+  "Prepend ELT into VEC.
+Returns ELT."
+  (puthash (1- (comp-vec-beg vec)) elt (comp-vec-data vec))
+  (cl-decf (comp-vec-beg vec))
+  elt)
+
+
+
+(eval-when-compile
+  (defconst comp-op-stack-info
+    (cl-loop with h = (make-hash-table)
+            for k across byte-code-vector
+            for v across byte-stack+-info
+            when k
+            do (puthash k v h)
+            finally return h)
+    "Hash table lap-op -> stack adjustment."))
+
+(define-hash-table-test 'comp-imm-equal-test #'equal-including-properties
+  #'sxhash-equal-including-properties)
+
+(cl-defstruct comp-data-container
+  "Data relocation container structure."
+  (l () :type list
+     :documentation "Constant objects used by functions.")
+  (idx (make-hash-table :test 'comp-imm-equal-test) :type hash-table
+       :documentation "Obj -> position into the previous field."))
+
+(cl-defstruct (comp-ctxt (:include comp-cstr-ctxt))
+  "Lisp side of the compiler context."
+  (output nil :type string
+          :documentation "Target output file-name for the compilation.")
+  (speed comp-speed :type number
+         :documentation "Default speed for this compilation unit.")
+  (debug comp-debug :type number
+         :documentation "Default debug level for this compilation unit.")
+  (driver-options comp-native-driver-options :type list
+         :documentation "Options for the GCC driver.")
+  (top-level-forms () :type list
+                   :documentation "List of spilled top level forms.")
+  (funcs-h (make-hash-table :test #'equal) :type hash-table
+           :documentation "c-name -> comp-func.")
+  (sym-to-c-name-h (make-hash-table :test #'eq) :type hash-table
+                   :documentation "symbol-function -> c-name.
+This is only for optimizing intra CU calls at speed 3.")
+  (byte-func-to-func-h (make-hash-table :test #'equal) :type hash-table
+                     :documentation "byte-function -> comp-func.
+Needed to replace immediate byte-compiled lambdas with the compiled 
reference.")
+  (lambda-fixups-h (make-hash-table :test #'equal) :type hash-table
+                   :documentation  "Hash table byte-func -> mvar to fixup.")
+  (function-docs (make-hash-table :test #'eql) :type (or hash-table vector)
+               :documentation "Documentation index -> documentation")
+  (d-default (make-comp-data-container) :type comp-data-container
+             :documentation "Standard data relocated in use by functions.")
+  (d-impure (make-comp-data-container) :type comp-data-container
+            :documentation "Relocated data that cannot be moved into pure 
space.
+This is typically for top-level forms other than defun.")
+  (d-ephemeral (make-comp-data-container) :type comp-data-container
+               :documentation "Relocated data not necessary after load.")
+  (with-late-load nil :type boolean
+                  :documentation "When non-nil support late load."))
+
+(cl-defstruct comp-args-base
+  (min nil :type integer
+       :documentation "Minimum number of arguments allowed."))
+
+(cl-defstruct (comp-args (:include comp-args-base))
+  (max nil :type integer
+       :documentation "Maximum number of arguments allowed."))
+
+(cl-defstruct (comp-nargs (:include comp-args-base))
+  "Describe args when the function signature is of kind:
+(ptrdiff_t nargs, Lisp_Object *args)."
+  (nonrest nil :type integer
+           :documentation "Number of non rest arguments.")
+  (rest nil :type boolean
+        :documentation "t if rest argument is present."))
+
+(cl-defstruct (comp-block (:copier nil)
+                          (:constructor nil))
+  "A base class for basic blocks."
+  (name nil :type symbol)
+  (insns () :type list
+         :documentation "List of instructions.")
+  (closed nil :type boolean
+          :documentation "t if closed.")
+  ;; All the following are for SSA and CGF analysis.
+  ;; Keep in sync with `comp-clean-ssa'!!
+  (in-edges () :type list
+            :documentation "List of incoming edges.")
+  (out-edges () :type list
+             :documentation "List of out-coming edges.")
+  (idom nil :type (or null comp-block)
+        :documentation "Immediate dominator.")
+  (df (make-hash-table) :type (or null hash-table)
+      :documentation "Dominance frontier set. Block-name -> block")
+  (post-num nil :type (or null number)
+            :documentation "Post order number.")
+  (final-frame nil :type (or null comp-vec)
+             :documentation "This is a copy of the frame when leaving the 
block.
+Is in use to help the SSA rename pass."))
+
+(cl-defstruct (comp-block-lap (:copier nil)
+                              (:include comp-block)
+                              (:constructor make--comp-block-lap
+                                            (addr sp name))) ; Positional
+  "A basic block created from lap (real code)."
+  ;; These two slots are used during limplification.
+  (sp nil :type number
+      :documentation "When non-nil indicates the sp value while entering
+into it.")
+  (addr nil :type number
+        :documentation "Start block LAP address.")
+  (non-ret-insn nil :type list
+                :documentation "Insn known to perform a non local exit.
+`comp-fwprop' may identify and store here basic blocks performing
+non local exits and mark it rewrite it later.")
+  (no-ret nil :type boolean
+         :documentation "t when the block is known to perform a
+non local exit (ends with an `unreachable' insn)."))
+
+(cl-defstruct (comp-latch (:copier nil)
+                          (:include comp-block))
+  "A basic block for a latch loop.")
+
+(cl-defstruct (comp-block-cstr (:copier nil)
+                               (:include comp-block))
+  "A basic block holding only constraints.")
+
+(cl-defstruct (comp-edge (:copier nil) (:constructor make--comp-edge))
+  "An edge connecting two basic blocks."
+  (src nil :type (or null comp-block))
+  (dst nil :type (or null comp-block))
+  (number nil :type number
+          :documentation "The index number corresponding to this edge in the
+ edge hash."))
+
+(defun make-comp-edge (&rest args)
+  "Create a `comp-edge' with basic blocks SRC and DST."
+  (let ((n (funcall (comp-func-edge-cnt-gen comp-func))))
+    (puthash
+     n
+     (apply #'make--comp-edge :number n args)
+     (comp-func-edges-h comp-func))))
+
+(defun comp-block-preds (basic-block)
+  "Return the list of predecessors of BASIC-BLOCK."
+  (mapcar #'comp-edge-src (comp-block-in-edges basic-block)))
+
+(defun comp-gen-counter ()
+  "Return a sequential number generator."
+  (let ((n -1))
+    (lambda ()
+      (cl-incf n))))
+
+(cl-defstruct (comp-func (:copier nil))
+  "LIMPLE representation of a function."
+  (name nil :type symbol
+        :documentation "Function symbol name. Nil indicates anonymous.")
+  (c-name nil :type string
+          :documentation "The function name in the native world.")
+  (byte-func nil
+             :documentation "Byte-compiled version.")
+  (doc nil :type string
+       :documentation "Doc string.")
+  (int-spec nil :type list
+            :documentation "Interactive form.")
+  (lap () :type list
+       :documentation "LAP assembly representation.")
+  (ssa-status nil :type symbol
+       :documentation "SSA status either: 'nil', 'dirty' or 't'.
+Once in SSA form this *must* be set to 'dirty' every time the topology of the
+CFG is mutated by a pass.")
+  (frame-size nil :type integer)
+  (vframe-size 0 :type integer)
+  (blocks (make-hash-table :test #'eq) :type hash-table
+          :documentation "Basic block symbol -> basic block.")
+  (lap-block (make-hash-table :test #'equal) :type hash-table
+             :documentation "LAP label -> LIMPLE basic block name.")
+  (edges-h (make-hash-table) :type hash-table
+         :documentation "Hash edge-num -> edge connecting basic two blocks.")
+  (block-cnt-gen (funcall #'comp-gen-counter) :type function
+                 :documentation "Generates block numbers.")
+  (edge-cnt-gen (funcall #'comp-gen-counter) :type function
+                :documentation "Generates edges numbers.")
+  (has-non-local nil :type boolean
+                 :documentation "t if non local jumps are present.")
+  (speed nil :type number
+         :documentation "Optimization level (see `comp-speed').")
+  (pure nil :type boolean
+        :documentation "t if pure nil otherwise.")
+  (type nil :type (or null comp-mvar)
+        :documentation "Mvar holding the derived return type."))
+
+(cl-defstruct (comp-func-l (:include comp-func))
+  "Lexically-scoped function."
+  (args nil :type comp-args-base
+        :documentation "Argument specification of the function"))
+
+(cl-defstruct (comp-func-d (:include comp-func))
+  "Dynamically-scoped function."
+  (lambda-list nil :type list
+        :documentation "Original lambda-list."))
+
+(cl-defstruct (comp-mvar (:constructor make--comp-mvar)
+                         (:include comp-cstr))
+  "A meta-variable being a slot in the meta-stack."
+  (id nil :type (or null number)
+      :documentation "Unique id when in SSA form.")
+  (slot nil :type (or fixnum symbol)
+        :documentation "Slot number in the array if a number or
+        'scratch' for scratch slot."))
+
+(defun comp-mvar-type-hint-match-p (mvar type-hint)
+  "Match MVAR against TYPE-HINT.
+In use by the backend."
+  (cl-ecase type-hint
+    (cons (comp-cstr-cons-p mvar))
+    (fixnum (comp-cstr-fixnum-p mvar))))
+
+
+
+(defun comp-ensure-native-compiler ()
+  "Make sure Emacs has native compiler support and libgccjit can be loaded.
+Signal an error otherwise.
+To be used by all entry points."
+  (cond
+   ((null (featurep 'nativecomp))
+    (error "Emacs was not compiled with native compiler support 
(--with-native-compilation)"))
+   ((null (native-comp-available-p))
+    (error "Cannot find libgccjit library"))))
+
+(defun comp-equality-fun-p (function)
+  "Equality functions predicate for FUNCTION."
+  (when (memq function '(eq eql equal)) t))
+
+(defun comp-arithm-cmp-fun-p (function)
+  "Predicate for arithmetic comparison functions."
+  (when (memq function '(= > < >= <=)) t))
+
+(defun comp-set-op-p (op)
+  "Assignment predicate for OP."
+  (when (memq op comp-limple-sets) t))
+
+(defun comp-assign-op-p (op)
+  "Assignment predicate for OP."
+  (when (memq op comp-limple-assignments) t))
+
+(defun comp-call-op-p (op)
+  "Call predicate for OP."
+  (when (memq op comp-limple-calls) t))
+
+(defun comp-branch-op-p (op)
+  "Branch predicate for OP."
+  (when (memq op comp-limple-branches) t))
+
+(defsubst comp-limple-insn-call-p (insn)
+  "Limple INSN call predicate."
+  (comp-call-op-p (car-safe insn)))
+
+(defun comp-type-hint-p (func)
+  "Type-hint predicate for function name FUNC."
+  (when (memq func comp-type-hints) t))
+
+(defun comp-func-unique-in-cu-p (func)
+  "Return t if FUNC is known to be unique in the current compilation unit."
+  (if (symbolp func)
+      (cl-loop with h = (make-hash-table :test #'eq)
+               for f being the hash-value in (comp-ctxt-funcs-h comp-ctxt)
+               for name = (comp-func-name f)
+               when (gethash name h)
+                 return nil
+               do (puthash name t h)
+               finally return t)
+    t))
+
+(defsubst comp-symbol-func-to-fun (symbol-funcion)
+  "Given a function called SYMBOL-FUNCION return its `comp-func'."
+  (gethash (gethash symbol-funcion (comp-ctxt-sym-to-c-name-h
+                                    comp-ctxt))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+(defun comp-function-pure-p (f)
+  "Return t if F is pure."
+  (or (get f 'pure)
+      (when-let ((func (comp-symbol-func-to-fun f)))
+        (comp-func-pure func))))
+
+(defun comp-alloc-class-to-container (alloc-class)
+  "Given ALLOC-CLASS, return the data container for the current context.
+Assume allocation class 'd-default as default."
+  (cl-struct-slot-value 'comp-ctxt (or alloc-class 'd-default) comp-ctxt))
+
+(defsubst comp-add-const-to-relocs (obj)
+  "Keep track of OBJ into the ctxt relocations."
+  (puthash obj t (comp-data-container-idx (comp-alloc-class-to-container
+                                           comp-curr-allocation-class))))
+
+
+;;; Log routines.
+
+(defconst comp-limple-lock-keywords
+  `((,(rx bol "(comment" (1+ not-newline)) . font-lock-comment-face)
+    (,(rx "#(" (group-n 1 "mvar"))
+     (1 font-lock-function-name-face))
+    (,(rx bol "(" (group-n 1 "phi"))
+     (1 font-lock-variable-name-face))
+    (,(rx bol "(" (group-n 1 (or "return" "unreachable")))
+     (1 font-lock-warning-face))
+    (,(rx (group-n 1 (or "entry"
+                         (seq (or "entry_" "entry_fallback_" "bb_")
+                              (1+ num) (? (or "_latch"
+                                              (seq "_cstrs_" (1+ num))))))))
+     (1 font-lock-constant-face))
+    (,(rx-to-string
+       `(seq "(" (group-n 1 (or ,@(mapcar #'symbol-name comp-limple-ops)))))
+     (1 font-lock-keyword-face)))
+  "Highlights used by `comp-limple-mode'.")
+
+(define-derived-mode comp-limple-mode fundamental-mode "LIMPLE"
+  "Syntax-highlight LIMPLE IR."
+  (setf font-lock-defaults '(comp-limple-lock-keywords)))
+
+(cl-defun comp-log (data &optional (level 1) quoted)
+  "Log DATA at LEVEL.
+LEVEL is a number from 1-3, and defaults to 1; if it is less
+than `comp-verbose', do nothing.  If `noninteractive', log
+with `message'.  Otherwise, log with `comp-log-to-buffer'."
+  (when (>= comp-verbose level)
+    (if noninteractive
+        (cl-typecase data
+          (atom (message "%s" data))
+          (t (dolist (elem data)
+               (message "%s" elem))))
+      (comp-log-to-buffer data quoted))))
+
+(cl-defun comp-log-to-buffer (data &optional quoted)
+  "Log DATA to `comp-log-buffer-name'."
+  (let* ((print-f (if quoted #'prin1 #'princ))
+         (log-buffer
+             (or (get-buffer comp-log-buffer-name)
+                 (with-current-buffer (get-buffer-create comp-log-buffer-name)
+                   (setf buffer-read-only t)
+                   (current-buffer))))
+         (log-window (get-buffer-window log-buffer))
+         (inhibit-read-only t)
+         at-end-p)
+    (with-current-buffer log-buffer
+      (unless (eq major-mode 'comp-limple-mode)
+        (comp-limple-mode))
+      (when (= (point) (point-max))
+        (setf at-end-p t))
+      (save-excursion
+        (goto-char (point-max))
+        (cl-typecase data
+          (atom (funcall print-f data log-buffer))
+          (t (dolist (elem data)
+               (funcall print-f elem log-buffer)
+               (insert "\n"))))
+        (insert "\n"))
+      (when (and at-end-p log-window)
+        ;; When log window's point is at the end, follow the tail.
+        (with-selected-window log-window
+          (goto-char (point-max)))))))
+
+(defun comp-prettyformat-mvar (mvar)
+  (format "#(mvar %s %s %S)"
+          (comp-mvar-id mvar)
+          (comp-mvar-slot mvar)
+          (comp-cstr-to-type-spec mvar)))
+
+(defun comp-prettyformat-insn (insn)
+  (cl-typecase insn
+    (comp-mvar (comp-prettyformat-mvar insn))
+    (atom (prin1-to-string insn))
+    (cons (concat "(" (mapconcat #'comp-prettyformat-insn insn " ") ")"))))
+
+(defun comp-log-func (func verbosity)
+  "Log function FUNC at VERBOSITY.
+VERBOSITY is a number between 0 and 3."
+  (when (>= comp-verbose verbosity)
+    (comp-log (format "\nFunction: %s\n" (comp-func-name func)) verbosity)
+    (cl-loop
+     for block-name being each hash-keys of (comp-func-blocks func)
+     using (hash-value bb)
+     do (comp-log (concat "<" (symbol-name block-name) ">") verbosity)
+        (cl-loop
+         for insn in (comp-block-insns bb)
+         do (comp-log (comp-prettyformat-insn insn) verbosity)))))
+
+(defun comp-log-edges (func)
+  "Log edges in FUNC."
+  (let ((edges (comp-func-edges-h func)))
+    (comp-log (format "\nEdges in function: %s\n"
+                      (comp-func-name func))
+              2)
+    (maphash (lambda (_ e)
+               (comp-log (format "n: %d src: %s dst: %s\n"
+                                 (comp-edge-number e)
+                                 (comp-block-name (comp-edge-src e))
+                                 (comp-block-name (comp-edge-dst e)))
+                         2))
+          edges)))
+
+
+
+(defmacro comp-loop-insn-in-block (basic-block &rest body)
+  "Loop over all insns in BASIC-BLOCK executing BODY.
+Inside BODY, `insn' and `insn-cell'can be used to read or set the
+current instruction or its cell."
+  (declare (debug (form body))
+           (indent defun))
+  `(cl-symbol-macrolet ((insn (car insn-cell)))
+     (let ((insn-cell (comp-block-insns ,basic-block)))
+       (while insn-cell
+         ,@body
+         (setf insn-cell (cdr insn-cell))))))
+
+;;; spill-lap pass specific code.
+
+(defun comp-lex-byte-func-p (f)
+  "Return t if F is a lexically-scoped byte compiled function."
+  (and (byte-code-function-p f)
+       (fixnump (aref f 0))))
+
+(defun comp-spill-decl-spec (function-name spec)
+  "Return the declared specifier SPEC for FUNCTION-NAME."
+  (plist-get (cdr (assq function-name byte-to-native-plist-environment))
+             spec))
+
+(defun comp-spill-speed (function-name)
+  "Return the speed for FUNCTION-NAME."
+  (or (comp-spill-decl-spec function-name 'speed)
+      (comp-ctxt-speed comp-ctxt)))
+
+;; Autoloaded as might be used by `disassemble-internal'.
+;;;###autoload
+(defun comp-c-func-name (name prefix &optional first)
+  "Given NAME, return a name suitable for the native code.
+Add PREFIX in front of it.  If FIRST is not nil, pick the first
+available name ignoring compilation context and potential name
+clashes."
+  ;; Unfortunately not all symbol names are valid as C function names...
+  ;; Nassi's algorithm here:
+  (let* ((orig-name (if (symbolp name) (symbol-name name) name))
+         (crypted (cl-loop with str = (make-string (* 2 (length orig-name)) 0)
+                          for j from 0 by 2
+                          for i across orig-name
+                          for byte = (format "%x" i)
+                          do (aset str j (aref byte 0))
+                             (aset str (1+ j) (aref byte 1))
+                          finally return str))
+         (human-readable (replace-regexp-in-string
+                          "-" "_" orig-name))
+         (human-readable (replace-regexp-in-string
+                          (rx (not (any "0-9a-z_"))) "" human-readable)))
+    (if (null first)
+        ;; Prevent C namespace conflicts.
+        (cl-loop
+         with h = (comp-ctxt-funcs-h comp-ctxt)
+         for i from 0
+         for c-sym = (concat prefix crypted "_" human-readable "_"
+                             (number-to-string i))
+         unless (gethash c-sym h)
+         return c-sym)
+      ;; When called out of a compilation context (ex disassembling)
+      ;; pick the first one.
+      (concat prefix crypted "_" human-readable "_0"))))
+
+(defun comp-decrypt-arg-list (x function-name)
+  "Decrypt argument list X for FUNCTION-NAME."
+  (unless (fixnump x)
+    (signal 'native-compiler-error-dyn-func function-name))
+  (let ((rest (not (= (logand x 128) 0)))
+        (mandatory (logand x 127))
+        (nonrest (ash x -8)))
+    (if (and (null rest)
+             (< nonrest 9)) ;; SUBR_MAX_ARGS
+        (make-comp-args :min mandatory
+                        :max nonrest)
+      (make-comp-nargs :min mandatory
+                       :nonrest nonrest
+                       :rest rest))))
+
+(defsubst comp-byte-frame-size (byte-compiled-func)
+  "Return the frame size to be allocated for BYTE-COMPILED-FUNC."
+  (aref byte-compiled-func 3))
+
+(defun comp-add-func-to-ctxt (func)
+  "Add FUNC to the current compiler context."
+  (let ((name (comp-func-name func))
+        (c-name (comp-func-c-name func)))
+    (puthash name c-name (comp-ctxt-sym-to-c-name-h comp-ctxt))
+    (puthash c-name func (comp-ctxt-funcs-h comp-ctxt))))
+
+(cl-defgeneric comp-spill-lap-function (input)
+  "Byte-compile INPUT and spill lap for further stages.")
+
+(cl-defmethod comp-spill-lap-function ((function-name symbol))
+  "Byte-compile FUNCTION-NAME, spilling data from the byte compiler."
+  (unless (comp-ctxt-output comp-ctxt)
+    (setf (comp-ctxt-output comp-ctxt)
+          (make-temp-file (comp-c-func-name function-name "freefn-")
+                          nil ".eln")))
+  (let* ((f (symbol-function function-name))
+         (c-name (comp-c-func-name function-name "F"))
+         (func (make-comp-func-l :name function-name
+                                 :c-name c-name
+                                 :doc (documentation f t)
+                                 :int-spec (interactive-form f)
+                                 :speed (comp-spill-speed function-name)
+                                 :pure (comp-spill-decl-spec function-name
+                                                             'pure))))
+      (when (byte-code-function-p f)
+        (signal 'native-compiler-error
+                "can't native compile an already bytecompiled function"))
+      (setf (comp-func-byte-func func)
+            (byte-compile (comp-func-name func)))
+      (let ((lap (byte-to-native-lambda-lap
+                  (gethash (aref (comp-func-byte-func func) 1)
+                           byte-to-native-lambdas-h))))
+        (cl-assert lap)
+        (comp-log lap 2 t)
+        (let ((arg-list (aref (comp-func-byte-func func) 0)))
+          (setf (comp-func-l-args func)
+                (comp-decrypt-arg-list arg-list function-name)
+                (comp-func-lap func)
+                lap
+                (comp-func-frame-size func)
+                (comp-byte-frame-size (comp-func-byte-func func))))
+        (setf (comp-ctxt-top-level-forms comp-ctxt)
+              (list (make-byte-to-native-func-def :name function-name
+                                                  :c-name c-name)))
+        (comp-add-func-to-ctxt func))))
+
+(cl-defmethod comp-spill-lap-function ((form list))
+  "Byte-compile FORM, spilling data from the byte compiler."
+  (unless (eq (car-safe form) 'lambda)
+    (signal 'native-compiler-error
+            "Cannot native-compile, form is not a lambda"))
+  (unless (comp-ctxt-output comp-ctxt)
+    (setf (comp-ctxt-output comp-ctxt)
+          (make-temp-file "comp-lambda-" nil ".eln")))
+  (let* ((byte-code (byte-compile form))
+         (c-name (comp-c-func-name "anonymous-lambda" "F"))
+         (func (if (comp-lex-byte-func-p byte-code)
+                   (make-comp-func-l :c-name c-name
+                                     :doc (documentation form t)
+                                     :int-spec (interactive-form form)
+                                     :speed (comp-ctxt-speed comp-ctxt))
+                 (make-comp-func-d :c-name c-name
+                                   :doc (documentation form t)
+                                   :int-spec (interactive-form form)
+                                   :speed (comp-ctxt-speed comp-ctxt)))))
+    (let ((lap (byte-to-native-lambda-lap
+                (gethash (aref byte-code 1)
+                         byte-to-native-lambdas-h))))
+      (cl-assert lap)
+      (comp-log lap 2 t)
+      (if (comp-func-l-p func)
+          (setf (comp-func-l-args func)
+                (comp-decrypt-arg-list (aref byte-code 0) byte-code))
+        (setf (comp-func-d-lambda-list func) (cadr form)))
+      (setf (comp-func-lap func) lap
+            (comp-func-frame-size func) (comp-byte-frame-size
+                                         byte-code))
+      (setf (comp-func-byte-func func) byte-code
+            (comp-ctxt-top-level-forms comp-ctxt)
+            (list (make-byte-to-native-func-def :name '--anonymous-lambda
+                                                :c-name c-name)))
+      (comp-add-func-to-ctxt func))))
+
+(defun comp-intern-func-in-ctxt (_ obj)
+  "Given OBJ of type `byte-to-native-lambda', create a function in 
`comp-ctxt'."
+  (when-let ((byte-func (byte-to-native-lambda-byte-func obj)))
+    (let* ((lap (byte-to-native-lambda-lap obj))
+           (top-l-form (cl-loop
+                        for form in (comp-ctxt-top-level-forms comp-ctxt)
+                        when (and (byte-to-native-func-def-p form)
+                                  (eq (byte-to-native-func-def-byte-func form)
+                                      byte-func))
+                        return form))
+           (name (when top-l-form
+                   (byte-to-native-func-def-name top-l-form)))
+           (c-name (comp-c-func-name (or name "anonymous-lambda") "F"))
+           (func (if (comp-lex-byte-func-p byte-func)
+                     (make-comp-func-l
+                      :args (comp-decrypt-arg-list (aref byte-func 0)
+                                                   name))
+                   (make-comp-func-d :lambda-list (aref byte-func 0)))))
+      (setf (comp-func-name func) name
+            (comp-func-byte-func func) byte-func
+            (comp-func-doc func) (documentation byte-func t)
+            (comp-func-int-spec func) (interactive-form byte-func)
+            (comp-func-c-name func) c-name
+            (comp-func-lap func) lap
+            (comp-func-frame-size func) (comp-byte-frame-size byte-func)
+            (comp-func-speed func) (comp-spill-speed name)
+            (comp-func-pure func) (comp-spill-decl-spec name 'pure))
+
+      ;; Store the c-name to have it retrivable from
+      ;; `comp-ctxt-top-level-forms'.
+      (when top-l-form
+        (setf (byte-to-native-func-def-c-name top-l-form) c-name))
+      (unless name
+        (puthash byte-func func (comp-ctxt-byte-func-to-func-h comp-ctxt)))
+      (comp-add-func-to-ctxt func)
+      (comp-log (format "Function %s:\n" name) 1)
+      (comp-log lap 1 t))))
+
+(cl-defmethod comp-spill-lap-function ((filename string))
+  "Byte-compile FILENAME, spilling data from the byte compiler."
+  (byte-compile-file filename)
+  (when (or (null byte-native-qualities)
+            (alist-get 'no-native-compile byte-native-qualities))
+    (throw 'no-native-compile nil))
+  (unless byte-to-native-top-level-forms
+    (signal 'native-compiler-error-empty-byte filename))
+  (unless (comp-ctxt-output comp-ctxt)
+    (setf (comp-ctxt-output comp-ctxt) (comp-el-to-eln-filename
+                                        filename
+                                        (when byte-native-for-bootstrap
+                                          (car (last comp-eln-load-path))))))
+  (setf (comp-ctxt-speed comp-ctxt) (alist-get 'comp-speed
+                                               byte-native-qualities)
+        (comp-ctxt-debug comp-ctxt) (alist-get 'comp-debug
+                                               byte-native-qualities)
+        (comp-ctxt-driver-options comp-ctxt) (alist-get 
'comp-native-driver-options
+                                                        byte-native-qualities)
+        (comp-ctxt-top-level-forms comp-ctxt)
+        (cl-loop
+         for form in (reverse byte-to-native-top-level-forms)
+         collect
+         (if (and (byte-to-native-func-def-p form)
+                  (eq -1
+                      (comp-spill-speed (byte-to-native-func-def-name form))))
+             (let ((byte-code (byte-to-native-func-def-byte-func form)))
+               (remhash byte-code byte-to-native-lambdas-h)
+               (make-byte-to-native-top-level
+                :form `(defalias
+                         ',(byte-to-native-func-def-name form)
+                         ,byte-code
+                         nil)
+                :lexical (comp-lex-byte-func-p byte-code)))
+           form)))
+  (maphash #'comp-intern-func-in-ctxt byte-to-native-lambdas-h))
+
+(defun comp-spill-lap (input)
+  "Byte-compile and spill the LAP representation for INPUT.
+If INPUT is a symbol, it is the function-name to be compiled.
+If INPUT is a string, it is the filename to be compiled."
+  (let ((byte-native-compiling t)
+        (byte-to-native-lambdas-h (make-hash-table :test #'eq))
+        (byte-to-native-top-level-forms ())
+        (byte-to-native-plist-environment ()))
+    (comp-spill-lap-function input)))
+
+
+;;; Limplification pass specific code.
+
+(cl-defstruct (comp-limplify (:copier nil))
+  "Support structure used during function limplification."
+  (frame nil :type (or null comp-vec)
+         :documentation "Meta-stack used to flat LAP.")
+  (curr-block nil :type comp-block
+              :documentation "Current block being limplified.")
+  (sp -1 :type number
+      :documentation "Current stack pointer while walking LAP.
+Points to the next slot to be filled.")
+  (pc 0 :type number
+      :documentation "Current program counter while walking LAP.")
+  (label-to-addr nil :type hash-table
+                 :documentation "LAP hash table -> address.")
+  (pending-blocks () :type list
+                  :documentation "List of blocks waiting for limplification."))
+
+(defconst comp-lap-eob-ops
+  '(byte-goto byte-goto-if-nil byte-goto-if-not-nil byte-goto-if-nil-else-pop
+              byte-goto-if-not-nil-else-pop byte-return byte-pushcatch
+              byte-switch byte-pushconditioncase)
+  "LAP end of basic blocks op codes.")
+
+(defun comp-lap-eob-p (inst)
+  "Return t if INST closes the current basic blocks, nil otherwise."
+  (when (memq (car inst) comp-lap-eob-ops)
+    t))
+
+(defun comp-lap-fall-through-p (inst)
+  "Return t if INST falls through, nil otherwise."
+  (when (not (memq (car inst) '(byte-goto byte-return)))
+    t))
+
+(defsubst comp-sp ()
+  "Current stack pointer."
+  (declare (gv-setter (lambda (val)
+                        `(setf (comp-limplify-sp comp-pass) ,val))))
+  (comp-limplify-sp comp-pass))
+
+(defmacro comp-with-sp (sp &rest body)
+  "Execute BODY setting the stack pointer to SP.
+Restore the original value afterwards."
+  (declare (debug (form body))
+           (indent defun))
+  (let ((sym (gensym)))
+    `(let ((,sym (comp-sp)))
+       (setf (comp-sp) ,sp)
+       (progn ,@body)
+       (setf (comp-sp) ,sym))))
+
+(defsubst comp-slot-n (n)
+  "Slot N into the meta-stack."
+  (comp-vec-aref (comp-limplify-frame comp-pass) n))
+
+(defsubst comp-slot ()
+  "Current slot into the meta-stack pointed by sp."
+  (comp-slot-n (comp-sp)))
+
+(defsubst comp-slot+1 ()
+  "Slot into the meta-stack pointed by sp + 1."
+  (comp-slot-n (1+ (comp-sp))))
+
+(defsubst comp-label-to-addr (label)
+  "Find the address of LABEL."
+  (or (gethash label (comp-limplify-label-to-addr comp-pass))
+      (signal 'native-ice (list "label not found" label))))
+
+(defsubst comp-mark-curr-bb-closed ()
+  "Mark the current basic block as closed."
+  (setf (comp-block-closed (comp-limplify-curr-block comp-pass)) t))
+
+(defun comp-bb-maybe-add (lap-addr &optional sp)
+  "If necessary create a pending basic block for LAP-ADDR with stack depth SP.
+The basic block is returned regardless it was already declared or not."
+  (let ((bb (or (cl-loop  ; See if the block was already limplified.
+                 for bb being the hash-value in (comp-func-blocks comp-func)
+                 when (and (comp-block-lap-p bb)
+                           (equal (comp-block-lap-addr bb) lap-addr))
+                   return bb)
+                (cl-find-if (lambda (bb) ; Look within the pendings blocks.
+                              (and (comp-block-lap-p bb)
+                                   (= (comp-block-lap-addr bb) lap-addr)))
+                            (comp-limplify-pending-blocks comp-pass)))))
+    (if bb
+        (progn
+          (unless (or (null sp) (= sp (comp-block-lap-sp bb)))
+            (signal 'native-ice (list "incoherent stack pointers"
+                                      sp (comp-block-lap-sp bb))))
+          bb)
+      (car (push (make--comp-block-lap lap-addr sp (comp-new-block-sym))
+                 (comp-limplify-pending-blocks comp-pass))))))
+
+(defsubst comp-call (func &rest args)
+  "Emit a call for function FUNC with ARGS."
+  `(call ,func ,@args))
+
+(defun comp-callref (func nargs stack-off)
+  "Emit a call using narg abi for FUNC.
+NARGS is the number of arguments.
+STACK-OFF is the index of the first slot frame involved."
+  `(callref ,func ,@(cl-loop repeat nargs
+                             for sp from stack-off
+                             collect (comp-slot-n sp))))
+
+(cl-defun make-comp-mvar (&key slot (constant nil const-vld) type)
+  "`comp-mvar' initializer."
+  (let ((mvar (make--comp-mvar :slot slot)))
+    (when const-vld
+      (comp-add-const-to-relocs constant)
+      (setf (comp-cstr-imm mvar) constant))
+    (when type
+      (setf (comp-mvar-typeset mvar) (list type)))
+    mvar))
+
+(defun comp-new-frame (size vsize &optional ssa)
+  "Return a clean frame of meta variables of size SIZE and VSIZE.
+If SSA is non-nil, populate it with m-var in ssa form."
+  (cl-loop with v = (make-comp-vec :beg (- vsize) :end size)
+           for i from (- vsize) below size
+           for mvar = (if ssa
+                          (make-comp-ssa-mvar :slot i)
+                        (make-comp-mvar :slot i))
+           do (setf (comp-vec-aref v i) mvar)
+           finally return v))
+
+(defun comp-emit (insn)
+  "Emit INSN into basic block BB."
+  (let ((bb (comp-limplify-curr-block comp-pass)))
+    (cl-assert (not (comp-block-closed bb)))
+    (push insn (comp-block-insns bb))))
+
+(defun comp-emit-set-call (call)
+  "Emit CALL assigning the result the the current slot frame.
+If the callee function is known to have a return type, propagate it."
+  (cl-assert call)
+  (comp-emit (list 'set (comp-slot) call)))
+
+(defun comp-copy-slot (src-n &optional dst-n)
+  "Set slot number DST-N to slot number SRC-N as source.
+If DST-N is specified, use it; otherwise assume it to be the current slot."
+  (comp-with-sp (or dst-n (comp-sp))
+    (let ((src-slot (comp-slot-n src-n)))
+      (cl-assert src-slot)
+      (comp-emit `(set ,(comp-slot) ,src-slot)))))
+
+(defsubst comp-emit-annotation (str)
+  "Emit annotation STR."
+  (comp-emit `(comment ,str)))
+
+(defsubst comp-emit-setimm (val)
+  "Set constant VAL to current slot."
+  (comp-add-const-to-relocs val)
+  ;; Leave relocation index nil on purpose, will be fixed-up in final
+  ;; by `comp-finalize-relocs'.
+  (comp-emit `(setimm ,(comp-slot) ,val)))
+
+(defun comp-make-curr-block (block-name entry-sp &optional addr)
+  "Create a basic block with BLOCK-NAME and set it as current block.
+ENTRY-SP is the sp value when entering.
+Add block to the current function and return it."
+  (let ((bb (make--comp-block-lap addr entry-sp block-name)))
+    (setf (comp-limplify-curr-block comp-pass) bb
+          (comp-limplify-pc comp-pass) addr
+          (comp-limplify-sp comp-pass) (when (comp-block-lap-p bb)
+                                         (comp-block-lap-sp bb)))
+    (puthash (comp-block-name bb) bb (comp-func-blocks comp-func))
+    bb))
+
+(defun comp-latch-make-fill (target)
+  "Create a latch pointing to TARGET and fill it.
+Return the created latch."
+  (let ((latch (make-comp-latch :name (comp-new-block-sym "latch")))
+        (curr-bb (comp-limplify-curr-block comp-pass)))
+    ;; See `comp-make-curr-block'.
+    (setf (comp-limplify-curr-block comp-pass) latch)
+    (when (< (comp-func-speed comp-func) 3)
+      ;; At speed 3 the programmer is responsible to manually
+      ;; place `comp-maybe-gc-or-quit'.
+      (comp-emit '(call comp-maybe-gc-or-quit)))
+    ;; See `comp-emit-uncond-jump'.
+    (comp-emit `(jump ,(comp-block-name target)))
+    (comp-mark-curr-bb-closed)
+    (puthash (comp-block-name latch) latch (comp-func-blocks comp-func))
+    (setf (comp-limplify-curr-block comp-pass) curr-bb)
+    latch))
+
+(defun comp-emit-uncond-jump (lap-label)
+  "Emit an unconditional branch to LAP-LABEL."
+  (cl-destructuring-bind (label-num . stack-depth) lap-label
+    (when stack-depth
+      (cl-assert (= (1- stack-depth) (comp-sp))))
+    (let* ((target-addr (comp-label-to-addr label-num))
+           (target (comp-bb-maybe-add target-addr
+                                      (comp-sp)))
+           (latch (when (< target-addr (comp-limplify-pc comp-pass))
+                    (comp-latch-make-fill target)))
+           (eff-target-name (comp-block-name (or latch target))))
+      (comp-emit `(jump ,eff-target-name))
+      (comp-mark-curr-bb-closed))))
+
+(defun comp-emit-cond-jump (a b target-offset lap-label negated)
+  "Emit a conditional jump to LAP-LABEL when A and B satisfy EQ.
+TARGET-OFFSET is the positive offset on the SP when branching to the target
+block.
+If NEGATED is non null, negate the tested condition.
+Return value is the fall-through block name."
+  (cl-destructuring-bind (label-num . label-sp) lap-label
+    (let* ((bb (comp-block-name (comp-bb-maybe-add
+                                 (1+ (comp-limplify-pc comp-pass))
+                                 (comp-sp)))) ; Fall through block.
+           (target-sp (+ target-offset (comp-sp)))
+           (target-addr (comp-label-to-addr label-num))
+           (target (comp-bb-maybe-add target-addr target-sp))
+           (latch (when (< target-addr (comp-limplify-pc comp-pass))
+                    (comp-latch-make-fill target)))
+           (eff-target-name (comp-block-name (or latch target))))
+      (when label-sp
+        (cl-assert (= (1- label-sp) (+ target-offset (comp-sp)))))
+      (comp-emit (if negated
+                     (list 'cond-jump a b bb eff-target-name)
+                  (list 'cond-jump a b eff-target-name bb)))
+      (comp-mark-curr-bb-closed)
+      bb)))
+
+(defun comp-emit-handler (lap-label handler-type)
+  "Emit a nonlocal-exit handler to LAP-LABEL of type HANDLER-TYPE."
+  (cl-destructuring-bind (label-num . label-sp) lap-label
+    (cl-assert (= (- label-sp 2) (comp-sp)))
+    (setf (comp-func-has-non-local comp-func) t)
+    (let* ((guarded-bb (comp-bb-maybe-add (1+ (comp-limplify-pc comp-pass))
+                                          (comp-sp)))
+           (handler-bb (comp-bb-maybe-add (comp-label-to-addr label-num)
+                                          (1+ (comp-sp))))
+           (pop-bb (make--comp-block-lap nil (comp-sp) (comp-new-block-sym))))
+      (comp-emit (list 'push-handler
+                       handler-type
+                       (comp-slot+1)
+                       (comp-block-name pop-bb)
+                       (comp-block-name guarded-bb)))
+      (comp-mark-curr-bb-closed)
+      ;; Emit the basic block to pop the handler if we got the non local.
+      (puthash (comp-block-name pop-bb) pop-bb (comp-func-blocks comp-func))
+      (setf (comp-limplify-curr-block comp-pass) pop-bb)
+      (comp-emit `(fetch-handler ,(comp-slot+1)))
+      (comp-emit `(jump ,(comp-block-name handler-bb)))
+      (comp-mark-curr-bb-closed))))
+
+(defun comp-limplify-listn (n)
+  "Limplify list N."
+  (comp-with-sp (+ (comp-sp) n -1)
+    (comp-emit-set-call (comp-call 'cons
+                                   (comp-slot)
+                                   (make-comp-mvar :constant nil))))
+  (cl-loop for sp from (+ (comp-sp) n -2) downto (comp-sp)
+           do (comp-with-sp sp
+                (comp-emit-set-call (comp-call 'cons
+                                               (comp-slot)
+                                               (comp-slot+1))))))
+
+(defun comp-new-block-sym (&optional postfix)
+  "Return a unique symbol postfixing POSTFIX naming the next new basic block."
+  (intern (format (if postfix "bb_%s_%s" "bb_%s")
+                  (funcall (comp-func-block-cnt-gen comp-func))
+                  postfix)))
+
+(defun comp-fill-label-h ()
+  "Fill label-to-addr hash table for the current function."
+  (setf (comp-limplify-label-to-addr comp-pass) (make-hash-table :test 'eql))
+  (cl-loop for insn in (comp-func-lap comp-func)
+           for addr from 0
+           do (pcase insn
+                (`(TAG ,label . ,_)
+                 (puthash label addr (comp-limplify-label-to-addr 
comp-pass))))))
+
+(defun comp-jump-table-optimizable (jmp-table)
+  "Return t if JMP-TABLE can be optimized out."
+  (cl-loop
+   with labels = (cl-loop for target-label being each hash-value of jmp-table
+                          collect target-label)
+   with x = (car labels)
+   for l in (cdr-safe labels)
+   unless (= l x)
+     return nil
+   finally return t))
+
+(defun comp-emit-switch (var last-insn)
+  "Emit a limple for a lap jump table given VAR and LAST-INSN."
+  ;; FIXME this not efficient for big jump tables. We should have a second
+  ;; strategy for this case.
+  (pcase last-insn
+    (`(setimm ,_ ,jmp-table)
+     (unless (comp-jump-table-optimizable jmp-table)
+       (cl-loop
+        for test being each hash-keys of jmp-table
+        using (hash-value target-label)
+        with len = (hash-table-count jmp-table)
+        with test-func = (hash-table-test jmp-table)
+        for n from 1
+        for last = (= n len)
+        for m-test = (make-comp-mvar :constant test)
+        for target-name = (comp-block-name (comp-bb-maybe-add
+                                            (comp-label-to-addr target-label)
+                                            (comp-sp)))
+        for ff-bb = (if last
+                        (comp-bb-maybe-add (1+ (comp-limplify-pc comp-pass))
+                                           (comp-sp))
+                      (make--comp-block-lap nil
+                                            (comp-sp)
+                                            (comp-new-block-sym)))
+        for ff-bb-name = (comp-block-name ff-bb)
+        if (eq test-func 'eq)
+          do (comp-emit (list 'cond-jump var m-test target-name ff-bb-name))
+        else
+        ;; Store the result of the comparison into the scratch slot before
+        ;; emitting the conditional jump.
+          do (comp-emit (list 'set (make-comp-mvar :slot 'scratch)
+                              (comp-call test-func var m-test)))
+             (comp-emit (list 'cond-jump
+                              (make-comp-mvar :slot 'scratch)
+                              (make-comp-mvar :constant nil)
+                              ff-bb-name target-name))
+        unless last
+        ;; All fall through are artificially created here except the last one.
+          do (puthash ff-bb-name ff-bb (comp-func-blocks comp-func))
+             (setf (comp-limplify-curr-block comp-pass) ff-bb))))
+    (_ (signal 'native-ice
+               "missing previous setimm while creating a switch"))))
+
+(defun comp-emit-set-call-subr (subr-name sp-delta)
+    "Emit a call for SUBR-NAME.
+SP-DELTA is the stack adjustment."
+    (let ((subr (symbol-function subr-name))
+          (nargs (1+ (- sp-delta))))
+      (let* ((arity (func-arity subr))
+             (minarg (car arity))
+             (maxarg (cdr arity)))
+        (when (eq maxarg 'unevalled)
+          (signal 'native-ice (list "subr contains  unevalled args" 
subr-name)))
+        (if (eq maxarg 'many)
+            ;; callref case.
+            (comp-emit-set-call (comp-callref subr-name nargs (comp-sp)))
+          ;; Normal call.
+          (unless (and (>= maxarg nargs) (<= minarg nargs))
+            (signal 'native-ice
+                    (list "incoherent stack adjustment" nargs maxarg minarg)))
+          (let* ((subr-name subr-name)
+                 (slots (cl-loop for i from 0 below maxarg
+                                 collect (comp-slot-n (+ i (comp-sp))))))
+            (comp-emit-set-call (apply #'comp-call (cons subr-name 
slots))))))))
+
+(eval-when-compile
+  (defun comp-op-to-fun (x)
+    "Given the LAP op strip \"byte-\" to have the subr name."
+    (intern (replace-regexp-in-string "byte-" "" x)))
+
+  (defun comp-body-eff (body op-name sp-delta)
+    "Given the original BODY, compute the effective one.
+When BODY is `auto', guess function name from the LAP byte-code
+name.  Otherwise expect lname fnname."
+    (pcase (car body)
+      ('auto
+       `((comp-emit-set-call-subr ',(comp-op-to-fun op-name) ,sp-delta)))
+      ((pred symbolp)
+       `((comp-emit-set-call-subr ',(car body) ,sp-delta)))
+      (_ body))))
+
+(defmacro comp-op-case (&rest cases)
+  "Expand CASES into the corresponding `pcase' expansion.
+This is responsible for generating the proper stack adjustment, when known,
+and the annotation emission."
+  (declare (debug (body))
+           (indent defun))
+  `(pcase op
+     ,@(cl-loop for (op . body) in cases
+               for sp-delta = (gethash op comp-op-stack-info)
+                for op-name = (symbol-name op)
+               if body
+               collect `(',op
+                          ;; Log all LAP ops except the TAG one.
+                          ;; ,(unless (eq op 'TAG)
+                          ;;    `(comp-emit-annotation
+                          ;;      ,(concat "LAP op " op-name)))
+                          ;; Emit the stack adjustment if present.
+                          ,(when (and sp-delta (not (eq 0 sp-delta)))
+                            `(cl-incf (comp-sp) ,sp-delta))
+                          ,@(comp-body-eff body op-name sp-delta))
+                else
+               collect `(',op (signal 'native-ice
+                                       (list "unsupported LAP op" ',op-name))))
+     (_ (signal 'native-ice (list "unexpected LAP op" (symbol-name op))))))
+
+(defun comp-limplify-lap-inst (insn)
+  "Limplify LAP instruction INSN pushing it in the proper basic block."
+  (let ((op (car insn))
+        (arg (if (consp (cdr insn))
+                 (cadr insn)
+               (cdr insn))))
+    (comp-op-case
+      (TAG
+       (cl-destructuring-bind (_TAG label-num . label-sp) insn
+         ;; Paranoid?
+         (when label-sp
+           (cl-assert (= (1- label-sp) (comp-limplify-sp comp-pass))))
+         (comp-emit-annotation (format "LAP TAG %d" label-num))))
+      (byte-stack-ref
+       (comp-copy-slot (- (comp-sp) arg 1)))
+      (byte-varref
+       (comp-emit-set-call (comp-call 'symbol-value (make-comp-mvar
+                                                     :constant arg))))
+      (byte-varset
+       (comp-emit (comp-call 'set_internal
+                             (make-comp-mvar :constant arg)
+                             (comp-slot+1))))
+      (byte-varbind ;; Verify
+       (comp-emit (comp-call 'specbind
+                             (make-comp-mvar :constant arg)
+                             (comp-slot+1))))
+      (byte-call
+       (cl-incf (comp-sp) (- arg))
+       (comp-emit-set-call (comp-callref 'funcall (1+ arg) (comp-sp))))
+      (byte-unbind
+       (comp-emit (comp-call 'helper_unbind_n
+                             (make-comp-mvar :constant arg))))
+      (byte-pophandler
+       (comp-emit '(pop-handler)))
+      (byte-pushconditioncase
+       (comp-emit-handler (cddr insn) 'condition-case))
+      (byte-pushcatch
+       (comp-emit-handler (cddr insn) 'catcher))
+      (byte-nth auto)
+      (byte-symbolp auto)
+      (byte-consp auto)
+      (byte-stringp auto)
+      (byte-listp auto)
+      (byte-eq auto)
+      (byte-memq auto)
+      (byte-not
+       (comp-emit-set-call (comp-call 'eq (comp-slot-n (comp-sp))
+                                      (make-comp-mvar :constant nil))))
+      (byte-car auto)
+      (byte-cdr auto)
+      (byte-cons auto)
+      (byte-list1
+       (comp-limplify-listn 1))
+      (byte-list2
+       (comp-limplify-listn 2))
+      (byte-list3
+       (comp-limplify-listn 3))
+      (byte-list4
+       (comp-limplify-listn 4))
+      (byte-length auto)
+      (byte-aref auto)
+      (byte-aset auto)
+      (byte-symbol-value auto)
+      (byte-symbol-function auto)
+      (byte-set auto)
+      (byte-fset auto)
+      (byte-get auto)
+      (byte-substring auto)
+      (byte-concat2
+       (comp-emit-set-call (comp-callref 'concat 2 (comp-sp))))
+      (byte-concat3
+       (comp-emit-set-call (comp-callref 'concat 3 (comp-sp))))
+      (byte-concat4
+       (comp-emit-set-call (comp-callref 'concat 4 (comp-sp))))
+      (byte-sub1 1-)
+      (byte-add1 1+)
+      (byte-eqlsign =)
+      (byte-gtr >)
+      (byte-lss <)
+      (byte-leq <=)
+      (byte-geq >=)
+      (byte-diff -)
+      (byte-negate
+       (comp-emit-set-call (comp-call 'negate (comp-slot))))
+      (byte-plus +)
+      (byte-max auto)
+      (byte-min auto)
+      (byte-mult *)
+      (byte-point auto)
+      (byte-goto-char auto)
+      (byte-insert auto)
+      (byte-point-max auto)
+      (byte-point-min auto)
+      (byte-char-after auto)
+      (byte-following-char auto)
+      (byte-preceding-char preceding-char)
+      (byte-current-column auto)
+      (byte-indent-to
+       (comp-emit-set-call (comp-call 'indent-to
+                                      (comp-slot)
+                                      (make-comp-mvar :constant nil))))
+      (byte-scan-buffer-OBSOLETE)
+      (byte-eolp auto)
+      (byte-eobp auto)
+      (byte-bolp auto)
+      (byte-bobp auto)
+      (byte-current-buffer auto)
+      (byte-set-buffer auto)
+      (byte-save-current-buffer
+       (comp-emit (comp-call 'record_unwind_current_buffer)))
+      (byte-set-mark-OBSOLETE)
+      (byte-interactive-p-OBSOLETE)
+      (byte-forward-char auto)
+      (byte-forward-word auto)
+      (byte-skip-chars-forward auto)
+      (byte-skip-chars-backward auto)
+      (byte-forward-line auto)
+      (byte-char-syntax auto)
+      (byte-buffer-substring auto)
+      (byte-delete-region auto)
+      (byte-narrow-to-region
+       (comp-emit-set-call (comp-call 'narrow-to-region
+                                      (comp-slot)
+                                      (comp-slot+1))))
+      (byte-widen
+       (comp-emit-set-call (comp-call 'widen)))
+      (byte-end-of-line auto)
+      (byte-constant2) ; TODO
+      ;; Branches.
+      (byte-goto
+       (comp-emit-uncond-jump (cddr insn)))
+      (byte-goto-if-nil
+       (comp-emit-cond-jump (comp-slot+1) (make-comp-mvar :constant nil) 0
+                            (cddr insn) nil))
+      (byte-goto-if-not-nil
+       (comp-emit-cond-jump (comp-slot+1) (make-comp-mvar :constant nil) 0
+                            (cddr insn) t))
+      (byte-goto-if-nil-else-pop
+       (comp-emit-cond-jump (comp-slot+1) (make-comp-mvar :constant nil) 1
+                            (cddr insn) nil))
+      (byte-goto-if-not-nil-else-pop
+       (comp-emit-cond-jump (comp-slot+1) (make-comp-mvar :constant nil) 1
+                            (cddr insn) t))
+      (byte-return
+       (comp-emit `(return ,(comp-slot+1))))
+      (byte-discard 'pass)
+      (byte-dup
+       (comp-copy-slot (1- (comp-sp))))
+      (byte-save-excursion
+       (comp-emit (comp-call 'record_unwind_protect_excursion)))
+      (byte-save-window-excursion-OBSOLETE)
+      (byte-save-restriction
+       (comp-emit (comp-call 'helper_save_restriction)))
+      (byte-catch) ;; Obsolete
+      (byte-unwind-protect
+       (comp-emit (comp-call 'helper_unwind_protect (comp-slot+1))))
+      (byte-condition-case) ;; Obsolete
+      (byte-temp-output-buffer-setup-OBSOLETE)
+      (byte-temp-output-buffer-show-OBSOLETE)
+      (byte-unbind-all) ;; Obsolete
+      (byte-set-marker auto)
+      (byte-match-beginning auto)
+      (byte-match-end auto)
+      (byte-upcase auto)
+      (byte-downcase auto)
+      (byte-string= string-equal)
+      (byte-string< string-lessp)
+      (byte-equal auto)
+      (byte-nthcdr auto)
+      (byte-elt auto)
+      (byte-member auto)
+      (byte-assq auto)
+      (byte-nreverse auto)
+      (byte-setcar auto)
+      (byte-setcdr auto)
+      (byte-car-safe auto)
+      (byte-cdr-safe auto)
+      (byte-nconc auto)
+      (byte-quo /)
+      (byte-rem %)
+      (byte-numberp auto)
+      (byte-integerp auto)
+      (byte-listN
+       (cl-incf (comp-sp) (- 1 arg))
+       (comp-emit-set-call (comp-callref 'list arg (comp-sp))))
+      (byte-concatN
+       (cl-incf (comp-sp) (- 1 arg))
+       (comp-emit-set-call (comp-callref 'concat arg (comp-sp))))
+      (byte-insertN
+       (cl-incf (comp-sp) (- 1 arg))
+       (comp-emit-set-call (comp-callref 'insert arg (comp-sp))))
+      (byte-stack-set
+       (comp-copy-slot (1+ (comp-sp)) (- (comp-sp) arg -1)))
+      (byte-stack-set2 (cl-assert nil)) ;; TODO
+      (byte-discardN
+       (cl-incf (comp-sp) (- arg)))
+      (byte-switch
+       ;; Assume to follow the emission of a setimm.
+       ;; This is checked into comp-emit-switch.
+       (comp-emit-switch (comp-slot+1)
+                         (cl-first (comp-block-insns
+                                    (comp-limplify-curr-block comp-pass)))))
+      (byte-constant
+       (comp-emit-setimm arg))
+      (byte-discardN-preserve-tos
+       (cl-incf (comp-sp) (- arg))
+       (comp-copy-slot (+ arg (comp-sp)))))))
+
+(defun comp-emit-narg-prologue (minarg nonrest rest)
+  "Emit the prologue for a narg function."
+  (cl-loop for i below minarg
+           do (comp-emit `(set-args-to-local ,(comp-slot-n i)))
+              (comp-emit '(inc-args)))
+  (cl-loop for i from minarg below nonrest
+           for bb = (intern (format "entry_%s" i))
+           for fallback = (intern (format "entry_fallback_%s" i))
+           do (comp-emit `(cond-jump-narg-leq ,i ,fallback ,bb))
+              (comp-make-curr-block bb (comp-sp))
+              (comp-emit `(set-args-to-local ,(comp-slot-n i)))
+              (comp-emit '(inc-args))
+              finally (comp-emit '(jump entry_rest_args)))
+  (when (/= minarg nonrest)
+    (cl-loop for i from minarg below nonrest
+             for bb = (intern (format "entry_fallback_%s" i))
+             for next-bb = (if (= (1+ i) nonrest)
+                               'entry_rest_args
+                             (intern (format "entry_fallback_%s" (1+ i))))
+             do (comp-with-sp i
+                  (comp-make-curr-block bb (comp-sp))
+                  (comp-emit-setimm nil)
+                  (comp-emit `(jump ,next-bb)))))
+  (comp-make-curr-block 'entry_rest_args (comp-sp))
+  (comp-emit `(set-rest-args-to-local ,(comp-slot-n nonrest)))
+  (setf (comp-sp) nonrest)
+  (when (and (> nonrest 8) (null rest))
+    (cl-decf (comp-sp))))
+
+(defun comp-limplify-finalize-function (func)
+  "Reverse insns into all basic blocks of FUNC."
+  (cl-loop for bb being the hash-value in (comp-func-blocks func)
+           do (setf (comp-block-insns bb)
+                    (nreverse (comp-block-insns bb))))
+  (comp-log-func func 2)
+  func)
+
+(cl-defgeneric comp-prepare-args-for-top-level (function)
+  "Given FUNCTION, return the two arguments for comp--register-...")
+
+(cl-defmethod comp-prepare-args-for-top-level ((function comp-func-l))
+  "Lexically-scoped FUNCTION."
+  (let ((args (comp-func-l-args function)))
+    (cons (make-comp-mvar :constant (comp-args-base-min args))
+          (make-comp-mvar :constant (if (comp-args-p args)
+                                        (comp-args-max args)
+                                      'many)))))
+
+(cl-defmethod comp-prepare-args-for-top-level ((function comp-func-d))
+  "Dynamically scoped FUNCTION."
+  (cons (make-comp-mvar :constant (func-arity (comp-func-byte-func function)))
+        (let ((comp-curr-allocation-class 'd-default))
+          ;; Lambda-lists must stay in the same relocation class of
+          ;; the object referenced by code to respect uninterned
+          ;; symbols.
+          (make-comp-mvar :constant (comp-func-d-lambda-list function)))))
+
+(cl-defgeneric comp-emit-for-top-level (form for-late-load)
+  "Emit the limple code for top level FORM.")
+
+(cl-defmethod comp-emit-for-top-level ((form byte-to-native-func-def)
+                                       for-late-load)
+  (let* ((name (byte-to-native-func-def-name form))
+         (c-name (byte-to-native-func-def-c-name form))
+         (f (gethash c-name (comp-ctxt-funcs-h comp-ctxt)))
+         (args (comp-prepare-args-for-top-level f)))
+    (cl-assert (and name f))
+    (comp-emit
+     `(set ,(make-comp-mvar :slot 1)
+           ,(comp-call (if for-late-load
+                           'comp--late-register-subr
+                         'comp--register-subr)
+                       (make-comp-mvar :constant name)
+                       (make-comp-mvar :constant c-name)
+                       (car args)
+                       (cdr args)
+                       (setf (comp-func-type f)
+                             (make-comp-mvar :constant nil))
+                       (make-comp-mvar
+                        :constant
+                        (list
+                         (let* ((h (comp-ctxt-function-docs comp-ctxt))
+                                (i (hash-table-count h)))
+                           (puthash i (comp-func-doc f) h)
+                           i)
+                         (comp-func-int-spec f)))
+                       ;; This is the compilation unit it-self passed as
+                       ;; parameter.
+                       (make-comp-mvar :slot 0))))))
+
+(cl-defmethod comp-emit-for-top-level ((form byte-to-native-top-level)
+                                       for-late-load)
+  (unless for-late-load
+    (comp-emit
+     (comp-call 'eval
+                (let ((comp-curr-allocation-class 'd-impure))
+                  (make-comp-mvar :constant
+                                  (byte-to-native-top-level-form form)))
+                (make-comp-mvar :constant
+                                (byte-to-native-top-level-lexical form))))))
+
+(defun comp-emit-lambda-for-top-level (func)
+  "Emit the creation of subrs for lambda FUNC.
+These are stored in the reloc data array."
+  (let ((args (comp-prepare-args-for-top-level func)))
+    (let ((comp-curr-allocation-class 'd-impure))
+      (comp-add-const-to-relocs (comp-func-byte-func func)))
+    (comp-emit
+     (comp-call 'comp--register-lambda
+                ;; mvar to be fixed-up when containers are
+                ;; finalized.
+                (or (gethash (comp-func-byte-func func)
+                             (comp-ctxt-lambda-fixups-h comp-ctxt))
+                    (puthash (comp-func-byte-func func)
+                             (make-comp-mvar :constant nil)
+                             (comp-ctxt-lambda-fixups-h comp-ctxt)))
+                (make-comp-mvar :constant (comp-func-c-name func))
+                (car args)
+                (cdr args)
+                (setf (comp-func-type func)
+                      (make-comp-mvar :constant nil))
+                (make-comp-mvar
+                 :constant
+                 (list
+                  (let* ((h (comp-ctxt-function-docs comp-ctxt))
+                         (i (hash-table-count h)))
+                    (puthash i (comp-func-doc func) h)
+                    i)
+                  (comp-func-int-spec func)))
+                ;; This is the compilation unit it-self passed as
+                ;; parameter.
+                (make-comp-mvar :slot 0)))))
+
+(defun comp-limplify-top-level (for-late-load)
+  "Create a limple function to modify the global environment at load.
+When FOR-LATE-LOAD is non-nil, the emitted function modifies only
+function definition.
+
+Synthesize a function called `top_level_run' that gets one single
+parameter (the compilation unit itself).  To define native
+functions, `top_level_run' will call back `comp--register-subr'
+into the C code forwarding the compilation unit."
+  ;; Once an .eln is loaded and Emacs is dumped 'top_level_run' has no
+  ;; reasons to be executed ever again.  Therefore all objects can be
+  ;; just ephemeral.
+  (let* ((comp-curr-allocation-class 'd-ephemeral)
+         (func (make-comp-func-l :name (if for-late-load
+                                           'late-top-level-run
+                                         'top-level-run)
+                                 :c-name (if for-late-load
+                                             "late_top_level_run"
+                                           "top_level_run")
+                                 :args (make-comp-args :min 1 :max 1)
+                                 ;; Frame is 2 wide: Slot 0 is the
+                                 ;; compilation unit being loaded
+                                 ;; (incoming parameter).  Slot 1 is
+                                 ;; the last function being
+                                 ;; registered.
+                                 :frame-size 2
+                                 :speed (comp-ctxt-speed comp-ctxt)))
+         (comp-func func)
+         (comp-pass (make-comp-limplify
+                     :curr-block (make--comp-block-lap -1 0 'top-level)
+                     :frame (comp-new-frame 1 0))))
+    (comp-make-curr-block 'entry (comp-sp))
+    (comp-emit-annotation (if for-late-load
+                              "Late top level"
+                            "Top level"))
+    ;; Assign the compilation unit incoming as parameter to the slot frame 0.
+    (comp-emit `(set-par-to-local ,(comp-slot-n 0) 0))
+    (maphash (lambda (_ func)
+               (comp-emit-lambda-for-top-level func))
+             (comp-ctxt-byte-func-to-func-h comp-ctxt))
+    (mapc (lambda (x) (comp-emit-for-top-level x for-late-load))
+          (comp-ctxt-top-level-forms comp-ctxt))
+    (comp-emit `(return ,(make-comp-mvar :slot 1)))
+    (comp-limplify-finalize-function func)))
+
+(defun comp-addr-to-bb-name (addr)
+  "Search for a block starting at ADDR into pending or limplified blocks."
+  ;; FIXME Actually we could have another hash for this.
+  (cl-flet ((pred (bb)
+              (equal (comp-block-lap-addr bb) addr)))
+    (if-let ((pending (cl-find-if #'pred
+                                  (comp-limplify-pending-blocks comp-pass))))
+        (comp-block-name pending)
+      (cl-loop for bb being the hash-value in (comp-func-blocks comp-func)
+               when (pred bb)
+                 return (comp-block-name bb)))))
+
+(defun comp-limplify-block (bb)
+  "Limplify basic-block BB and add it to the current function."
+  (setf (comp-limplify-curr-block comp-pass) bb
+        (comp-limplify-sp comp-pass) (comp-block-lap-sp bb)
+        (comp-limplify-pc comp-pass) (comp-block-lap-addr bb))
+  (puthash (comp-block-name bb) bb (comp-func-blocks comp-func))
+  (cl-loop
+   for inst-cell on (nthcdr (comp-limplify-pc comp-pass)
+                            (comp-func-lap comp-func))
+   for inst = (car inst-cell)
+   for next-inst = (car-safe (cdr inst-cell))
+   do (comp-limplify-lap-inst inst)
+      (cl-incf (comp-limplify-pc comp-pass))
+   when (comp-lap-fall-through-p inst)
+   do (pcase next-inst
+        (`(TAG ,_label . ,label-sp)
+         (when label-sp
+           (cl-assert (= (1- label-sp) (comp-sp))))
+         (let* ((stack-depth (if label-sp
+                                 (1- label-sp)
+                               (comp-sp)))
+                (next-bb (comp-block-name (comp-bb-maybe-add
+                                           (comp-limplify-pc comp-pass)
+                                           stack-depth))))
+           (unless (comp-block-closed bb)
+             (comp-emit `(jump ,next-bb))))
+         (cl-return)))
+   until (comp-lap-eob-p inst)))
+
+(defun comp-limplify-function (func)
+  "Limplify a single function FUNC."
+  (let* ((frame-size (comp-func-frame-size func))
+         (comp-func func)
+         (comp-pass (make-comp-limplify
+                     :frame (comp-new-frame frame-size 0))))
+    (comp-fill-label-h)
+    ;; Prologue
+    (comp-make-curr-block 'entry (comp-sp))
+    (comp-emit-annotation (concat "Lisp function: "
+                                  (symbol-name (comp-func-name func))))
+    ;; Dynamic functions have parameters bound by the trampoline.
+    (when (comp-func-l-p func)
+      (let ((args (comp-func-l-args func)))
+        (if (comp-args-p args)
+            (cl-loop for i below (comp-args-max args)
+                     do (cl-incf (comp-sp))
+                        (comp-emit `(set-par-to-local ,(comp-slot) ,i)))
+          (comp-emit-narg-prologue (comp-args-base-min args)
+                                   (comp-nargs-nonrest args)
+                                   (comp-nargs-rest args)))))
+    (comp-emit '(jump bb_0))
+    ;; Body
+    (comp-bb-maybe-add 0 (comp-sp))
+    (cl-loop for next-bb = (pop (comp-limplify-pending-blocks comp-pass))
+             while next-bb
+             do (comp-limplify-block next-bb))
+    ;; Sanity check against block duplication.
+    (cl-loop with addr-h = (make-hash-table)
+             for bb being the hash-value in (comp-func-blocks func)
+             for addr = (when (comp-block-lap-p bb)
+                          (comp-block-lap-addr bb))
+             when addr
+               do (cl-assert (null (gethash addr addr-h)))
+                  (puthash addr t addr-h))
+    (comp-limplify-finalize-function func)))
+
+(defun comp-limplify (_)
+  "Compute LIMPLE IR for forms in `comp-ctxt'."
+  (maphash (lambda (_ f) (comp-limplify-function f))
+           (comp-ctxt-funcs-h comp-ctxt))
+  (comp-add-func-to-ctxt (comp-limplify-top-level nil))
+  (when (comp-ctxt-with-late-load comp-ctxt)
+    (comp-add-func-to-ctxt (comp-limplify-top-level t))))
+
+
+;;; add-cstrs pass specific code.
+
+;; This pass is responsible for adding constraints, these are
+;; generated from:
+;;
+;;  - Conditional branches: each branch taken or non taken can be used
+;;    in the CFG to infer information on the tested variables.
+;;
+;;  - Range propagation under test and branch (when the test is an
+;;    arithmetic comparison).
+;;
+;;  - Type constraint under test and branch (when the test is a
+;;    known predicate).
+;;
+;;  - Function calls: function calls to function assumed to be not
+;;    redefinable can be used to add constrains on the function
+;;    arguments.  Ex: if we execute successfully (= x y) we know that
+;;    afterwards both x and y must satisfy the (or number marker)
+;;    type specifier.
+
+
+(defsubst comp-mvar-used-p (mvar)
+  "Non-nil when MVAR is used as lhs in the current function."
+  (declare (gv-setter (lambda (val)
+                       `(puthash ,mvar ,val comp-pass))))
+  (gethash mvar comp-pass))
+
+(defun comp-collect-mvars (form)
+  "Add rhs m-var present in FORM into `comp-pass'."
+  (cl-loop for x in form
+           if (consp x)
+             do (comp-collect-mvars x)
+           else
+             when (comp-mvar-p x)
+               do (setf (comp-mvar-used-p x) t)))
+
+(defun comp-collect-rhs ()
+  "Collect all lhs mvars into `comp-pass'."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do (cl-loop
+       for insn in (comp-block-insns b)
+       for (op . args) = insn
+       if (comp-assign-op-p op)
+         do (comp-collect-mvars (cdr args))
+       else
+         do (comp-collect-mvars args))))
+
+(defun comp-negate-arithm-cmp-fun (function)
+  "Negate FUNCTION.
+Return nil if we don't want to emit constraints for its negation."
+  (cl-ecase function
+    (= nil)
+    (> '<=)
+    (< '>=)
+    (>= '<)
+    (<= '>)))
+
+(defun comp-reverse-arithm-fun (function)
+  "Reverse FUNCTION."
+  (cl-case function
+    (= '=)
+    (> '<)
+    (< '>)
+    (>= '<=)
+    (<= '>=)
+    (t function)))
+
+(defun comp-emit-assume (kind lhs rhs bb negated)
+  "Emit an assume of kind KIND for mvar LHS being RHS.
+When NEGATED is non-nil, the assumption is negated.
+The assume is emitted at the beginning of the block BB."
+  (let ((lhs-slot (comp-mvar-slot lhs)))
+    (cl-assert lhs-slot)
+    (pcase kind
+      ((or 'and 'and-nhc)
+       (if (comp-mvar-p rhs)
+           (let ((tmp-mvar (if negated
+                               (make-comp-mvar :slot (comp-mvar-slot rhs))
+                             rhs)))
+             (push `(assume ,(make-comp-mvar :slot lhs-slot)
+                            (,kind ,lhs ,tmp-mvar))
+                  (comp-block-insns bb))
+             (if negated
+                 (push `(assume ,tmp-mvar (not ,rhs))
+                      (comp-block-insns bb))))
+         ;; If is only a constraint we can negate it directly.
+         (push `(assume ,(make-comp-mvar :slot lhs-slot)
+                        (,kind ,lhs ,(if negated
+                                       (comp-cstr-negation-make rhs)
+                                     rhs)))
+              (comp-block-insns bb))))
+      ((pred comp-arithm-cmp-fun-p)
+       (when-let ((kind (if negated
+                            (comp-negate-arithm-cmp-fun kind)
+                          kind)))
+         (push `(assume ,(make-comp-mvar :slot lhs-slot)
+                        (,kind ,lhs
+                               ,(if-let* ((vld (comp-cstr-imm-vld-p rhs))
+                                          (val (comp-cstr-imm rhs))
+                                          (ok (and (integerp val)
+                                                   (not (memq kind '(= !=))))))
+                                    val
+                                  (make-comp-mvar :slot (comp-mvar-slot 
rhs)))))
+              (comp-block-insns bb))))
+      (_ (cl-assert nil)))
+    (setf (comp-func-ssa-status comp-func) 'dirty)))
+
+(defun comp-maybe-add-vmvar (op cmp-res insns-seq)
+  "If CMP-RES is clobbering OP emit a new constrained mvar and return it.
+Return OP otherwise."
+  (if-let ((match (eql (comp-mvar-slot op) (comp-mvar-slot cmp-res)))
+           (new-mvar (make-comp-mvar
+                      :slot
+                      (- (cl-incf (comp-func-vframe-size comp-func))))))
+      (progn
+        (push `(assume ,new-mvar ,op) (cdr insns-seq))
+        new-mvar)
+    op))
+
+(defun comp-add-new-block-between (bb-symbol bb-a bb-b)
+  "Create a new basic-block named BB-SYMBOL and add it between BB-A and BB-B."
+  (cl-loop
+   with new-bb = (make-comp-block-cstr :name bb-symbol
+                                       :insns `((jump ,(comp-block-name 
bb-b))))
+   with new-edge = (make-comp-edge :src bb-a :dst new-bb)
+   for ed in (comp-block-in-edges bb-b)
+   when (eq (comp-edge-src ed) bb-a)
+   do
+   ;; Connect `ed' to `new-bb' and disconnect it from `bb-a'.
+   (cl-assert (memq ed (comp-block-out-edges bb-a)))
+   (setf (comp-edge-src ed) new-bb
+         (comp-block-out-edges bb-a) (delq ed (comp-block-out-edges bb-a)))
+   (push ed (comp-block-out-edges new-bb))
+   ;; Connect `bb-a' `new-bb' with `new-edge'.
+   (push new-edge (comp-block-out-edges bb-a))
+   (push new-edge (comp-block-in-edges new-bb))
+   (setf (comp-func-ssa-status comp-func) 'dirty)
+   ;; Add `new-edge' to the current function and return it.
+   (cl-return (puthash bb-symbol new-bb (comp-func-blocks comp-func)))
+   finally (cl-assert nil)))
+
+;; Cheap substitute to a copy propagation pass...
+(defun comp-cond-cstrs-target-mvar (mvar exit-insn bb)
+  "Given MVAR, search in BB the original mvar MVAR got assigned from.
+Keep on searching till EXIT-INSN is encountered."
+  (cl-flet ((targetp (x)
+              ;; Ret t if x is an mvar and target the correct slot number.
+              (and (comp-mvar-p x)
+                   (eql (comp-mvar-slot mvar) (comp-mvar-slot x)))))
+    (cl-loop
+     with res = nil
+     for insn in (comp-block-insns bb)
+     when (eq insn exit-insn)
+     do (cl-return (and (comp-mvar-p res) res))
+     do (pcase insn
+          (`(,(pred comp-assign-op-p) ,(pred targetp) ,rhs)
+           (setf res rhs)))
+     finally (cl-assert nil))))
+
+(defun comp-add-cond-cstrs-target-block (curr-bb target-bb-sym)
+  "Return the appropriate basic block to add constraint assumptions into.
+CURR-BB is the current basic block.
+TARGET-BB-SYM is the symbol name of the target block."
+  (let* ((target-bb (gethash target-bb-sym
+                             (comp-func-blocks comp-func)))
+         (target-bb-in-edges (comp-block-in-edges target-bb)))
+    (cl-assert target-bb-in-edges)
+    (if (length= target-bb-in-edges 1)
+        ;; If block has only one predecessor is already suitable for
+        ;; adding constraint assumptions.
+        target-bb
+      (cl-loop
+       ;; Search for the first suitable basic block name.
+       for i from 0
+       for new-name = (intern (format "%s_cstrs_%d" (symbol-name target-bb-sym)
+                                      i))
+       until (null (gethash new-name (comp-func-blocks comp-func)))
+       finally
+       ;; Add it.
+       (cl-return (comp-add-new-block-between new-name curr-bb target-bb))))))
+
+(defun comp-add-cond-cstrs-simple ()
+  "`comp-add-cstrs' worker function for each selected function."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do
+   (cl-loop
+    named in-the-basic-block
+    for insn-seq on (comp-block-insns b)
+    do
+    (pcase insn-seq
+      (`((set ,(and (pred comp-mvar-p) tmp-mvar) ,(pred comp-mvar-p))
+         ;; (comment ,_comment-str)
+         (cond-jump ,tmp-mvar ,obj2 . ,blocks))
+       (cl-loop
+        for branch-target-cell on blocks
+        for branch-target = (car branch-target-cell)
+        for negated in '(nil t)
+       when (comp-mvar-used-p tmp-mvar)
+        do
+       (let ((block-target (comp-add-cond-cstrs-target-block b branch-target)))
+          (setf (car branch-target-cell) (comp-block-name block-target))
+          (comp-emit-assume 'and tmp-mvar obj2 block-target negated))
+        finally (cl-return-from in-the-basic-block)))
+      (`((cond-jump ,obj1 ,obj2 . ,blocks))
+       (cl-loop
+        for branch-target-cell on blocks
+        for branch-target = (car branch-target-cell)
+        for negated in '(nil t)
+       when (comp-mvar-used-p obj1)
+        do
+       (let ((block-target (comp-add-cond-cstrs-target-block b branch-target)))
+          (setf (car branch-target-cell) (comp-block-name block-target))
+          (comp-emit-assume 'and obj1 obj2 block-target negated))
+        finally (cl-return-from in-the-basic-block)))))))
+
+(defun comp-add-cond-cstrs ()
+  "`comp-add-cstrs' worker function for each selected function."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do
+   (cl-loop
+    named in-the-basic-block
+    with prev-insns-seq
+    for insns-seq on (comp-block-insns b)
+    do
+    (pcase insns-seq
+      (`((set ,(and (pred comp-mvar-p) cmp-res)
+              (,(pred comp-call-op-p)
+               ,(and (or (pred comp-equality-fun-p)
+                         (pred comp-arithm-cmp-fun-p))
+                     fun)
+               ,op1 ,op2))
+        ;; (comment ,_comment-str)
+        (cond-jump ,cmp-res ,(pred comp-mvar-p) . ,blocks))
+       (cl-loop
+        with target-mvar1 = (comp-cond-cstrs-target-mvar op1 (car insns-seq) b)
+        with target-mvar2 = (comp-cond-cstrs-target-mvar op2 (car insns-seq) b)
+        for branch-target-cell on blocks
+        for branch-target = (car branch-target-cell)
+        for negated in '(t nil)
+        for kind = (cl-case fun
+                     (equal 'and-nhc)
+                     (eql 'and-nhc)
+                     (eq 'and)
+                     (t fun))
+        when (or (comp-mvar-used-p target-mvar1)
+                 (comp-mvar-used-p target-mvar2))
+        do
+        (let ((block-target (comp-add-cond-cstrs-target-block b 
branch-target)))
+          (setf (car branch-target-cell) (comp-block-name block-target))
+          (when (comp-mvar-used-p target-mvar1)
+            (comp-emit-assume kind target-mvar1
+                              (comp-maybe-add-vmvar op2 cmp-res prev-insns-seq)
+                              block-target negated))
+          (when (comp-mvar-used-p target-mvar2)
+            (comp-emit-assume (comp-reverse-arithm-fun kind)
+                              target-mvar2
+                              (comp-maybe-add-vmvar op1 cmp-res prev-insns-seq)
+                              block-target negated)))
+        finally (cl-return-from in-the-basic-block)))
+      (`((set ,(and (pred comp-mvar-p) cmp-res)
+              (,(pred comp-call-op-p)
+               ,(and (pred comp-known-predicate-p) fun)
+               ,op))
+        ;; (comment ,_comment-str)
+        (cond-jump ,cmp-res ,(pred comp-mvar-p) . ,blocks))
+       (cl-loop
+        with target-mvar = (comp-cond-cstrs-target-mvar op (car insns-seq) b)
+        with cstr = (comp-pred-to-cstr fun)
+        for branch-target-cell on blocks
+        for branch-target = (car branch-target-cell)
+        for negated in '(t nil)
+        when (comp-mvar-used-p target-mvar)
+        do
+        (let ((block-target (comp-add-cond-cstrs-target-block b 
branch-target)))
+          (setf (car branch-target-cell) (comp-block-name block-target))
+          (comp-emit-assume 'and target-mvar cstr block-target negated))
+        finally (cl-return-from in-the-basic-block)))
+      ;; Match predicate on the negated branch (unless).
+      (`((set ,(and (pred comp-mvar-p) cmp-res)
+              (,(pred comp-call-op-p)
+               ,(and (pred comp-known-predicate-p) fun)
+               ,op))
+         (set ,neg-cmp-res (call eq ,cmp-res ,(pred comp-cstr-null-p)))
+        (cond-jump ,neg-cmp-res ,(pred comp-mvar-p) . ,blocks))
+       (cl-loop
+        with target-mvar = (comp-cond-cstrs-target-mvar op (car insns-seq) b)
+        with cstr = (comp-pred-to-cstr fun)
+        for branch-target-cell on blocks
+        for branch-target = (car branch-target-cell)
+        for negated in '(nil t)
+        when (comp-mvar-used-p target-mvar)
+        do
+        (let ((block-target (comp-add-cond-cstrs-target-block b 
branch-target)))
+          (setf (car branch-target-cell) (comp-block-name block-target))
+          (comp-emit-assume 'and target-mvar cstr block-target negated))
+        finally (cl-return-from in-the-basic-block))))
+    (setf prev-insns-seq insns-seq))))
+
+(defsubst comp-insert-insn (insn insn-cell)
+  "Insert INSN as second insn of INSN-CELL."
+  (let ((next-cell (cdr insn-cell))
+        (new-cell `(,insn)))
+    (setf (cdr insn-cell) new-cell
+          (cdr new-cell) next-cell
+          (comp-func-ssa-status comp-func) 'dirty)))
+
+(defun comp-emit-call-cstr (mvar call-cell cstr)
+  "Emit a constraint CSTR for MVAR after CALL-CELL."
+  (let* ((new-mvar (make-comp-mvar :slot (comp-mvar-slot mvar)))
+         ;; Have new-mvar as LHS *and* RHS to ensure monotonicity and
+         ;; fwprop convergence!!
+         (insn `(assume ,new-mvar (and ,new-mvar ,mvar ,cstr))))
+    (comp-insert-insn insn call-cell)))
+
+(defun comp-lambda-list-gen (lambda-list)
+  "Return a generator to iterate over LAMBDA-LIST."
+  (lambda ()
+    (cl-case (car lambda-list)
+      (&optional
+       (setf lambda-list (cdr lambda-list))
+       (prog1
+           (car lambda-list)
+         (setf lambda-list (cdr lambda-list))))
+      (&rest
+       (cadr lambda-list))
+      (t
+       (prog1
+           (car lambda-list)
+         (setf lambda-list (cdr lambda-list)))))))
+
+(defun comp-add-call-cstr ()
+  "Add args assumptions for each function of which the type specifier is 
known."
+  (cl-loop
+   for bb being each hash-value of (comp-func-blocks comp-func)
+   do
+   (comp-loop-insn-in-block bb
+     (when-let ((match
+                 (pcase insn
+                   (`(set ,lhs (,(pred comp-call-op-p) ,f . ,args))
+                    (when-let ((cstr-f (gethash f comp-known-func-cstr-h)))
+                      (cl-values f cstr-f lhs args)))
+                   (`(,(pred comp-call-op-p) ,f . ,args)
+                    (when-let ((cstr-f (gethash f comp-known-func-cstr-h)))
+                      (cl-values f cstr-f nil args))))))
+       (cl-multiple-value-bind (f cstr-f lhs args) match
+         (cl-loop
+          with gen = (comp-lambda-list-gen (comp-cstr-f-args cstr-f))
+          for arg in args
+          for cstr = (funcall gen)
+          for target = (comp-cond-cstrs-target-mvar arg insn bb)
+          unless (comp-cstr-p cstr)
+            do (signal 'native-ice
+                       (list "Incoherent type specifier for function" f))
+          when (and target
+                    ;; No need to add call constraints if this is t
+                    ;; (bug#45812 bug#45705 bug#45751).
+                    (not (equal comp-cstr-t cstr))
+                    (or (null lhs)
+                        (not (eql (comp-mvar-slot lhs)
+                                  (comp-mvar-slot target)))))
+            do (comp-emit-call-cstr target insn-cell cstr)))))))
+
+(defun comp-add-cstrs (_)
+  "Rewrite conditional branches adding appropriate 'assume' insns.
+This is introducing and placing 'assume' insns in use by fwprop
+to propagate conditional branch test information on target basic
+blocks."
+  (maphash (lambda (_ f)
+             (when (and (>= (comp-func-speed f) 1)
+                        ;; No point to run this on dynamic scope as
+                        ;; this pass is effecive only on local
+                        ;; variables.
+                       (comp-func-l-p f)
+                        (not (comp-func-has-non-local f)))
+               (let ((comp-func f)
+                     (comp-pass (make-hash-table :test #'eq)))
+                 (comp-collect-rhs)
+                (comp-add-cond-cstrs-simple)
+                 (comp-add-cond-cstrs)
+                 (comp-add-call-cstr)
+                 (comp-log-func comp-func 3))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; pure-func pass specific code.
+
+;; Simple IPA pass to infer function purity of functions not
+;; explicitly declared as such.  This is effective only at speed 3 to
+;; avoid optimizing-out functions and preventing their redefinition
+;; being effective.
+
+(defun comp-collect-calls (f)
+  "Return a list with all the functions called by F."
+  (cl-loop
+   with h = (make-hash-table :test #'eq)
+   for b being each hash-value of (comp-func-blocks f)
+   do (cl-loop
+       for insn in (comp-block-insns b)
+       do (pcase insn
+            (`(set ,_lval (,(pred comp-call-op-p) ,f . ,_rest))
+             (puthash f t h))
+            (`(,(pred comp-call-op-p) ,f . ,_rest)
+             (puthash f t h))))
+   finally return (cl-loop
+                   for f being each hash-key of h
+                   collect (if (stringp f)
+                               (comp-func-name
+                                (gethash f
+                                         (comp-ctxt-funcs-h comp-ctxt)))
+                             f))))
+
+(defun comp-pure-infer-func (f)
+  "If all functions called by F are pure then F is pure too."
+  (when (and (cl-every (lambda (x)
+                         (or (comp-function-pure-p x)
+                             (eq x (comp-func-name f))))
+                       (comp-collect-calls f))
+             (not (eq (comp-func-pure f) t)))
+    (comp-log (format "%s inferred to be pure" (comp-func-name f)))
+    (setf (comp-func-pure f) t)))
+
+(defun comp-ipa-pure (_)
+  "Infer function purity."
+  (cl-loop
+   with pure-n = 0
+   for n from 1
+   while
+   (/= pure-n
+       (setf pure-n
+             (cl-loop
+              for f being each hash-value of (comp-ctxt-funcs-h comp-ctxt)
+              when (and (>= (comp-func-speed f) 3)
+                        (comp-func-l-p f)
+                        (not (comp-func-pure f)))
+              do (comp-pure-infer-func f)
+              count (comp-func-pure f))))
+   finally (comp-log (format "ipa-pure iterated %d times" n))))
+
+
+;;; SSA pass specific code.
+;; After limplification no edges are present between basic blocks and an
+;; implicit phi is present for every slot at the beginning of every basic 
block.
+;; This pass is responsible for building all the edges and replace all m-vars
+;; plus placing the needed phis.
+;; Because the number of phis placed is (supposed) to be the minimum necessary
+;; this form is called 'minimal SSA form'.
+;; This pass should be run every time basic blocks or m-var are shuffled.
+
+(cl-defun make-comp-ssa-mvar (&rest rest &key _slot _constant _type)
+  "Same as `make-comp-mvar' but set the `id' slot."
+  (let ((mvar (apply #'make-comp-mvar rest)))
+    (setf (comp-mvar-id mvar) (sxhash-eq mvar))
+    mvar))
+
+(defun comp-clean-ssa (f)
+  "Clean-up SSA for function F."
+  (setf (comp-func-edges-h f) (make-hash-table))
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks f)
+   do (setf (comp-block-in-edges b) ()
+            (comp-block-out-edges b) ()
+            (comp-block-idom b) nil
+            (comp-block-df b) (make-hash-table)
+            (comp-block-post-num b) nil
+            (comp-block-final-frame b) nil
+            ;; Prune all phis.
+            (comp-block-insns b) (cl-loop for insn in (comp-block-insns b)
+                                          unless (eq 'phi (car insn))
+                                            collect insn))))
+
+(defun comp-compute-edges ()
+  "Compute the basic block edges for the current function."
+  (cl-loop with blocks = (comp-func-blocks comp-func)
+           for bb being each hash-value of blocks
+           for last-insn = (car (last (comp-block-insns bb)))
+           for (op first second third forth) = last-insn
+           do (cl-case op
+                (jump
+                 (make-comp-edge :src bb :dst (gethash first blocks)))
+                (cond-jump
+                 (make-comp-edge :src bb :dst (gethash third blocks))
+                 (make-comp-edge :src bb :dst (gethash forth blocks)))
+                (cond-jump-narg-leq
+                 (make-comp-edge :src bb :dst (gethash second blocks))
+                 (make-comp-edge :src bb :dst (gethash third blocks)))
+                (push-handler
+                 (make-comp-edge :src bb :dst (gethash third blocks))
+                 (make-comp-edge :src bb :dst (gethash forth blocks)))
+                (return)
+                (unreachable)
+                (otherwise
+                 (signal 'native-ice
+                         (list "block does not end with a branch"
+                               bb
+                               (comp-func-name comp-func)))))
+           ;; Update edge refs into blocks.
+           finally
+           (cl-loop
+            for edge being the hash-value in (comp-func-edges-h comp-func)
+            do
+            (push edge
+                  (comp-block-out-edges (comp-edge-src edge)))
+            (push edge
+                  (comp-block-in-edges (comp-edge-dst edge))))
+           (comp-log-edges comp-func)))
+
+(defun comp-collect-rev-post-order (basic-block)
+  "Walk BASIC-BLOCK children and return their name in reversed post-order."
+  (let ((visited (make-hash-table))
+        (acc ()))
+    (cl-labels ((collect-rec (bb)
+                  (let ((name (comp-block-name bb)))
+                    (unless (gethash name visited)
+                      (puthash name t visited)
+                      (cl-loop for e in (comp-block-out-edges bb)
+                               for dst-block = (comp-edge-dst e)
+                               do (collect-rec dst-block))
+                      (push name acc)))))
+      (collect-rec basic-block)
+      acc)))
+
+(defun comp-compute-dominator-tree ()
+  "Compute immediate dominators for each basic block in current function."
+  ;; Originally based on: "A Simple, Fast Dominance Algorithm"
+  ;; Cooper, Keith D.; Harvey, Timothy J.; Kennedy, Ken (2001).
+  (cl-flet ((intersect (b1 b2)
+              (let ((finger1 (comp-block-post-num b1))
+                    (finger2 (comp-block-post-num b2)))
+                (while (not (= finger1 finger2))
+                  (while (< finger1 finger2)
+                    (setf b1 (comp-block-idom b1)
+                          finger1 (comp-block-post-num b1)))
+                  (while (< finger2 finger1)
+                    (setf b2 (comp-block-idom b2)
+                          finger2 (comp-block-post-num b2))))
+                b1))
+            (first-processed (l)
+              (if-let ((p (cl-find-if (lambda (p) (comp-block-idom p)) l)))
+                  p
+                (signal 'native-ice "cant't find first preprocessed"))))
+
+    (when-let ((blocks (comp-func-blocks comp-func))
+               (entry (gethash 'entry blocks))
+               ;; No point to go on if the only bb is 'entry'.
+               (bb0 (gethash 'bb_0 blocks)))
+      (cl-loop
+       with rev-bb-list = (comp-collect-rev-post-order entry)
+       with changed = t
+       while changed
+       initially (progn
+                   (comp-log "Computing dominator tree...\n" 2)
+                   (setf (comp-block-idom entry) entry)
+                   ;; Set the post order number.
+                   (cl-loop for name in (reverse rev-bb-list)
+                            for b = (gethash name blocks)
+                            for i from 0
+                            do (setf (comp-block-post-num b) i)))
+       do (cl-loop
+           for name in (cdr rev-bb-list)
+           for b = (gethash name blocks)
+           for preds = (comp-block-preds b)
+           for new-idom = (first-processed preds)
+           initially (setf changed nil)
+           do (cl-loop for p in (delq new-idom preds)
+                       when (comp-block-idom p)
+                       do (setf new-idom (intersect p new-idom)))
+           unless (eq (comp-block-idom b) new-idom)
+           do (setf (comp-block-idom b) (unless (and (comp-block-lap-p 
new-idom)
+                                                    (comp-block-lap-no-ret
+                                                     new-idom))
+                                         new-idom)
+                    changed t))))))
+
+(defun comp-compute-dominator-frontiers ()
+  "Compute the dominator frontier for each basic block in `comp-func'."
+  ;; Originally based on: "A Simple, Fast Dominance Algorithm"
+  ;; Cooper, Keith D.; Harvey, Timothy J.; Kennedy, Ken (2001).
+  (cl-loop with blocks = (comp-func-blocks comp-func)
+           for b-name being each hash-keys of blocks
+           using (hash-value b)
+           for preds = (comp-block-preds b)
+           when (length> preds 1) ; All joins
+           do (cl-loop for p in preds
+                       for runner = p
+                       do (while (not (eq runner (comp-block-idom b)))
+                            (puthash b-name b (comp-block-df runner))
+                            (setf runner (comp-block-idom runner))))))
+
+(defun comp-log-block-info ()
+  "Log basic blocks info for the current function."
+  (maphash (lambda (name bb)
+             (let ((dom (comp-block-idom bb))
+                   (df (comp-block-df bb)))
+               (comp-log (format "block: %s idom: %s DF %s\n"
+                                 name
+                                 (when dom (comp-block-name dom))
+                                 (cl-loop for b being each hash-keys of df
+                                          collect b))
+                         3)))
+           (comp-func-blocks comp-func)))
+
+(defun comp-place-phis ()
+  "Place phi insns into the current function."
+  ;; Originally based on: Static Single Assignment Book
+  ;; Algorithm 3.1: Standard algorithm for inserting phi-functions
+  (cl-flet ((add-phi (slot-n bb)
+             ;; Add a phi func for slot SLOT-N at the top of BB.
+             (push `(phi ,slot-n) (comp-block-insns bb)))
+            (slot-assigned-p (slot-n bb)
+             ;; Return t if a SLOT-N was assigned within BB.
+             (cl-loop for insn in (comp-block-insns bb)
+                      for op = (car insn)
+                      when (or (and (comp-assign-op-p op)
+                                    (eql slot-n (comp-mvar-slot (cadr insn))))
+                               ;; fetch-handler is after a non local
+                               ;; therefore clobbers all frame!!!
+                               (eq op 'fetch-handler))
+                        return t)))
+
+    (cl-loop for i from (- (comp-func-vframe-size comp-func))
+                   below (comp-func-frame-size comp-func)
+             ;; List of blocks with a definition of mvar i
+             for defs-v = (cl-loop with blocks = (comp-func-blocks comp-func)
+                                   for b being each hash-value of blocks
+                                   when (slot-assigned-p i b)
+                                   collect b)
+             ;; Set of basic blocks where phi is added.
+             for f = ()
+             ;; Worklist, set of basic blocks that contain definitions of v.
+             for w = defs-v
+             do
+             (while w
+               (let ((x (pop w)))
+                 (cl-loop for y being each hash-value of (comp-block-df x)
+                          unless (cl-find y f)
+                          do (add-phi i y)
+                             (push y f)
+                             ;; Adding a phi implies mentioning the
+                             ;; corresponding slot so in case adjust w.
+                             (unless (cl-find y defs-v)
+                               (push y w))))))))
+
+(defun comp-dom-tree-walker (bb pre-lambda post-lambda)
+  "Dominator tree walker function starting from basic block BB.
+PRE-LAMBDA and POST-LAMBDA are called in pre or post-order if non-nil."
+  (when pre-lambda
+    (funcall pre-lambda bb))
+  (when-let ((out-edges (comp-block-out-edges bb)))
+    (cl-loop for ed in out-edges
+             for child = (comp-edge-dst ed)
+             when (eq bb (comp-block-idom child))
+             ;; Current block is the immediate dominator then recur.
+             do (comp-dom-tree-walker child pre-lambda post-lambda)))
+  (when post-lambda
+    (funcall post-lambda bb)))
+
+(cl-defstruct (comp-ssa (:copier nil))
+  "Support structure used while SSA renaming."
+  (frame (comp-new-frame (comp-func-frame-size comp-func)
+                         (comp-func-vframe-size comp-func) t)
+         :type comp-vec
+         :documentation "`comp-vec' of m-vars."))
+
+(defun comp-ssa-rename-insn (insn frame)
+  (cl-loop
+   for slot-n from (- (comp-func-vframe-size comp-func))
+              below (comp-func-frame-size comp-func)
+   do
+   (cl-flet ((targetp (x)
+               ;; Ret t if x is an mvar and target the correct slot number.
+               (and (comp-mvar-p x)
+                    (eql slot-n (comp-mvar-slot x))))
+             (new-lvalue ()
+               ;; If is an assignment make a new mvar and put it as l-value.
+               (let ((mvar (make-comp-ssa-mvar :slot slot-n)))
+                 (setf (comp-vec-aref frame slot-n) mvar
+                       (cadr insn) mvar))))
+     (pcase insn
+       (`(,(pred comp-assign-op-p) ,(pred targetp) . ,_)
+        (let ((mvar (comp-vec-aref frame slot-n)))
+          (setf (cddr insn) (cl-nsubst-if mvar #'targetp (cddr insn))))
+        (new-lvalue))
+       (`(fetch-handler . ,_)
+        ;; Clobber all no matter what!
+        (setf (comp-vec-aref frame slot-n) (make-comp-ssa-mvar :slot slot-n)))
+       (`(phi ,n)
+        (when (equal n slot-n)
+          (new-lvalue)))
+       (_
+        (let ((mvar (comp-vec-aref frame slot-n)))
+          (setcdr insn (cl-nsubst-if mvar #'targetp (cdr insn)))))))))
+
+(defun comp-ssa-rename ()
+  "Entry point to rename into SSA within the current function."
+  (comp-log "Renaming\n" 2)
+  (let ((visited (make-hash-table)))
+    (cl-labels ((ssa-rename-rec (bb in-frame)
+                  (unless (gethash bb visited)
+                    (puthash bb t visited)
+                    (cl-loop for insn in (comp-block-insns bb)
+                             do (comp-ssa-rename-insn insn in-frame))
+                    (setf (comp-block-final-frame bb)
+                          (copy-sequence in-frame))
+                    (when-let ((out-edges (comp-block-out-edges bb)))
+                      (cl-loop
+                       for ed in out-edges
+                       for child = (comp-edge-dst ed)
+                       ;; Provide a copy of the same frame to all children.
+                       do (ssa-rename-rec child (comp-vec-copy in-frame)))))))
+
+      (ssa-rename-rec (gethash 'entry (comp-func-blocks comp-func))
+                      (comp-new-frame (comp-func-frame-size comp-func)
+                                      (comp-func-vframe-size comp-func)
+                                      t)))))
+
+(defun comp-finalize-phis ()
+  "Fixup r-values into phis in all basic blocks."
+  (cl-flet ((finalize-phi (args b)
+              ;; Concatenate into args all incoming m-vars for this phi.
+              (setcdr args
+                      (cl-loop with slot-n = (comp-mvar-slot (car args))
+                               for e in (comp-block-in-edges b)
+                               for b = (comp-edge-src e)
+                               for in-frame = (comp-block-final-frame b)
+                               collect (list (comp-vec-aref in-frame slot-n)
+                                             (comp-block-name b))))))
+
+    (cl-loop for b being each hash-value of (comp-func-blocks comp-func)
+             do (cl-loop for (op . args) in (comp-block-insns b)
+                         when (eq op 'phi)
+                           do (finalize-phi args b)))))
+
+(defun comp-remove-unreachable-blocks ()
+  "Remove unreachable basic blocks.
+Return t when one or more block was removed, nil otherwise."
+  (cl-loop
+   with ret
+   for bb being each hash-value of (comp-func-blocks comp-func)
+   for bb-name = (comp-block-name bb)
+   when (and (not (eq 'entry bb-name))
+             (null (comp-block-idom bb)))
+   do
+   (comp-log (format "Removing block: %s" bb-name) 1)
+   (remhash bb-name (comp-func-blocks comp-func))
+   (setf (comp-func-ssa-status comp-func) t
+              ret t)
+   finally return ret))
+
+(defun comp-ssa ()
+  "Port all functions into minimal SSA form."
+  (maphash (lambda (_ f)
+             (let* ((comp-func f)
+                    (ssa-status (comp-func-ssa-status f)))
+               (unless (eq ssa-status t)
+                 (cl-loop
+                  when (eq ssa-status 'dirty)
+                    do (comp-clean-ssa f)
+                  do (comp-compute-edges)
+                     (comp-compute-dominator-tree)
+                 until (null (comp-remove-unreachable-blocks)))
+                 (comp-compute-dominator-frontiers)
+                 (comp-log-block-info)
+                 (comp-place-phis)
+                 (comp-ssa-rename)
+                 (comp-finalize-phis)
+                 (comp-log-func comp-func 3)
+                 (setf (comp-func-ssa-status f) t))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; propagate pass specific code.
+;; A very basic propagation pass follows.
+;; This propagates values and types plus ref property in the control flow 
graph.
+;; This is also responsible for removing function calls to pure functions if
+;; possible.
+
+(defconst comp-fwprop-max-insns-scan 4500
+  ;; Choosen as ~ the greatest required value for full convergence
+  ;; native compiling all Emacs codebase.
+  "Max number of scanned insn before giving-up.")
+
+(defun comp-copy-insn (insn)
+  "Deep copy INSN."
+  ;; Adapted from `copy-tree'.
+  (if (consp insn)
+      (let (result)
+       (while (consp insn)
+         (let ((newcar (car insn)))
+           (if (or (consp (car insn)) (comp-mvar-p (car insn)))
+               (setf newcar (comp-copy-insn (car insn))))
+           (push newcar result))
+         (setf insn (cdr insn)))
+       (nconc (nreverse result)
+               (if (comp-mvar-p insn) (comp-copy-insn insn) insn)))
+    (if (comp-mvar-p insn)
+        (copy-comp-mvar insn)
+      insn)))
+
+(defmacro comp-apply-in-env (func &rest args)
+  "Apply FUNC to ARGS in the current compilation environment."
+  `(let ((env (cl-loop
+               for f being the hash-value in (comp-ctxt-funcs-h comp-ctxt)
+               for func-name = (comp-func-name f)
+               for byte-code = (comp-func-byte-func f)
+               when func-name
+               collect `(,func-name . ,(symbol-function func-name))
+               and do
+               (setf (symbol-function func-name) byte-code))))
+     (unwind-protect
+         (apply ,func ,@args)
+       (cl-loop
+        for (func-name . def) in env
+        do (setf (symbol-function func-name) def)))))
+
+(defun comp-fwprop-prologue ()
+  "Prologue for the propagate pass.
+Here goes everything that can be done not iteratively (read once).
+Forward propagate immediate involed in assignments."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do (cl-loop
+       for insn in (comp-block-insns b)
+       do (pcase insn
+            (`(setimm ,lval ,v)
+             (setf (comp-cstr-imm lval) v))))))
+
+(defun comp-mvar-propagate (lval rval)
+  "Propagate into LVAL properties of RVAL."
+  (setf (comp-mvar-typeset lval) (comp-mvar-typeset rval)
+        (comp-mvar-valset lval) (comp-mvar-valset rval)
+        (comp-mvar-range lval) (comp-mvar-range rval)
+        (comp-mvar-neg lval) (comp-mvar-neg rval)))
+
+(defun comp-function-foldable-p (f args)
+  "Given function F called with ARGS, return non-nil when optimizable."
+  (and (comp-function-pure-p f)
+       (cl-every #'comp-cstr-imm-vld-p args)))
+
+(defun comp-function-call-maybe-fold (insn f args)
+  "Given INSN, when F is pure if all ARGS are known, remove the function call.
+Return non-nil if the function is folded successfully."
+  (cl-flet ((rewrite-insn-as-setimm (insn value)
+               ;; See `comp-emit-setimm'.
+               (comp-add-const-to-relocs value)
+               (setf (car insn) 'setimm
+                     (cddr insn) `(,value))))
+    (cond
+     ((eq f 'symbol-value)
+      (when-let* ((arg0 (car args))
+                  (const (comp-cstr-imm-vld-p arg0))
+                  (ok-to-optim (member (comp-cstr-imm arg0)
+                                       comp-symbol-values-optimizable)))
+        (rewrite-insn-as-setimm insn (symbol-value (comp-cstr-imm
+                                                    (car args))))))
+     ((comp-function-foldable-p f args)
+      (ignore-errors
+        ;; No point to complain here in case of error because we
+        ;; should do basic block pruning in order to be sure that this
+        ;; is not dead-code.  This is now left to gcc, to be
+        ;; implemented only if we want a reliable diagnostic here.
+        (let* ((f (if-let (f-in-ctxt (comp-symbol-func-to-fun f))
+                      ;; If the function is IN the compilation ctxt
+                      ;; and know to be pure.
+                      (comp-func-byte-func f-in-ctxt)
+                    f))
+               (value (comp-apply-in-env f (mapcar #'comp-cstr-imm args))))
+          (rewrite-insn-as-setimm insn value)))))))
+
+(defun comp-fwprop-call (insn lval f args)
+  "Propagate on a call INSN into LVAL.
+F is the function being called with arguments ARGS.
+Fold the call in case."
+  (unless (comp-function-call-maybe-fold insn f args)
+    (when (and (eq 'funcall f)
+               (comp-cstr-imm-vld-p (car args)))
+      (setf f (comp-cstr-imm (car args))
+            args (cdr args)))
+    (when-let ((cstr-f (gethash f comp-known-func-cstr-h)))
+      (let ((cstr (comp-cstr-f-ret cstr-f)))
+        (when (comp-cstr-empty-p cstr)
+          ;; Store it to be rewrittein as non local exit.
+          (setf (comp-block-lap-non-ret-insn comp-block) insn))
+        (setf (comp-mvar-range lval) (comp-cstr-range cstr)
+              (comp-mvar-valset lval) (comp-cstr-valset cstr)
+              (comp-mvar-typeset lval) (comp-cstr-typeset cstr)
+              (comp-mvar-neg lval) (comp-cstr-neg cstr))))
+    (cl-case f
+      (+ (comp-cstr-add lval args))
+      (- (comp-cstr-sub lval args))
+      (1+ (comp-cstr-add lval `(,(car args) ,comp-cstr-one)))
+      (1- (comp-cstr-sub lval `(,(car args) ,comp-cstr-one))))))
+
+(defun comp-fwprop-insn (insn)
+  "Propagate within INSN."
+  (pcase insn
+    (`(set ,lval ,rval)
+     (pcase rval
+       (`(,(or 'call 'callref) ,f . ,args)
+        (comp-fwprop-call insn lval f args))
+       (`(,(or 'direct-call 'direct-callref) ,f . ,args)
+        (let ((f (comp-func-name (gethash f (comp-ctxt-funcs-h comp-ctxt)))))
+          (comp-fwprop-call insn lval f args)))
+       (_
+        (comp-mvar-propagate lval rval))))
+    (`(assume ,lval ,(and (pred comp-mvar-p) rval))
+     (comp-mvar-propagate lval rval))
+    (`(assume ,lval (,kind . ,operands))
+     (cl-case kind
+       (and
+        (apply #'comp-cstr-intersection lval operands))
+       (and-nhc
+        (apply #'comp-cstr-intersection-no-hashcons lval operands))
+       (not
+        ;; Prevent double negation!
+        (unless (comp-cstr-neg (car operands))
+          (comp-cstr-value-negation lval (car operands))))
+       (>
+        (comp-cstr-> lval (car operands) (cadr operands)))
+       (>=
+        (comp-cstr->= lval (car operands) (cadr operands)))
+       (<
+        (comp-cstr-< lval (car operands) (cadr operands)))
+       (<=
+        (comp-cstr-<= lval (car operands) (cadr operands)))
+       (=
+        (comp-cstr-= lval (car operands) (cadr operands)))))
+    (`(setimm ,lval ,v)
+     (setf (comp-cstr-imm lval) v))
+    (`(phi ,lval . ,rest)
+     (let* ((from-latch (cl-some
+                         (lambda (x)
+                           (let* ((bb-name (cadr x))
+                                  (bb (gethash bb-name
+                                               (comp-func-blocks comp-func))))
+                             (or (comp-latch-p bb)
+                                 (when (comp-block-cstr-p bb)
+                                   (comp-latch-p (car (comp-block-preds 
bb)))))))
+                         rest))
+            (prop-fn (if from-latch
+                         #'comp-cstr-union-no-range
+                       #'comp-cstr-union))
+            (rvals (mapcar #'car rest)))
+       (apply prop-fn lval rvals)))))
+
+(defun comp-fwprop* ()
+  "Propagate for set* and phi operands.
+Return t if something was changed."
+  (cl-loop named outer
+           with modified = nil
+           with i = 0
+           for b being each hash-value of (comp-func-blocks comp-func)
+           do (cl-loop
+               with comp-block = b
+               for insn in (comp-block-insns b)
+               for orig-insn = (unless modified
+                                 ;; Save consing after 1th change.
+                                 (comp-copy-insn insn))
+               do
+               (comp-fwprop-insn insn)
+               (cl-incf i)
+               when (and (null modified) (not (equal insn orig-insn)))
+                 do (setf modified t))
+               when (> i comp-fwprop-max-insns-scan)
+                 do (cl-return-from outer nil)
+           finally return modified))
+
+(defun comp-rewrite-non-locals ()
+  "Make explicit in LIMPLE non-local exits if identified."
+  (cl-loop
+   for bb being each hash-value of (comp-func-blocks comp-func)
+   for non-local-insn = (and (comp-block-lap-p bb)
+                             (comp-block-lap-non-ret-insn bb))
+   when non-local-insn
+   do
+   ;; Rework the current block.
+   (let* ((insn-seq (memq non-local-insn (comp-block-insns bb))))
+     (setf (comp-block-lap-non-ret-insn bb) ()
+           (comp-block-lap-no-ret bb) t
+           (comp-block-out-edges bb) ()
+           ;; Prune unnecessary insns!
+           (cdr insn-seq) '((unreachable))
+           (comp-func-ssa-status comp-func) 'dirty))))
+
+(defun comp-fwprop (_)
+  "Forward propagate types and consts within the lattice."
+  (comp-ssa)
+  (comp-dead-code)
+  (maphash (lambda (_ f)
+             (when (and (>= (comp-func-speed f) 2)
+                        ;; FIXME remove the following condition when tested.
+                        (not (comp-func-has-non-local f)))
+               (let ((comp-func f))
+                 (comp-fwprop-prologue)
+                 (cl-loop
+                  for i from 1 to 100
+                  while (comp-fwprop*)
+                  finally
+                  (when (= i 100)
+                    (display-warning
+                     'comp
+                     (format "fwprop pass jammed into %s?" (comp-func-name 
f))))
+                  (comp-log (format "Propagation run %d times\n" i) 2))
+                 (comp-rewrite-non-locals)
+                 (comp-log-func comp-func 3))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; Call optimizer pass specific code.
+;; This pass is responsible for the following optimizations:
+;; - Call to subrs that are in defined in the C source and are passing through
+;;   funcall trampoline gets optimized into normal indirect calls.
+;;   This makes effectively this calls equivalent to all the subrs that got
+;;   dedicated byte-code ops.
+;;   Triggered at comp-speed >= 2.
+;; - Recursive calls gets optimized into direct calls.
+;;   Triggered at comp-speed >= 2.
+;; - Intra compilation unit procedure calls gets optimized into direct calls.
+;;   This can be a big win and even allow gcc to inline but does not make
+;;   function in the compilation unit re-definable safely without recompiling
+;;   the full compilation unit.
+;;   For this reason this is triggered only at comp-speed == 3.
+
+(defun comp-func-in-unit (func)
+  "Given FUNC return the `comp-fun' definition in the current context.
+FUNCTION can be a function-name or byte compiled function."
+  (if (symbolp func)
+      (comp-symbol-func-to-fun func)
+    (cl-assert (byte-code-function-p func))
+    (gethash func (comp-ctxt-byte-func-to-func-h comp-ctxt))))
+
+(defun comp-call-optim-form-call (callee args)
+  (cl-flet ((fill-args (args total)
+              ;; Fill missing args to reach TOTAL
+              (append args (cl-loop repeat (- total (length args))
+                                    collect (make-comp-mvar :constant nil)))))
+    (when (and callee
+               (or (symbolp callee)
+                   (gethash callee (comp-ctxt-byte-func-to-func-h comp-ctxt)))
+               (not (memq callee comp-never-optimize-functions)))
+      (let* ((f (if (symbolp callee)
+                    (symbol-function callee)
+                  (cl-assert (byte-code-function-p callee))
+                  callee))
+             (subrp (subrp f))
+             (comp-func-callee (comp-func-in-unit callee)))
+        (cond
+         ((and subrp (not (subr-native-elisp-p f)))
+          ;; Trampoline removal.
+          (let* ((callee (intern (subr-name f))) ; Fix aliased names.
+                 (maxarg (cdr (subr-arity f)))
+                 (call-type (if (if subrp
+                                    (not (numberp maxarg))
+                                  (comp-nargs-p comp-func-callee))
+                                'callref
+                              'call))
+                 (args (if (eq call-type 'callref)
+                           args
+                         (fill-args args maxarg))))
+            `(,call-type ,callee ,@args)))
+         ;; Intra compilation unit procedure call optimization.
+         ;; Attention speed 3 triggers this for non self calls too!!
+         ((and comp-func-callee
+               (comp-func-c-name comp-func-callee)
+               (or (and (>= (comp-func-speed comp-func) 3)
+                        (comp-func-unique-in-cu-p callee))
+                   (and (>= (comp-func-speed comp-func) 2)
+                        ;; Anonymous lambdas can't be redefined so are
+                        ;; always safe to optimize.
+                        (byte-code-function-p callee))))
+          (let* ((func-args (comp-func-l-args comp-func-callee))
+                 (nargs (comp-nargs-p func-args))
+                 (call-type (if nargs 'direct-callref 'direct-call))
+                 (args (if (eq call-type 'direct-callref)
+                           args
+                         (fill-args args (comp-args-max func-args)))))
+            `(,call-type ,(comp-func-c-name comp-func-callee) ,@args)))
+         ((comp-type-hint-p callee)
+          `(call ,callee ,@args)))))))
+
+(defun comp-call-optim-func ()
+  "Perform the trampoline call optimization for the current function."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do (comp-loop-insn-in-block b
+        (pcase insn
+          (`(set ,lval (callref funcall ,f . ,rest))
+           (when-let ((ok (comp-cstr-imm-vld-p f))
+                      (new-form (comp-call-optim-form-call
+                                 (comp-cstr-imm f) rest)))
+             (setf insn `(set ,lval ,new-form))))
+          (`(callref funcall ,f . ,rest)
+           (when-let ((ok (comp-cstr-imm-vld-p f))
+                      (new-form (comp-call-optim-form-call
+                                 (comp-cstr-imm f) rest)))
+             (setf insn new-form)))))))
+
+(defun comp-call-optim (_)
+  "Try to optimize out funcall trampoline usage when possible."
+  (maphash (lambda (_ f)
+             (when (and (>= (comp-func-speed f) 2)
+                        (comp-func-l-p f))
+               (let ((comp-func f))
+                 (comp-call-optim-func))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; Dead code elimination pass specific code.
+;; This simple pass try to eliminate insns became useful after propagation.
+;; Even if gcc would take care of this is good to perform this here
+;; in the hope of removing memory references.
+;;
+;; This pass can be run as last optim.
+
+(defun comp-collect-mvar-ids (insn)
+  "Collect the m-var unique identifiers into INSN."
+  (cl-loop for x in insn
+           if (consp x)
+             append (comp-collect-mvar-ids x)
+           else
+             when (comp-mvar-p x)
+               collect (comp-mvar-id x)))
+
+(defun comp-dead-assignments-func ()
+  "Clean-up dead assignments into current function.
+Return the list of m-var ids nuked."
+  (let ((l-vals ())
+        (r-vals ()))
+    ;; Collect used r and l-values.
+    (cl-loop
+     for b being each hash-value of (comp-func-blocks comp-func)
+     do (cl-loop
+         for insn in (comp-block-insns b)
+         for (op arg0 . rest) = insn
+         if (comp-assign-op-p op)
+           do (push (comp-mvar-id arg0) l-vals)
+              (setf r-vals (nconc (comp-collect-mvar-ids rest) r-vals))
+         else
+           do (setf r-vals (nconc (comp-collect-mvar-ids insn) r-vals))))
+    ;; Every l-value appearing that does not appear as r-value has no right to
+    ;; exist and gets nuked.
+    (let ((nuke-list (cl-set-difference l-vals r-vals)))
+      (comp-log (format "Function %s\nl-vals %s\nr-vals %s\nNuking ids: %s\n"
+                        (comp-func-name comp-func)
+                        l-vals
+                        r-vals
+                        nuke-list)
+                3)
+      (cl-loop
+       for b being each hash-value of (comp-func-blocks comp-func)
+       do (comp-loop-insn-in-block b
+            (cl-destructuring-bind (op &optional arg0 arg1 &rest rest) insn
+              (when (and (comp-assign-op-p op)
+                         (memq (comp-mvar-id arg0) nuke-list))
+                (setf insn
+                      (if (comp-limple-insn-call-p arg1)
+                          arg1
+                        `(comment ,(format "optimized out: %s"
+                                           insn))))))))
+      nuke-list)))
+
+(defun comp-dead-code ()
+  "Dead code elimination."
+  (maphash (lambda (_ f)
+             (when (and (>= (comp-func-speed f) 2)
+                        ;; FIXME remove the following condition when tested.
+                        (not (comp-func-has-non-local f)))
+               (cl-loop
+                for comp-func = f
+                for i from 1
+                while (comp-dead-assignments-func)
+                finally (comp-log (format "dead code rm run %d times\n" i) 2)
+                (comp-log-func comp-func 3))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; Tail Call Optimization pass specific code.
+
+(defun comp-form-tco-call-seq (args)
+  "Generate a TCO sequence for ARGS."
+  `(,@(cl-loop for arg in args
+               for i from 0
+               collect `(set ,(make-comp-mvar :slot i) ,arg))
+    (jump bb_0)))
+
+(defun comp-tco-func ()
+  "Try to pattern match and perform TCO within the current function."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do (cl-loop
+       named in-the-basic-block
+       for insns-seq on (comp-block-insns b)
+       do (pcase insns-seq
+            (`((set ,l-val (direct-call ,func . ,args))
+               ;; (comment ,_comment)
+               (return ,ret-val))
+             (when (and (string= func (comp-func-c-name comp-func))
+                        (eq l-val ret-val))
+               (let ((tco-seq (comp-form-tco-call-seq args)))
+                 (setf (car insns-seq) (car tco-seq)
+                       (cdr insns-seq) (cdr tco-seq)
+                       (comp-func-ssa-status comp-func) 'dirty)
+                 (cl-return-from in-the-basic-block))))))))
+
+(defun comp-tco (_)
+  "Simple peephole pass performing self TCO."
+  (maphash (lambda (_ f)
+             (when (and (>= (comp-func-speed f) 3)
+                        (comp-func-l-p f)
+                        (not (comp-func-has-non-local f)))
+               (let ((comp-func f))
+                 (comp-tco-func)
+                 (comp-log-func comp-func 3))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; Type hint removal pass specific code.
+
+;; This must run after all SSA prop not to have the type hint
+;; information overwritten.
+
+(defun comp-remove-type-hints-func ()
+  "Remove type hints from the current function.
+These are substituted with a normal 'set' op."
+  (cl-loop
+   for b being each hash-value of (comp-func-blocks comp-func)
+   do (comp-loop-insn-in-block b
+        (pcase insn
+          (`(set ,l-val (call ,(pred comp-type-hint-p) ,r-val))
+           (setf insn `(set ,l-val ,r-val)))))))
+
+(defun comp-remove-type-hints (_)
+  "Dead code elimination."
+  (maphash (lambda (_ f)
+             (when (>= (comp-func-speed f) 2)
+               (let ((comp-func f))
+                 (comp-remove-type-hints-func)
+                 (comp-log-func comp-func 3))))
+           (comp-ctxt-funcs-h comp-ctxt)))
+
+
+;;; Final pass specific code.
+
+(defun comp-args-to-lambda-list (args)
+  "Return a lambda list for ARGS."
+  (cl-loop
+   with res
+   repeat (comp-args-base-min args)
+   do (push t res)
+   finally
+   (if (comp-args-p args)
+       (cl-loop
+        with n = (- (comp-args-max args) (comp-args-min args))
+        initially (unless (zerop n)
+                    (push '&optional res))
+        repeat n
+        do (push t res))
+     (cl-loop
+      with n = (- (comp-nargs-nonrest args) (comp-nargs-min args))
+      initially (unless (zerop n)
+                  (push '&optional res))
+      repeat n
+      do (push t res)
+      finally (when (comp-nargs-rest args)
+                (push '&rest res)
+                (push 't res))))
+   (cl-return (reverse res))))
+
+(defun comp-compute-function-type (_ func)
+  "Compute type specifier for `comp-func' FUNC.
+Set it into the `type' slot."
+  (when (and (comp-func-l-p func)
+             (comp-mvar-p (comp-func-type func)))
+    (let* ((comp-func (make-comp-func))
+           (res-mvar (apply #'comp-cstr-union
+                            (make-comp-cstr)
+                            (cl-loop
+                             with res = nil
+                             for bb being the hash-value in (comp-func-blocks
+                                                             func)
+                             do (cl-loop
+                                 for insn in (comp-block-insns bb)
+                                 ;; Collect over every exit point the returned
+                                 ;; mvars and union results.
+                                 do (pcase insn
+                                      (`(return ,mvar)
+                                       (push mvar res))))
+                             finally return res)))
+           (type `(function ,(comp-args-to-lambda-list (comp-func-l-args func))
+                            ,(comp-cstr-to-type-spec res-mvar))))
+      (comp-add-const-to-relocs type)
+      ;; Fix it up.
+      (setf (comp-cstr-imm (comp-func-type func)) type))))
+
+(defun comp-finalize-container (cont)
+  "Finalize data container CONT."
+  (setf (comp-data-container-l cont)
+        (cl-loop with h = (comp-data-container-idx cont)
+                 for obj each hash-keys of h
+                 for i from 0
+                 do (puthash obj i h)
+                 ;; Prune byte-code objects coming from lambdas.
+                 ;; These are not anymore necessary as they will be
+                 ;; replaced at load time by native-elisp-subrs.
+                 ;; Note: we leave the objects in the idx hash table
+                 ;; to still be able to retrieve the correct index
+                 ;; from the corresponding m-var.
+                 collect (if (gethash obj
+                                      (comp-ctxt-byte-func-to-func-h 
comp-ctxt))
+                             'lambda-fixup
+                           obj))))
+
+(defun comp-finalize-relocs ()
+  "Finalize data containers for each relocation class.
+Remove immediate duplicates within relocation classes.
+Update all insn accordingly."
+  ;; Symbols imported by C inlined functions.  We do this here because
+  ;; is better to add all objs to the relocation containers before we
+  ;; compacting them.
+  (mapc #'comp-add-const-to-relocs '(nil t consp listp))
+
+  (let* ((d-default (comp-ctxt-d-default comp-ctxt))
+         (d-default-idx (comp-data-container-idx d-default))
+         (d-impure (comp-ctxt-d-impure comp-ctxt))
+         (d-impure-idx (comp-data-container-idx d-impure))
+         (d-ephemeral (comp-ctxt-d-ephemeral comp-ctxt))
+         (d-ephemeral-idx (comp-data-container-idx d-ephemeral)))
+    ;; We never want compiled lambdas ending up in pure space.  A copy must
+    ;; be already present in impure (see `comp-emit-lambda-for-top-level').
+    (cl-loop for obj being each hash-keys of d-default-idx
+             when (gethash obj (comp-ctxt-lambda-fixups-h comp-ctxt))
+               do (cl-assert (gethash obj d-impure-idx))
+                  (remhash obj d-default-idx))
+    ;; Remove entries in d-impure already present in d-default.
+    (cl-loop for obj being each hash-keys of d-impure-idx
+             when (gethash obj d-default-idx)
+               do (remhash obj d-impure-idx))
+    ;; Remove entries in d-ephemeral already present in d-default or
+    ;; d-impure.
+    (cl-loop for obj being each hash-keys of d-ephemeral-idx
+             when (or (gethash obj d-default-idx) (gethash obj d-impure-idx))
+               do (remhash obj d-ephemeral-idx))
+    ;; Fix-up indexes in each relocation class and fill corresponding
+    ;; reloc lists.
+    (mapc #'comp-finalize-container (list d-default d-impure d-ephemeral))
+    ;; Make a vector from the function documentation hash table.
+    (cl-loop with h = (comp-ctxt-function-docs comp-ctxt)
+             with v = (make-vector (hash-table-count h) nil)
+             for idx being each hash-keys of h
+             for doc = (gethash idx h)
+             do (setf (aref v idx) doc)
+             finally
+             do (setf (comp-ctxt-function-docs comp-ctxt) v))
+    ;; And now we conclude with the following: We need to pass to
+    ;; `comp--register-lambda' the index in the impure relocation
+    ;; array to store revived lambdas, but given we know it only now
+    ;; we fix it up as last.
+    (cl-loop for f being each hash-keys of (comp-ctxt-lambda-fixups-h 
comp-ctxt)
+             using (hash-value mvar)
+             with reverse-h = (make-hash-table) ;; Make sure idx is unique.
+             for idx = (gethash f d-impure-idx)
+             do
+             (cl-assert (null (gethash idx reverse-h)))
+             (cl-assert (fixnump idx))
+             (setf (comp-mvar-valset mvar) ()
+                   (comp-mvar-range mvar) (list (cons idx idx)))
+             (puthash idx t reverse-h))))
+
+(defun comp-compile-ctxt-to-file (name)
+  "Compile as native code the current context naming it NAME.
+Prepare every function for final compilation and drive the C back-end."
+  (let ((dir (file-name-directory name)))
+    (comp-finalize-relocs)
+    (maphash (lambda (_ f)
+               (comp-log-func f 1))
+             (comp-ctxt-funcs-h comp-ctxt))
+    (unless (file-exists-p dir)
+      ;; In case it's created in the meanwhile.
+      (ignore-error file-already-exists
+        (make-directory dir t)))
+    (comp--compile-ctxt-to-file name)))
+
+(defun comp-final1 ()
+  (let (compile-result)
+    (comp--init-ctxt)
+    (unwind-protect
+        (setf compile-result
+              (comp-compile-ctxt-to-file (comp-ctxt-output comp-ctxt)))
+      (and (comp--release-ctxt)
+           compile-result))))
+
+(defvar comp-async-compilation nil
+  "Non-nil while executing an asynchronous native compilation.")
+
+(defun comp-final (_)
+  "Final pass driving the C back-end for code emission."
+  (maphash #'comp-compute-function-type (comp-ctxt-funcs-h comp-ctxt))
+  (unless comp-dry-run
+    ;; Always run the C side of the compilation as a sub-process
+    ;; unless during bootstrap or async compilation (bug#45056).  GCC
+    ;; leaks memory but also interfere with the ability of Emacs to
+    ;; detect when a sub-process completes (TODO understand why).
+    (if (or byte-native-for-bootstrap comp-async-compilation)
+       (comp-final1)
+      ;; Call comp-final1 in a child process.
+      (let* ((output (comp-ctxt-output comp-ctxt))
+             (print-escape-newlines t)
+             (print-length nil)
+             (print-level nil)
+             (print-quoted t)
+             (print-gensym t)
+             (print-circle t)
+             (print-escape-multibyte t)
+             (expr `((require 'comp)
+                     (setf comp-verbose ,comp-verbose
+                           comp-libgccjit-reproducer ,comp-libgccjit-reproducer
+                           comp-ctxt ,comp-ctxt
+                           comp-eln-load-path ',comp-eln-load-path
+                           comp-native-driver-options
+                           ',comp-native-driver-options
+                           load-path ',load-path)
+                     ,comp-async-env-modifier-form
+                     (message "Compiling %s..." ',output)
+                     (comp-final1)))
+             (temp-file (make-temp-file
+                        (concat "emacs-int-comp-"
+                                (file-name-base output) "-")
+                        nil ".el")))
+       (with-temp-file temp-file
+          (insert ";; -*-coding: nil; -*-\n")
+          (mapc (lambda (e)
+                  (insert (prin1-to-string e)))
+                expr))
+       (with-temp-buffer
+          (unwind-protect
+              (if (zerop
+                   (call-process (expand-file-name invocation-name
+                                                   invocation-directory)
+                                nil t t "--batch" "-l" temp-file))
+                  (progn
+                    (delete-file temp-file)
+                    output)
+               (signal 'native-compiler-error (buffer-string)))
+            (comp-log-to-buffer (buffer-string))))))))
+
+
+;;; Compiler type hints.
+;; Public entry points to be used by user code to give comp
+;; suggestions about types.  These are used to implement CL style
+;; `cl-the' and hopefully parameter type declaration.
+;; Note: types will propagates.
+;; WARNING: At speed >= 2 type checking is not performed anymore and 
suggestions
+;; are assumed just to be true. Use with extreme caution...
+
+(defun comp-hint-fixnum (x)
+  (declare (gv-setter (lambda (val) `(setf ,x ,val))))
+  x)
+
+(defun comp-hint-cons (x)
+  (declare (gv-setter (lambda (val) `(setf ,x ,val))))
+  x)
+
+
+;; Primitive function advice machinery
+
+(defun comp-eln-load-path-eff ()
+  "Return a list of effective eln load directories.
+Account for `comp-eln-load-path' and `comp-native-version-dir'."
+  (mapcar (lambda (dir)
+            (expand-file-name comp-native-version-dir
+                              (file-name-as-directory
+                               (expand-file-name dir invocation-directory))))
+          comp-eln-load-path))
+
+(defun comp-trampoline-filename (subr-name)
+  "Given SUBR-NAME return the filename containing the trampoline."
+  (concat (comp-c-func-name subr-name "subr--trampoline-" t) ".eln"))
+
+(defun comp-make-lambda-list-from-subr (subr)
+  "Given SUBR return the equivalent lambda-list."
+  (pcase-let ((`(,min . ,max) (subr-arity subr))
+              (lambda-list '()))
+    (cl-loop repeat min
+             do (push (gensym "arg") lambda-list))
+    (if (numberp max)
+        (cl-loop
+         initially (push '&optional lambda-list)
+         repeat (- max min)
+         do (push (gensym "arg") lambda-list))
+      (push '&rest lambda-list)
+      (push (gensym "arg") lambda-list))
+    (reverse lambda-list)))
+
+(defun comp-trampoline-search (subr-name)
+  "Search a trampoline file for SUBR-NAME.
+Return the trampoline if found or nil otherwise."
+  (cl-loop
+   with rel-filename = (comp-trampoline-filename subr-name)
+   for dir in (comp-eln-load-path-eff)
+   for filename = (expand-file-name rel-filename dir)
+   when (file-exists-p filename)
+     do (cl-return (native-elisp-load filename))))
+
+(defun comp-trampoline-compile (subr-name)
+  "Synthesize compile and return a trampoline for SUBR-NAME."
+  (let* ((lambda-list (comp-make-lambda-list-from-subr
+                       (symbol-function subr-name)))
+         ;; The synthesized trampoline must expose the exact same ABI of
+         ;; the primitive we are replacing in the function reloc table.
+         (form `(lambda ,lambda-list
+                  (let ((f #',subr-name))
+                    (,(if (memq '&rest lambda-list) #'apply 'funcall)
+                     f
+                     ,@(cl-loop
+                        for arg in lambda-list
+                        unless (memq arg '(&optional &rest))
+                        collect arg)))))
+         ;; Use speed 0 to maximize compilation speed and not to
+         ;; optimize away funcall calls!
+         (byte-optimize nil)
+         (comp-speed 1)
+         (lexical-binding t))
+    (comp--native-compile
+     form nil
+     (cl-loop
+      for dir in (comp-eln-load-path-eff)
+      for f = (expand-file-name
+               (comp-trampoline-filename subr-name)
+               dir)
+      unless (file-exists-p dir)
+        do (ignore-errors
+             (make-directory dir t)
+             (cl-return f))
+      when (file-writable-p f)
+        do (cl-return f)
+      finally (error "Cannot find suitable directory for output in \
+`comp-eln-load-path'")))))
+
+
+;; Some entry point support code.
+
+;;;###autoload
+(defun comp-clean-up-stale-eln (file)
+  "Given FILE remove all its *.eln files in `comp-eln-load-path'
+sharing the original source filename (including FILE)."
+  (when (string-match (rx "-" (group-n 1 (1+ hex)) "-" (1+ hex) ".eln" eos)
+                      file)
+    (cl-loop
+     with filename-hash = (match-string 1 file)
+     with regexp = (rx-to-string
+                    `(seq "-" ,filename-hash "-" (1+ hex) ".eln" eos))
+     for dir in (comp-eln-load-path-eff)
+     do (cl-loop
+         for f in (when (file-exists-p dir)
+                   (directory-files dir t regexp t))
+         ;; We may not be able to delete the file if we have no write
+         ;; permisison.
+         do (ignore-error file-error
+              (comp-delete-or-replace-file f))))))
+
+(defun comp-delete-or-replace-file (oldfile &optional newfile)
+  "Replace OLDFILE with NEWFILE.
+When NEWFILE is nil just delete OLDFILE.
+Takes the necessary steps when dealing with OLDFILE being a
+shared library that might be currently loaded into a running Emacs
+session."
+  (cond ((eq 'windows-nt system-type)
+         (ignore-errors (delete-file oldfile))
+         (while
+             (condition-case _
+                 (progn
+                   ;; oldfile maybe recreated by another Emacs in
+                   ;; between the following two rename-file calls
+                   (if (file-exists-p oldfile)
+                       (rename-file oldfile (make-temp-file-internal
+                                             (file-name-sans-extension oldfile)
+                                             nil ".eln.old" nil)
+                                    t))
+                   (when newfile
+                     (rename-file newfile oldfile nil))
+                   ;; Keep on trying.
+                   nil)
+               (file-already-exists
+                ;; Done
+                t))))
+        ;; Remove the old eln instead of copying the new one into it
+        ;; to get a new inode and prevent crashes in case the old one
+        ;; is currently loaded.
+        (t (delete-file oldfile)
+           (when newfile
+             (rename-file newfile oldfile)))))
+
+(defvar comp-files-queue ()
+  "List of Elisp files to be compiled.")
+
+(defvar comp-async-compilations (make-hash-table :test #'equal)
+  "Hash table file-name -> async compilation process.")
+
+(defun comp-async-runnings ()
+  "Return the number of async compilations currently running.
+This function has the side effect of cleaning-up finished
+processes from `comp-async-compilations'"
+  (cl-loop
+   for file-name in (cl-loop
+                     for file-name being each hash-key of 
comp-async-compilations
+                     for prc = (gethash file-name comp-async-compilations)
+                     unless (process-live-p prc)
+                       collect file-name)
+   do (remhash file-name comp-async-compilations))
+  (hash-table-count comp-async-compilations))
+
+(declare-function w32-get-nproc "w32.c")
+(defvar comp-num-cpus nil)
+(defun comp-effective-async-max-jobs ()
+  "Compute the effective number of async jobs."
+  (if (zerop comp-async-jobs-number)
+      (or comp-num-cpus
+          (setf comp-num-cpus
+                ;; FIXME: we already have a function to determine
+                ;; the number of processors, see get_native_system_info in 
w32.c.
+                ;; The result needs to be exported to Lisp.
+                (max 1 (/ (cond ((eq 'windows-nt system-type)
+                                 (w32-get-nproc))
+                                ((executable-find "nproc")
+                                 (string-to-number
+                                  (shell-command-to-string "nproc")))
+                                ((eq 'berkeley-unix system-type)
+                                 (string-to-number
+                                  (shell-command-to-string "sysctl -n 
hw.ncpu")))
+                                (t 1))
+                          2))))
+    comp-async-jobs-number))
+
+(defvar comp-last-scanned-async-output nil)
+(make-variable-buffer-local 'comp-last-scanned-async-output)
+(defun comp-accept-and-process-async-output (process)
+  "Accept PROCESS output and check for diagnostic messages."
+  (if comp-async-report-warnings-errors
+      (with-current-buffer (process-buffer process)
+        (save-excursion
+          (accept-process-output process)
+          (goto-char (or comp-last-scanned-async-output (point-min)))
+          (while (re-search-forward "^.*?\\(?:Error\\|Warning\\): .*$"
+                                    nil t)
+            (display-warning 'comp (match-string 0)))
+          (setq comp-last-scanned-async-output (point-max))))
+    (accept-process-output process)))
+
+(defun comp-run-async-workers ()
+  "Start compiling files from `comp-files-queue' asynchronously.
+When compilation is finished, run `comp-async-all-done-hook' and
+display a message."
+  (if (or comp-files-queue
+          (> (comp-async-runnings) 0))
+      (unless (>= (comp-async-runnings) (comp-effective-async-max-jobs))
+        (cl-loop
+         for (source-file . load) = (pop comp-files-queue)
+         while source-file
+         do (cl-assert (string-match-p comp-valid-source-re source-file) nil
+                       "`comp-files-queue' should be \".el\" files: %s"
+                       source-file)
+         when (or comp-always-compile
+                  load ; Always compile when the compilation is
+                       ; commanded for late load.
+                  (file-newer-than-file-p
+                   source-file (comp-el-to-eln-filename source-file)))
+         do (let* ((expr `((require 'comp)
+                           ,(when (boundp 'backtrace-line-length)
+                              `(setf backtrace-line-length 
,backtrace-line-length))
+                           (setf comp-speed ,comp-speed
+                                 comp-debug ,comp-debug
+                                 comp-verbose ,comp-verbose
+                                 comp-libgccjit-reproducer 
,comp-libgccjit-reproducer
+                                 comp-async-compilation t
+                                 comp-eln-load-path ',comp-eln-load-path
+                                 comp-native-driver-options
+                                 ',comp-native-driver-options
+                                 load-path ',load-path
+                                 warning-fill-column most-positive-fixnum)
+                           ,comp-async-env-modifier-form
+                           (message "Compiling %s..." ,source-file)
+                           (comp--native-compile ,source-file ,(and load t))))
+                   (source-file1 source-file) ;; Make the closure works :/
+                   (temp-file (make-temp-file
+                               (concat "emacs-async-comp-"
+                                       (file-name-base source-file) "-")
+                               nil ".el"))
+                   (expr-strings (mapcar #'prin1-to-string expr))
+                   (_ (progn
+                        (with-temp-file temp-file
+                          (mapc #'insert expr-strings))
+                        (comp-log "\n")
+                        (mapc #'comp-log expr-strings)))
+                   (load1 load)
+                   (process (make-process
+                             :name (concat "Compiling: " source-file)
+                             :buffer (get-buffer-create comp-async-buffer-name)
+                             :command (list
+                                       (expand-file-name invocation-name
+                                                         invocation-directory)
+                                       "--batch" "-l" temp-file)
+                             :sentinel
+                             (lambda (process _event)
+                               (run-hook-with-args
+                                'comp-async-cu-done-functions
+                                source-file)
+                               (comp-accept-and-process-async-output process)
+                               (ignore-errors (delete-file temp-file))
+                               (when (and load1
+                                          (zerop (process-exit-status 
process)))
+                                 (native-elisp-load
+                                  (comp-el-to-eln-filename source-file1)
+                                  (eq load1 'late)))
+                               (comp-run-async-workers))
+                             :noquery (not comp-async-query-on-exit))))
+              (puthash source-file process comp-async-compilations))
+         when (>= (comp-async-runnings) (comp-effective-async-max-jobs))
+           do (cl-return)))
+    ;; No files left to compile and all processes finished.
+    (run-hooks 'comp-async-all-done-hook)
+    (with-current-buffer (get-buffer-create comp-async-buffer-name)
+      (save-excursion
+        (goto-char (point-max))
+        (insert "Compilation finished.\n")))
+    ;; `comp-deferred-pending-h' should be empty at this stage.
+    ;; Reset it anyway.
+    (clrhash comp-deferred-pending-h)))
+
+(defun comp--native-compile (function-or-file &optional with-late-load output)
+  "Compile FUNCTION-OR-FILE into native code.
+When WITH-LATE-LOAD is non-nil, mark the compilation unit for late
+load once it finishes compiling.
+This serves as internal implementation of `native-compile' but
+allowing for WITH-LATE-LOAD to be controlled is in use also for
+the deferred compilation mechanism."
+  (comp-ensure-native-compiler)
+  (unless (or (functionp function-or-file)
+              (stringp function-or-file))
+    (signal 'native-compiler-error
+            (list "Not a function symbol or file" function-or-file)))
+  (catch 'no-native-compile
+    (let* ((data function-or-file)
+           (comp-native-compiling t)
+           (byte-native-qualities nil)
+           ;; Have byte compiler signal an error when compilation fails.
+           (byte-compile-debug t)
+           (comp-ctxt (make-comp-ctxt :output output
+                                      :with-late-load with-late-load)))
+      (comp-log "\n\n" 1)
+      (condition-case err
+          (cl-loop
+           with report = nil
+           for t0 = (current-time)
+           for pass in comp-passes
+           unless (memq pass comp-disabled-passes)
+           do
+           (comp-log (format "(%s) Running pass %s:\n"
+                             function-or-file pass)
+                     2)
+           (setf data (funcall pass data))
+           (push (cons pass (float-time (time-since t0))) report)
+           (cl-loop for f in (alist-get pass comp-post-pass-hooks)
+                    do (funcall f data))
+           finally
+           (when comp-log-time-report
+             (comp-log (format "Done compiling %s" data) 0)
+             (cl-loop for (pass . time) in (reverse report)
+                      do (comp-log (format "Pass %s took: %fs." pass time) 
0))))
+        (native-compiler-skip)
+        (t
+         (let ((err-val (cdr err)))
+           ;; If we are doing an async native compilation print the
+           ;; error in the correct format so is parsable and abort.
+           (if (and comp-async-compilation
+                    (not (eq (car err) 'native-compiler-error)))
+               (progn
+                 (message (if err-val
+                              "%s: Error: %s %s"
+                            "%s: Error %s")
+                          function-or-file
+                          (get (car err) 'error-message)
+                          (car-safe err-val))
+                 (kill-emacs -1))
+             ;; Otherwise re-signal it adding the compilation input.
+            (signal (car err) (if (consp err-val)
+                                  (cons function-or-file err-val)
+                                (list function-or-file err-val)))))))
+      (if (stringp function-or-file)
+          data
+        ;; So we return the compiled function.
+        (native-elisp-load data)))))
+
+(defun native-compile-async-skip-p (file load selector)
+  "Return non-nil if FILE's compilation should be skipped.
+
+LOAD and SELECTOR work as described in `native--compile-async'."
+  ;; Make sure we are not already compiling `file' (bug#40838).
+  (or (gethash file comp-async-compilations)
+      (cond
+       ((null selector) nil)
+       ((functionp selector) (not (funcall selector file)))
+       ((stringp selector) (not (string-match-p selector file)))
+       (t (error "SELECTOR must be a function a regexp or nil")))
+      ;; Also exclude files from deferred compilation if
+      ;; any of the regexps in
+      ;; `comp-deferred-compilation-deny-list' matches.
+      (and (eq load 'late)
+           (cl-some (lambda (re)
+                      (string-match-p re file))
+                    comp-deferred-compilation-deny-list))))
+
+(defun native--compile-async (files &optional recursively load selector)
+  "Compile FILES asynchronously.
+FILES is one filename or a list of filenames or directories.
+
+If optional argument RECURSIVELY is non-nil, recurse into
+subdirectories of given directories.
+
+If optional argument LOAD is non-nil, request to load the file
+after compiling.
+
+The optional argument SELECTOR has the following valid values:
+
+nil -- Select all files.
+a string -- A regular expression selecting files with matching names.
+a function -- A function selecting files with matching names.
+
+The variable `comp-async-jobs-number' specifies the number
+of (commands) to run simultaneously.
+
+LOAD can also be the symbol `late'.  This is used internally if
+the byte code has already been loaded when this function is
+called.  It means that we request the special kind of load
+necessary in that situation, called \"late\" loading.
+
+During a \"late\" load, instead of executing all top-level forms
+of the original files, only function definitions are
+loaded (paying attention to have these effective only if the
+bytecode definition was not changed in the meantime)."
+  (comp-ensure-native-compiler)
+  (unless (member load '(nil t late))
+    (error "LOAD must be nil, t or 'late"))
+  (unless (listp files)
+    (setf files (list files)))
+  (let (file-list)
+    (dolist (path files)
+      (cond ((file-directory-p path)
+             (dolist (file (if recursively
+                               (directory-files-recursively
+                                path comp-valid-source-re)
+                             (directory-files path t comp-valid-source-re)))
+               (push file file-list)))
+            ((file-exists-p path) (push path file-list))
+            (t (signal 'native-compiler-error
+                       (list "Path not a file nor directory" path)))))
+    (dolist (file file-list)
+      (if-let ((entry (cl-find file comp-files-queue :key #'car :test 
#'string=)))
+          ;; Most likely the byte-compiler has requested a deferred
+          ;; compilation, so update `comp-files-queue' to reflect that.
+          (unless (or (null load)
+                      (eq load (cdr entry)))
+            (cl-substitute (cons file load) (car entry) comp-files-queue
+                           :key #'car :test #'string=))
+
+        (unless (native-compile-async-skip-p file load selector)
+          (let* ((out-filename (comp-el-to-eln-filename file))
+                 (out-dir (file-name-directory out-filename)))
+            (unless (file-exists-p out-dir)
+              (make-directory out-dir t))
+            (if (file-writable-p out-filename)
+                (setf comp-files-queue
+                      (append comp-files-queue `((,file . ,load))))
+              (display-warning 'comp
+                               (format "No write access for %s skipping."
+                                       out-filename)))))))
+    (when (zerop (comp-async-runnings))
+      (comp-run-async-workers))))
+
+
+;;; Compiler entry points.
+
+;;;###autoload
+(defun comp-lookup-eln (filename)
+  "Given a Lisp source FILENAME return the corresponding .eln file if found.
+Search happens in `comp-eln-load-path'."
+  (cl-loop
+   with eln-filename = (comp-el-to-eln-rel-filename filename)
+   for dir in comp-eln-load-path
+   for f = (expand-file-name eln-filename
+                             (expand-file-name comp-native-version-dir
+                                               (expand-file-name
+                                                dir
+                                                invocation-directory)))
+   when (file-exists-p f)
+     do (cl-return f)))
+
+;;;###autoload
+(defun native-compile (function-or-file &optional output)
+  "Compile FUNCTION-OR-FILE into native code.
+This is the synchronous entry-point for the Emacs Lisp native
+compiler.
+FUNCTION-OR-FILE is a function symbol, a form, or the filename of
+an Emacs Lisp source file.
+If OUTPUT is non-nil, use it as the filename for the compiled
+object.
+If FUNCTION-OR-FILE is a filename, return the filename of the
+compiled object.  If FUNCTION-OR-FILE is a function symbol or a
+form, return the compiled function."
+  (comp--native-compile function-or-file nil output))
+
+;;;###autoload
+(defun batch-native-compile ()
+  "Perform native compilation on remaining command-line arguments.
+Use this from the command line, with ‘-batch’;
+it won’t work in an interactive Emacs.
+Native compilation equivalent to `batch-byte-compile'."
+  (comp-ensure-native-compiler)
+  (cl-loop for file in command-line-args-left
+           if (or (null byte-native-for-bootstrap)
+                  (cl-notany (lambda (re) (string-match re file))
+                             comp-bootstrap-deny-list))
+           do (comp--native-compile file)
+           else
+           do (byte-compile-file file)))
+
+;;;###autoload
+(defun batch-byte-native-compile-for-bootstrap ()
+  "Like `batch-native-compile', but used for bootstrap.
+Generate .elc files in addition to the .eln files.
+Force the produced .eln to be outputted in the eln system
+directory (the last entry in `comp-eln-load-path').
+If the environment variable 'NATIVE_DISABLED' is set, only byte
+compile."
+  (comp-ensure-native-compiler)
+  (if (equal (getenv "NATIVE_DISABLED") "1")
+      (batch-byte-compile)
+    (cl-assert (length= command-line-args-left 1))
+    (let ((byte-native-for-bootstrap t)
+          (byte-to-native-output-file nil))
+      (batch-native-compile)
+      (pcase byte-to-native-output-file
+        (`(,tempfile . ,target-file)
+         (rename-file tempfile target-file t))))))
+
+;;;###autoload
+(defun native-compile-async (files &optional recursively load selector)
+  "Compile FILES asynchronously.
+FILES is one file or a list of filenames or directories.
+
+If optional argument RECURSIVELY is non-nil, recurse into
+subdirectories of given directories.
+
+If optional argument LOAD is non-nil, request to load the file
+after compiling.
+
+The optional argument SELECTOR has the following valid values:
+
+nil -- Select all files.
+a string -- A regular expression selecting files with matching names.
+a function -- A function selecting files with matching names.
+
+The variable `comp-async-jobs-number' specifies the number
+of (commands) to run simultaneously."
+  ;; Normalize: we only want to pass t or nil, never e.g. `late'.
+  (let ((load (not (not load))))
+    (native--compile-async files recursively load selector)))
+
+(provide 'comp)
+
+;;; comp.el ends here
diff --git a/lisp/emacs-lisp/disass.el b/lisp/emacs-lisp/disass.el
index 0d28909..6ac76f1 100644
--- a/lisp/emacs-lisp/disass.el
+++ b/lisp/emacs-lisp/disass.el
@@ -43,6 +43,8 @@
 ;; Since we don't use byte-decompile-lapcode, let's try not loading byte-opt.
 (require 'byte-compile "bytecomp")
 
+(declare-function comp-c-func-name "comp.el")
+
 (defvar disassemble-column-1-indent 8 "*")
 (defvar disassemble-column-2-indent 10 "*")
 
@@ -73,8 +75,9 @@ redefine OBJECT if it is a symbol."
       (disassemble-internal object indent nil)))
   nil)
 
-
-(defun disassemble-internal (obj indent interactive-p)
+(declare-function native-comp-unit-file "data.c")
+(declare-function subr-native-comp-unit "data.c")
+(cl-defun disassemble-internal (obj indent interactive-p)
   (let ((macro 'nil)
        (name (when (symbolp obj)
                 (prog1 obj
@@ -82,7 +85,27 @@ redefine OBJECT if it is a symbol."
        args)
     (setq obj (autoload-do-load obj name))
     (if (subrp obj)
-       (error "Can't disassemble #<subr %s>" name))
+        (if (and (fboundp 'subr-native-elisp-p)
+                 (subr-native-elisp-p obj))
+            (progn
+              (require 'comp)
+              (call-process "objdump" nil (current-buffer) t "-S"
+                            (native-comp-unit-file (subr-native-comp-unit 
obj)))
+              (goto-char (point-min))
+              (re-search-forward (concat "^.*"
+                                         (regexp-quote
+                                          (concat "<"
+                                                  (comp-c-func-name
+                                                   (subr-name obj) "F" t)
+                                                  ">:"))))
+              (beginning-of-line)
+              (delete-region (point-min) (point))
+              (when (re-search-forward "^.*<.*>:" nil t 2)
+                (delete-region (match-beginning 0) (point-max)))
+              (asm-mode)
+              (setq buffer-read-only t)
+              (cl-return-from disassemble-internal))
+         (error "Can't disassemble #<subr %s>" name)))
     (if (eq (car-safe obj) 'macro)     ;Handle macros.
        (setq macro t
              obj (cdr obj)))
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index c399a68..58876a4 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -178,13 +178,18 @@ See the functions `find-function' and `find-variable'."
             (setq name rel))))
     (unless (equal name library) name)))
 
+(defvar comp-eln-to-el-h)
+
 (defun find-library-name (library)
   "Return the absolute file name of the Emacs Lisp source of LIBRARY.
 LIBRARY should be a string (the name of the library)."
   ;; If the library is byte-compiled, try to find a source library by
   ;; the same name.
-  (when (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
+  (cond
+   ((string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
     (setq library (replace-match "" t t library)))
+   ((string-match "\\.eln\\'" library)
+    (setq library (gethash (file-name-nondirectory library) 
comp-eln-to-el-h))))
   (or
    (locate-file library
                 (or find-function-source-path load-path)
@@ -203,7 +208,7 @@ LIBRARY should be a string (the name of the library)."
                        (or find-function-source-path load-path)
                        load-file-rep-suffixes)))))
    (find-library--from-load-history library)
-   (error "Can't find library %s" library)))
+   (signal 'file-error (list "Can't find library" library))))
 
 (defun find-library--from-load-history (library)
   ;; In `load-history', the file may be ".elc", ".el", ".el.gz", and
@@ -491,7 +496,7 @@ message about the whole chain of aliases."
     (cons function
           (cond
            ((autoloadp def) (nth 1 def))
-           ((subrp def)
+           ((subr-primitive-p def)
             (if lisp-only
                 (error "%s is a built-in function" function))
             (help-C-file-name def 'subr))
diff --git a/lisp/emacs-lisp/map-ynp.el b/lisp/emacs-lisp/map-ynp.el
index 86a0c76..0522b31 100644
--- a/lisp/emacs-lisp/map-ynp.el
+++ b/lisp/emacs-lisp/map-ynp.el
@@ -38,46 +38,62 @@
 
 (defun map-y-or-n-p (prompter actor list &optional help action-alist
                              no-cursor-in-echo-area)
-  "Ask a series of boolean questions.
-Takes args PROMPTER ACTOR LIST, and optional args HELP and ACTION-ALIST.
+  "Ask a boolean question per PROMPTER for each object in LIST, then call 
ACTOR.
 
 LIST is a list of objects, or a function of no arguments to return the next
-object or nil.
-
-If PROMPTER is a string, the prompt is \(format PROMPTER OBJECT).  If not
-a string, PROMPTER is a function of one arg (an object from LIST), which
-returns a string to be used as the prompt for that object.  If the return
-value is not a string, it may be nil to ignore the object or non-nil to act
-on the object without asking the user.
-
-ACTOR is a function of one arg (an object from LIST),
-which gets called with each object that the user answers `yes' for.
-
-If HELP is given, it is a list (OBJECT OBJECTS ACTION),
-where OBJECT is a string giving the singular noun for an elt of LIST;
-OBJECTS is the plural noun for elts of LIST, and ACTION is a transitive
-verb describing ACTOR.  The default is \(\"object\" \"objects\" \"act on\").
-
-At the prompts, the user may enter y, Y, or SPC to act on that object;
-n, N, or DEL to skip that object; ! to act on all following objects;
-ESC or q to exit (skip all following objects); . (period) to act on the
-current object and then exit; or \\[help-command] to get help.
-
-If ACTION-ALIST is given, it is an alist (KEY FUNCTION HELP) of extra keys
-that will be accepted.  KEY is a character; FUNCTION is a function of one
-arg (an object from LIST); HELP is a string.  When the user hits KEY,
-FUNCTION is called.  If it returns non-nil, the object is considered
-\"acted upon\", and the next object from LIST is processed.  If it returns
-nil, the prompt is repeated for the same object.
-
-Final optional argument NO-CURSOR-IN-ECHO-AREA non-nil says not to set
-`cursor-in-echo-area' while prompting.
+object; when it returns nil, the list of objects is considered exhausted.
+
+If PROMPTER is a string, it should be a format string to be used to format
+the question as \(format PROMPTER OBJECT).
+If PROMPTER is not a string, it should be a function of one argument, an
+object from LIST, which returns a string to be used as the question for
+that object.  If the function's return value is not a string, it may be
+nil to ignore the object, or non-nil to act on the object with ACTOR
+without asking the user.
+
+ACTOR is a function of one argument, an object from LIST,
+which gets called with each object for which the user answers `yes'
+to the question presented by PROMPTER.
+
+The user's answers to the questions may be one of the following:
+
+ - y, Y, or SPC to act on that object;
+ - n, N, or DEL to skip that object;
+ - ! to act on all following objects;
+ - ESC or q to exit (skip all following objects);
+ - . (period) to act on the current object and then exit; or
+ - \\[help-command] to get help.
+
+HELP provides information for displaying help when the user
+types \\[help-command].  If HELP is given, it should be a list of
+the form (OBJECT OBJECTS ACTION), where OBJECT is a string giving
+the singular noun describing an element of LIST; OBJECTS is the
+plural noun describing several elements of LIST, and ACTION is a
+transitive verb describing action by ACTOR on one or more elements
+of LIST.  If HELP is omitted or nil, it defaults
+to \(\"object\" \"objects\" \"act on\").
+
+If ACTION-ALIST is given, it is an alist specifying additional keys
+that will be accepted as an answer to the questions.  Each element
+of the alist has the form (KEY FUNCTION HELP), where KEY is a character;
+FUNCTION is a function of one argument (an object from LIST); and HELP
+is a string.  When the user presses KEY, FUNCTION is called; if it
+returns non-nil, the object is considered to have been \"acted upon\",
+and `map-y-or-n-p' proceeeds to the next object from LIST.  If
+FUNCTION returns nil, the prompt is re-issued for the same object: this
+comes in handy if FUNCTION produces some display that will allow the
+user to make an intelligent decision whether the object in question
+should be acted upon.  If the user types \\[help-command], the string
+given by HELP is used to describe the effect of KEY.
+
+Optional argument NO-CURSOR-IN-ECHO-AREA, if non-nil, means not to set
+`cursor-in-echo-area' while prompting with the questions.
 
 This function uses `query-replace-map' to define the standard responses,
-but not all of the responses which `query-replace' understands
-are meaningful here.
+but only some of the responses which `query-replace' understands
+are meaningful here, as described above.
 
-Returns the number of actions taken."
+The function's value is the number of actions taken."
   (let* ((actions 0)
          (msg (current-message))
         user-keys mouse-event map prompt char elt def
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index afdd372..f974056 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -316,8 +316,26 @@ is also interactive.  There are 3 cases:
   `(advice--add-function ,where (gv-ref ,(advice--normalize-place place))
                          ,function ,props))
 
+(declare-function comp-subr-trampoline-install "comp")
+
 ;;;###autoload
 (defun advice--add-function (where ref function props)
+  (when (and (featurep 'nativecomp)
+             (subr-primitive-p (gv-deref ref)))
+    (let ((subr-name (intern (subr-name (gv-deref ref)))))
+      ;; Requiring the native compiler to advice `macroexpand' cause a
+      ;; circular dependency in eager macro expansion.
+      ;; uniquify is advising `rename-buffer' while being loaded in
+      ;; loadup.el.  This would require the whole native compiler
+      ;; machinery but we don't want to include it in the dump.
+      ;; Because these two functions are already handled in
+      ;; `comp-never-optimize-functions' we hack the problem this way
+      ;; for now :/
+      (unless (memq subr-name '(macroexpand rename-buffer))
+        ;; Must require explicitly as during bootstrap we have no
+        ;; autoloads.
+        (require 'comp)
+        (comp-subr-trampoline-install subr-name))))
   (let* ((name (cdr (assq 'name props)))
          (a (advice--member-p (or name function) (if name t) (gv-deref ref))))
     (when a
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 64d7d56..5035850 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -396,6 +396,12 @@ a sane initial value."
   :version "25.1"
   :type '(repeat symbol))
 
+(defcustom package-native-compile nil
+  "Non-nil means to native compile packages on installation."
+  :type '(boolean)
+  :risky t
+  :version "28.1")
+
 (defcustom package-menu-async t
   "If non-nil, package-menu will use async operations when possible.
 Currently, only the refreshing of archive contents supports
@@ -829,8 +835,6 @@ correspond to previously loaded files (those returned by
       ;; Don't return nil.
       t)))
 
-(declare-function find-library-name "find-func" (library))
-
 (defun package--files-load-history ()
   (delq nil
         (mapcar (lambda (x)
@@ -840,20 +844,22 @@ correspond to previously loaded files (those returned by
                 load-history)))
 
 (defun package--list-of-conflicts (dir history)
-   (delq
-    nil
-    (mapcar
-     (lambda (x) (let* ((file (file-relative-name x dir))
-                        ;; Previously loaded file, if any.
-                        (previous
-                         (ignore-errors
-                           (file-name-sans-extension
-                            (file-truename (find-library-name file)))))
-                        (pos (when previous (member previous history))))
-                   ;; Return (RELATIVE-FILENAME . HISTORY-POSITION)
-                   (when pos
-                     (cons (file-name-sans-extension file) (length pos)))))
-     (directory-files-recursively dir "\\`[^\\.].*\\.el\\'"))))
+  (require 'find-func)
+  (declare-function find-library-name "find-func" (library))
+  (delq
+   nil
+   (mapcar
+    (lambda (x) (let* ((file (file-relative-name x dir))
+                  ;; Previously loaded file, if any.
+                  (previous
+                   (ignore-error file-error ;"Can't find library"
+                     (file-name-sans-extension
+                      (file-truename (find-library-name file)))))
+                  (pos (when previous (member previous history))))
+             ;; Return (RELATIVE-FILENAME . HISTORY-POSITION)
+             (when pos
+               (cons (file-name-sans-extension file) (length pos)))))
+    (directory-files-recursively dir "\\`[^\\.].*\\.el\\'"))))
 
 (defun package--list-loaded-files (dir)
   "Recursively list all files in DIR which correspond to loaded features.
@@ -985,6 +991,8 @@ untar into a directory named DIR; otherwise, signal an 
error."
         ;; E.g. for multi-package installs, we should first install all 
packages
         ;; and then compile them.
         (package--compile new-desc)
+        (when package-native-compile
+          (package--native-compile-async new-desc))
         ;; After compilation, load again any files loaded by
         ;; `activate-1', so that we use the byte-compiled definitions.
         (package--load-files-for-activation new-desc :reload)))
@@ -1069,6 +1077,15 @@ This assumes that `pkg-desc' has already been activated 
with
         (load-path load-path))
     (byte-recompile-directory (package-desc-dir pkg-desc) 0 t)))
 
+(defun package--native-compile-async (pkg-desc)
+  "Native compile installed package PKG-DESC asynchronously.
+This assumes that `pkg-desc' has already been activated with
+`package-activate-1'."
+  (when (and (featurep 'nativecomp)
+             (native-comp-available-p))
+    (let ((warning-minimum-level :error))
+      (native-compile-async (package-desc-dir pkg-desc) t))))
+
 ;;;; Inferring package from current buffer
 (defun package-read-from-string (str)
   "Read a Lisp expression from STR.
@@ -2243,6 +2260,17 @@ confirmation to install packages."
   (equal (cadr (assq (package-desc-name pkg) package-alist))
          pkg))
 
+(declare-function comp-el-to-eln-filename "comp.c")
+(defun package--delete-directory (dir)
+  "Delete DIR recursively.
+Clean-up the corresponding .eln files if Emacs is native
+compiled."
+  (when (featurep 'nativecomp)
+    (cl-loop
+     for file in (directory-files-recursively dir ".el\\'")
+     do (comp-clean-up-stale-eln (comp-el-to-eln-filename file))))
+  (delete-directory dir t))
+
 (defun package-delete (pkg-desc &optional force nosave)
   "Delete package PKG-DESC.
 
@@ -2295,7 +2323,7 @@ If NOSAVE is non-nil, the package is not removed from
                   (package-desc-name pkg-used-elsewhere-by)))
           (t
            (add-hook 'post-command-hook #'package-menu--post-refresh)
-           (delete-directory dir t)
+           (package--delete-directory dir)
            ;; Remove NAME-VERSION.signed and NAME-readme.txt files.
            ;;
            ;; NAME-readme.txt files are no longer created, but they
@@ -2696,9 +2724,9 @@ PROPERTIES are passed to `insert-text-button', for which 
this
 function is a convenience wrapper used by `describe-package-1'."
   (let ((button-text (if (display-graphic-p) text (concat "[" text "]")))
         (button-face (if (display-graphic-p)
-                         '(:box (:line-width 2 :color "dark grey")
-                                :background "light grey"
-                                :foreground "black")
+                         (progn
+                           (require 'cus-edit) ; for the custom-button face
+                           'custom-button)
                        'link)))
     (apply #'insert-text-button button-text 'face button-face 'follow-link t
            properties)))
@@ -4118,7 +4146,8 @@ activations need to be changed, such as when 
`package-load-list' is modified."
                 (let ((load-suffixes '(".el" ".elc")))
                   (locate-library (package--autoloads-file-name pkg))))
                (pfile (prin1-to-string file)))
-          (insert "(let ((load-file-name " pfile "))\n")
+          (insert "(let ((load-true-file-name " pfile ")\
+(load-file-name " pfile "))\n")
           (insert-file-contents file)
           ;; Fixup the special #$ reader form and throw away comments.
           (while (re-search-forward "#\\$\\|^;\\(.*\n\\)" nil 'move)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 43661a2..6717ee3 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -9,6 +9,7 @@
 ;;               Andreas Fuchs (afs@void.at)
 ;;               Gergely Nagy (algernon@midgard.debian.net)
 ;;               David Edmondson (dme@dme.org)
+;;               Michael Olson (mwolson@gnu.org)
 ;;               Kelvin White (kwhite@gnu.org)
 ;; Maintainer: Amin Bandali <bandali@gnu.org>
 ;; Keywords: IRC, chat, client, Internet
diff --git a/lisp/faces.el b/lisp/faces.el
index da848c5..fdb47f9 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2245,7 +2245,8 @@ If you set `term-file-prefix' to nil, this function does 
nothing."
                           (let ((file (locate-library (concat term-file-prefix 
type))))
                             (and file
                                  (or (assoc file load-history)
-                                     (load file t t)))))
+                                     (load (file-name-sans-extension file)
+                                            t t)))))
                       type)
        ;; Next, try to find a matching initialization function, and call it.
        (tty-find-type #'(lambda (type)
diff --git a/lisp/files.el b/lisp/files.el
index ee16abf..8e8fbac 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -908,6 +908,8 @@ See `file-symlink-p' to distinguish symlinks."
                       (read-file-name "Load file: " nil nil 'lambda))))
   (load (expand-file-name file) nil nil t))
 
+(defvar comp-eln-to-el-h)
+
 (defun locate-file (filename path &optional suffixes predicate)
   "Search for FILENAME through PATH.
 If found, return the absolute file name of FILENAME; otherwise
@@ -934,7 +936,10 @@ one or more of those symbols."
          (logior (if (memq 'executable predicate) 1 0)
                  (if (memq 'writable predicate) 2 0)
                  (if (memq 'readable predicate) 4 0))))
-  (locate-file-internal filename path suffixes predicate))
+  (let ((file (locate-file-internal filename path suffixes predicate)))
+    (if (and file (string-match "\\.eln\\'" file))
+        (gethash (file-name-nondirectory file) comp-eln-to-el-h)
+      file)))
 
 (defun locate-file-completion-table (dirs suffixes string pred action)
   "Do completion for file names passed to `locate-file'."
diff --git a/lisp/frame.el b/lisp/frame.el
index e206313..bb5da0d 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -615,15 +615,6 @@ there (in decreasing order of priority)."
              (face-set-after-frame-default frame-initial-frame)
              (setq newparms (delq new-bg newparms)))
 
-           (when (numberp (car frame-size-history))
-             (setq frame-size-history
-                   (cons (1- (car frame-size-history))
-                         (cons
-                          (list frame-initial-frame
-                                "FRAME-NOTICE-USER"
-                                nil newparms)
-                          (cdr frame-size-history)))))
-
            (modify-frame-parameters frame-initial-frame newparms)))))
 
     ;; Restore the original buffer.
@@ -926,12 +917,6 @@ the new frame according to its own rules."
         (let ((val (frame-parameter oldframe param)))
           (when val (set-frame-parameter frame param val)))))
 
-    (when (numberp (car frame-size-history))
-      (setq frame-size-history
-           (cons (1- (car frame-size-history))
-                 (cons (list frame "MAKE-FRAME")
-                       (cdr frame-size-history)))))
-
     ;; We can run `window-configuration-change-hook' for this frame now.
     (frame-after-make-frame frame t)
     (run-hook-with-args 'after-make-frame-functions frame)
@@ -1698,26 +1683,104 @@ and width values are in pixels.
 
 (defun frame--size-history (&optional frame)
   "Print history of resize operations for FRAME.
-Print prettified version of `frame-size-history' into a buffer
-called *frame-size-history*.  Optional argument FRAME denotes the
-frame whose history will be printed.  FRAME defaults to the
-selected frame."
+This function dumps a prettified version of `frame-size-history'
+into a buffer called *frame-size-history*.  The optional argument
+FRAME denotes the frame whose history will be dumped; it defaults
+to the selected frame.
+
+Storing information about resize operations is off by default.
+If you set the variable `frame-size-history' like this
+
+(setq frame-size-history '(100))
+
+then Emacs will save information about the next 100 significant
+operations affecting any frame's size in that variable.  This
+function prints the entries for FRAME stored in that variable in
+a more legible way.
+
+All lines start with an indication of the requested action.  An
+entry like `menu-bar-lines' or `scroll-bar-width' indicates that
+a change of the corresponding frame parameter or Lisp variable
+was requested.  An entry like gui_figure_window_size indicates
+that that C function was executed, an entry like ConfigureNotify
+indicates that that event was received.
+
+In long entries, a number in parentheses displays the INHIBIT
+parameter passed to the C function adjust_frame_size.  Such
+entries may also display changes of frame rectangles in a form
+like R=n1xn2~>n3xn4 where R denotes the rectangle type (TS for
+text, NS for native and IS for inner frame rectangle sizes, all
+in pixels, TC for text rectangle sizes in frame columns and
+lines), n1 and n2 denote the old width and height and n3 and n4
+the new width and height in the according units.  MS stands for
+the minimum inner frame size in pixels, IH and IV, if present,
+indicate that resizing horizontally and/or vertically was
+inhibited (either by `frame-inhibit-implied-resize' or because of
+the frame's fullscreen state).
+
+Shorter entries represent C functions that process width and
+height changes of the native rectangle where PS stands for the
+frame's present pixel width and height, XS for a requested pixel
+width and height and DS for some earlier requested but so far
+delayed pixel width and height.
+
+Very short entries represent calls of C functions that do not
+directly ask for size changes but may indirectly affect the size
+of frames like calls to map a frame or change its visibility."
   (let ((history (reverse frame-size-history))
-       entry)
+       entry item)
     (setq frame (window-normalize-frame frame))
     (with-current-buffer (get-buffer-create "*frame-size-history*")
       (erase-buffer)
       (insert (format "Frame size history of %s\n" frame))
       (while (consp (setq entry (pop history)))
-       (when (eq (car entry) frame)
-          (pop entry)
-          (insert (format "%s" (pop entry)))
-          (move-to-column 24 t)
-          (while entry
-            (insert (format " %s" (pop entry))))
-          (insert "\n")))
-      (unless frame-size-history
-        (insert "Frame size history is nil.\n")))))
+        (setq item (car entry))
+       (cond
+         ((not (consp item))
+          ;; An item added quickly for debugging purposes.
+          (insert (format "%s\n" entry)))
+         ((and (eq (nth 0 item) frame) (= (nth 1 item) 1))
+          ;; Length 1 is a "plain event".
+          (insert (format "%s\n" (nth 2 item))))
+         ((and (eq (nth 0 item) frame) (= (nth 1 item) 2))
+          ;; Length 2 is an "extra" item.
+          (insert (format "%s" (nth 2 item)))
+          (setq item (nth 0 (cdr entry)))
+          (insert (format ", PS=%sx%s" (nth 0 item) (nth 1 item)))
+          (when (or (>= (nth 2 item) 0) (>= (nth 3 item) 0))
+            (insert (format ", XS=%sx%s" (nth 2 item) (nth 3 item))))
+          (setq item (nth 1 (cdr entry)))
+          (when (or (>= (nth 0 item) 0) (>= (nth 1 item) 0))
+            (insert (format ", DS=%sx%s" (nth 0 item) (nth 1 item))))
+          (insert "\n"))
+         ((and (eq (nth 0 item) frame) (= (nth 1 item) 5))
+          ;; Length 5 is an `adjust-frame-size' item.
+          (insert (format "%s (%s)" (nth 3 item) (nth 2 item)))
+          (setq item (nth 0 (cdr entry)))
+          (unless (and (= (nth 0 item) (nth 2 item))
+                       (= (nth 1 item) (nth 3 item)))
+            (insert (format ", TS=%sx%s~>%sx%s"
+                            (nth 0 item) (nth 1 item) (nth 2 item) (nth 3 
item))))
+          (setq item (nth 1 (cdr entry)))
+          (unless (and (= (nth 0 item) (nth 2 item))
+                       (= (nth 1 item) (nth 3 item)))
+            (insert (format ", TC=%sx%s~>%sx%s"
+                            (nth 0 item) (nth 1 item) (nth 2 item) (nth 3 
item))))
+          (setq item (nth 2 (cdr entry)))
+          (unless (and (= (nth 0 item) (nth 2 item))
+                       (= (nth 1 item) (nth 3 item)))
+            (insert (format ", NS=%sx%s~>%sx%s"
+                            (nth 0 item) (nth 1 item) (nth 2 item) (nth 3 
item))))
+          (setq item (nth 3 (cdr entry)))
+          (unless (and (= (nth 0 item) (nth 2 item))
+                       (= (nth 1 item) (nth 3 item)))
+            (insert (format ", IS=%sx%s~>%sx%s"
+                            (nth 0 item) (nth 1 item) (nth 2 item) (nth 3 
item))))
+          (setq item (nth 4 (cdr entry)))
+          (insert (format ", MS=%sx%s" (nth 0 item) (nth 1 item)))
+          (when (nth 2 item) (insert " IH"))
+          (when (nth 3 item) (insert " IV"))
+          (insert "\n")))))))
 
 (declare-function x-frame-edges "xfns.c" (&optional frame type))
 (declare-function w32-frame-edges "w32fns.c" (&optional frame type))
diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el
index f1181d4..bac987e 100644
--- a/lisp/gnus/gnus-msg.el
+++ b/lisp/gnus/gnus-msg.el
@@ -415,11 +415,12 @@ only affect the Gcc copy, but not the original message."
                             gnus-article-reply)))
           (,oarticle gnus-article-reply)
           (,yanked gnus-article-yanked-articles)
-          (,group (when gnus-article-reply
-                    (or (nnselect-article-group
-                         (or (car-safe gnus-article-reply)
-                             gnus-article-reply))
-                        gnus-newsgroup-name)))
+           (,group (if gnus-article-reply
+                      (or (nnselect-article-group
+                           (or (car-safe gnus-article-reply)
+                               gnus-article-reply))
+                           gnus-newsgroup-name)
+                     gnus-newsgroup-name))
           (message-header-setup-hook
            (copy-sequence message-header-setup-hook))
           (mbl mml-buffer-list)
diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el
index 11b6f7d..0468d72 100644
--- a/lisp/gnus/gnus-registry.el
+++ b/lisp/gnus/gnus-registry.el
@@ -319,9 +319,12 @@ Encode names if ENCODE is non-nil, otherwise decode."
       (setf (oref db tracked)
             (append gnus-registry-track-extra
                     '(mark group keyword)))
-      (when (not (equal old (oref db tracked)))
+      (when (not (seq-set-equal-p old (oref db tracked)))
         (gnus-message 9 "Reindexing the Gnus registry (tracked change)")
-        (registry-reindex db))
+       (let ((message-log-max (if (< gnus-verbose 9)
+                                  nil
+                                message-log-max)))
+          (registry-reindex db)))
       (gnus-registry--munge-group-names db)))
   db)
 
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 2f2b206..7de1cd1 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -4156,8 +4156,9 @@ prompt the user for the name of an NNTP server to use."
   ;; file.
   (unless (string-match "^Gnus" gnus-version)
     (load "gnus-load" nil t))
-  (unless (byte-code-function-p (symbol-function 'gnus))
-    (message "You should byte-compile Gnus")
+  (unless (or (byte-code-function-p (symbol-function 'gnus))
+             (subr-native-elisp-p (symbol-function 'gnus)))
+    (message "You should compile Gnus")
     (sit-for 2))
   (let ((gnus-action-message-log (list nil)))
     (gnus-1 arg dont-connect child)
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index c27cdb5..e20a1a5 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -805,6 +805,8 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED 
REAL-DEF)."
                 ;; aliases before functions.
                 (aliased
                  (format-message "an alias for `%s'" real-def))
+                 ((subr-native-elisp-p def)
+                  (concat beg "native compiled Lisp function"))
                 ((subrp def)
                  (concat beg (if (eq 'unevalled (cdr (subr-arity def)))
                                  "special form"
diff --git a/lisp/help-macro.el b/lisp/help-macro.el
index 96edeaf..7fe1fb6 100644
--- a/lisp/help-macro.el
+++ b/lisp/help-macro.el
@@ -59,12 +59,6 @@
 ;;->  (define-key c-mp "\C-h" 'help-for-empire-redistribute-map)
 ;;->  (define-key c-mp help-character 'help-for-empire-redistribute-map)
 
-;;; Change Log:
-;;
-;; 22-Jan-1991         Lynn Slater x2048
-;;    Last Modified: Mon Oct  1 11:43:52 1990 #3 (Lynn Slater)
-;;    documented better
-
 ;;; Code:
 
 (require 'backquote)
@@ -148,18 +142,23 @@ and then returns."
                    (setq new-minor-mode-map-alist minor-mode-map-alist))
                  (goto-char (point-min))
                  (while (or (memq char (append help-event-list
-                                               (cons help-char '(?? ?\C-v ?\s 
?\177 delete backspace vertical-scroll-bar ?\M-v))))
+                                               (cons help-char '( ?? ?\C-v ?\s 
?\177 deletechar backspace vertical-scroll-bar ?\M-v
+                                                                  next prior 
up down))))
                             (eq (car-safe char) 'switch-frame)
                             (equal key "\M-v"))
                    (condition-case nil
                        (cond
                         ((eq (car-safe char) 'switch-frame)
                          (handle-switch-frame char))
-                        ((memq char '(?\C-v ?\s))
+                        ((memq char '(?\C-v ?\s next))
                          (scroll-up))
-                        ((or (memq char '(?\177 ?\M-v delete backspace))
+                        ((or (memq char '(?\177 ?\M-v deletechar backspace 
prior))
                              (equal key "\M-v"))
-                         (scroll-down)))
+                         (scroll-down))
+                        ((memq char '(down))
+                         (scroll-up 1))
+                        ((memq char '(up))
+                         (scroll-down 1)))
                      (error nil))
                    (let ((cursor-in-echo-area t)
                          (overriding-local-map local-map))
diff --git a/lisp/help.el b/lisp/help.el
index 4dcb235..63f9974 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -239,7 +239,7 @@ Do not call this in the scope of `with-help-window'."
    (help--key-description-fontified "\C-s")
    " to search, or \\<help-map>\\[help-quit] to exit.)"
    (help--for-help-make-sections
-    '(("Commands, Keys and Functions"
+    `(("Commands, Keys and Functions"
        ("describe-mode"
         "Show help for current major and minor modes and their commands")
        ("describe-bindings" "Show all key bindings")
@@ -273,7 +273,8 @@ Do not call this in the scope of `with-help-window'."
        ("help-with-tutorial" "Start the Emacs tutorial")
        ("view-echo-area-messages"
         "Show recent messages (from echo area)")
-       ("view-lossage" "Show last 300 input keystrokes (lossage)")
+       ("view-lossage" ,(format "Show last %d input keystrokes (lossage)"
+                                (lossage-size)))
        ("display-local-help" "Show local help at point"))
       ("Miscellaneous"
        ("about-emacs" "About Emacs")
@@ -298,7 +299,8 @@ Do not call this in the scope of `with-help-window'."
         "Describe language environment")
        ("describe-syntax" "Show current syntax table")
        ("view-hello-file"
-        "Display the HELLO file illustrating various scripts")))))
+        "Display the HELLO file illustrating various scripts"))))
+   "\n")
   help-map
   help-for-help-buffer-name)
 
@@ -1868,6 +1870,8 @@ ARGLIST can also be t or a string of the form \"(FUN ARG1 
ARG2 ...)\"."
                   (error "Unrecognized usage format"))
              (help--make-usage-docstring 'fn arglist)))))
 
+(declare-function subr-native-lambda-list "data.c")
+
 (defun help-function-arglist (def &optional preserve-names)
   "Return a formal argument list for the function DEF.
 If PRESERVE-NAMES is non-nil, return a formal arglist that uses
@@ -1883,6 +1887,10 @@ the same names as used in the original source code, when 
possible."
    ((and (byte-code-function-p def) (listp (aref def 0))) (aref def 0))
    ((eq (car-safe def) 'lambda) (nth 1 def))
    ((eq (car-safe def) 'closure) (nth 2 def))
+   ((and (featurep 'nativecomp)
+         (subrp def)
+         (listp (subr-native-lambda-list def)))
+    (subr-native-lambda-list def))
    ((or (and (byte-code-function-p def) (integerp (aref def 0)))
         (subrp def) (module-function-p def))
     (or (when preserve-names
diff --git a/lisp/international/mule.el b/lisp/international/mule.el
index a6fccff..9cd38af 100644
--- a/lisp/international/mule.el
+++ b/lisp/international/mule.el
@@ -317,8 +317,9 @@ Return t if file exists."
       (when purify-flag
        (push (purecopy file) preloaded-file-list))
       (unwind-protect
-         (let ((load-file-name fullname)
-               (set-auto-coding-for-load t)
+         (let ((load-true-file-name fullname)
+                (load-file-name fullname)
+                (set-auto-coding-for-load t)
                (inhibit-file-name-operation nil))
            (with-current-buffer buffer
               ;; So that we don't get completely screwed if the
diff --git a/lisp/loadup.el b/lisp/loadup.el
index cbd740a..c3b2da2 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -157,7 +157,8 @@
 ;; Load-time macro-expansion can only take effect after setting
 ;; load-source-file-function because of where it is called in lread.c.
 (load "emacs-lisp/macroexp")
-(if (byte-code-function-p (symbol-function 'macroexpand-all))
+(if (or (byte-code-function-p (symbol-function 'macroexpand-all))
+        (subr-native-elisp-p (symbol-function 'macroexpand-all)))
     nil
   ;; Since loaddefs is not yet loaded, macroexp's uses of pcase will simply
   ;; fail until pcase is explicitly loaded.  This also means that we have to
@@ -455,6 +456,43 @@ lost after dumping")))
 ;; At this point, we're ready to resume undo recording for scratch.
 (buffer-enable-undo "*scratch*")
 
+(when (featurep 'nativecomp)
+  ;; Fix the compilation unit filename to have it working when
+  ;; installed or if the source directory got moved.  This is set to be
+  ;; a pair in the form of:
+  ;;     (rel-filename-from-install-bin . rel-filename-from-local-bin).
+  (let ((h (make-hash-table :test #'eq))
+        (bin-dest-dir (cadr (member "--bin-dest" command-line-args)))
+        (eln-dest-dir (cadr (member "--eln-dest" command-line-args))))
+    (when (and bin-dest-dir eln-dest-dir)
+      (setq eln-dest-dir
+            (concat eln-dest-dir "native-lisp/" comp-native-version-dir "/"))
+      (mapatoms (lambda (s)
+                  (let ((f (symbol-function s)))
+                    (when (subr-native-elisp-p f)
+                      (puthash (subr-native-comp-unit f) nil h)))))
+      (maphash (lambda (cu _)
+                 (let* ((file (native-comp-unit-file cu))
+                        (preloaded (equal (substring (file-name-directory file)
+                                                     -10 -1)
+                                          "preloaded"))
+                        (eln-dest-dir-eff (if preloaded
+                                              (expand-file-name "preloaded"
+                                                                eln-dest-dir)
+                                            eln-dest-dir)))
+                   (native-comp-unit-set-file
+                    cu
+                   (cons
+                     ;; Relative filename from the installed binary.
+                     (file-relative-name (expand-file-name
+                                          (file-name-nondirectory
+                                           file)
+                                          eln-dest-dir-eff)
+                                         bin-dest-dir)
+                     ;; Relative filename from the built uninstalled binary.
+                     (file-relative-name file invocation-directory)))))
+              h))))
+
 (when (hash-table-p purify-flag)
   (let ((strings 0)
         (vectors 0)
@@ -490,6 +528,11 @@ lost after dumping")))
                         ((equal dump-mode "bootstrap") "emacs")
                         ((equal dump-mode "pbootstrap") "bootstrap-emacs.pdmp")
                         (t (error "unrecognized dump mode %s" dump-mode)))))
+      (when (and (featurep 'nativecomp)
+                 (equal dump-mode "pdump"))
+        ;; Don't enable this before bootstrap is completed, as the
+        ;; compiler infrastructure may not be usable yet.
+        (setq comp-enable-subr-trampolines t))
       (message "Dumping under the name %s" output)
       (condition-case ()
           (delete-file output)
@@ -546,6 +589,7 @@ lost after dumping")))
 ;; Don't keep `load-file-name' set during the top-level session!
 ;; Otherwise, it breaks a lot of code which does things like
 ;; (or load-file-name byte-compile-current-file).
+(setq load-true-file-name nil)
 (setq load-file-name nil)
 (eval top-level t)
 
diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el
index 5f3d75e..14c93f2 100644
--- a/lisp/mail/emacsbug.el
+++ b/lisp/mail/emacsbug.el
@@ -309,7 +309,7 @@ usually do not have translators for other languages.\n\n")))
      (lambda (var)
        (let ((val (getenv var)))
         (if val (insert (format "  value of $%s: %s\n" var val)))))
-     '("EMACSDATA" "EMACSDOC" "EMACSLOADPATH" "EMACSPATH"
+     '("EMACSDATA" "EMACSDOC" "EMACSLOADPATH" "EMACSNATIVELOADPATH" "EMACSPATH"
        "LC_ALL" "LC_COLLATE" "LC_CTYPE" "LC_MESSAGES"
        "LC_MONETARY" "LC_NUMERIC" "LC_TIME" "LANG" "XMODIFIERS"))
     (insert (format "  locale-coding-system: %s\n" locale-coding-system))
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index a411aaf..dc34b8f 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -5080,7 +5080,7 @@ ID-FORMAT valid values are `string' and `integer'."
     (or (when-let
            ((handler
              (find-file-name-handler
-              (tramp-make-tramp-file-name vec) 'tramp-get-remote-uid)))
+              (tramp-make-tramp-file-name vec) 'tramp-get-remote-gid)))
          (funcall handler #'tramp-get-remote-gid vec id-format))
        ;; Ensure there is a valid result.
        (and (equal id-format 'integer) tramp-unknown-id-integer)
diff --git a/lisp/printing.el b/lisp/printing.el
index b9a2e33..5c7da96 100644
--- a/lisp/printing.el
+++ b/lisp/printing.el
@@ -4775,13 +4775,13 @@ If menu binding was not done, calls `pr-menu-bind'."
 (defun pr-menu-create (name alist var-sym fun entry index)
   (cons name
        (mapcar
-        #'(lambda (elt)
-            (let ((sym (car elt)))
-              (vector
-               (symbol-name sym)
-               `(,fun ',sym nil ',entry ',index)
-               :style 'radio
-               :selected `(eq ,var-sym ',sym))))
+         (lambda (elt)
+           (let ((sym (car elt)))
+             (vector
+              (symbol-name sym)
+              `(,fun ',sym nil ',entry ',index)
+              :style 'radio
+              :selected `(eq ,var-sym ',sym))))
         alist)))
 
 
@@ -4883,23 +4883,23 @@ If menu binding was not done, calls `pr-menu-bind'."
                                               (cons inherits old)))))
           (mapc
            (cond ((not local)          ; global settings
-                  #'(lambda (option)
-                      (let ((var-sym (car option)))
-                        (or (eq var-sym 'inherits-from:)
-                            (set var-sym (eval (cdr option)))))))
+                   (lambda (option)
+                     (let ((var-sym (car option)))
+                       (or (eq var-sym 'inherits-from:)
+                           (set var-sym (eval (cdr option)))))))
                  (kill                 ; local settings with killing
-                  #'(lambda (option)
-                      (let ((var-sym (car option)))
-                        (unless (eq var-sym 'inherits-from:)
-                          (setq local-list (cons var-sym local-list))
-                          (set (make-local-variable var-sym)
-                               (eval (cdr option)))))))
+                   (lambda (option)
+                     (let ((var-sym (car option)))
+                       (unless (eq var-sym 'inherits-from:)
+                         (setq local-list (cons var-sym local-list))
+                         (set (make-local-variable var-sym)
+                              (eval (cdr option)))))))
                  (t                    ; local settings without killing
-                  #'(lambda (option)
-                      (let ((var-sym (car option)))
-                        (or (eq var-sym 'inherits-from:)
-                            (set (make-local-variable var-sym)
-                                 (eval (cdr option))))))))
+                   (lambda (option)
+                     (let ((var-sym (car option)))
+                       (or (eq var-sym 'inherits-from:)
+                           (set (make-local-variable var-sym)
+                                (eval (cdr option))))))))
            (nthcdr 3 setting))
           local-list))))
 
@@ -5077,9 +5077,9 @@ If menu binding was not done, calls `pr-menu-bind'."
 
 
 (defun pr-complete-alist (prompt alist default)
-  (let ((collection (mapcar #'(lambda (elt)
-                               (setq elt (car elt))
-                               (cons (symbol-name elt) elt))
+  (let ((collection (mapcar (lambda (elt)
+                              (setq elt (car elt))
+                              (cons (symbol-name elt) elt))
                            alist)))
     (cdr (assoc (completing-read (concat prompt ": ")
                                 collection nil t
@@ -5413,19 +5413,19 @@ If menu binding was not done, calls `pr-menu-bind'."
 
 
 (defun pr-file-list (dir file-regexp fun)
-  (mapcar #'(lambda (file)
-             (and (or pr-list-directory
-                      (not (file-directory-p file)))
-                  (let ((buffer (pr-find-buffer-visiting file))
-                        pop-up-windows
-                        pop-up-frames)
-                    (and (or buffer
-                             (file-readable-p file))
-                         (with-current-buffer (or buffer
-                                                   (find-file-noselect file))
-                           (funcall fun)
-                           (or buffer
-                               (kill-buffer (current-buffer))))))))
+  (mapcar (lambda (file)
+            (and (or pr-list-directory
+                     (not (file-directory-p file)))
+                 (let ((buffer (pr-find-buffer-visiting file))
+                       pop-up-windows
+                       pop-up-frames)
+                   (and (or buffer
+                            (file-readable-p file))
+                        (with-current-buffer (or buffer
+                                                 (find-file-noselect file))
+                          (funcall fun)
+                          (or buffer
+                              (kill-buffer (current-buffer))))))))
          (directory-files dir t file-regexp)))
 
 
@@ -5438,10 +5438,10 @@ If menu binding was not done, calls `pr-menu-bind'."
   (pr-delete-file-if-exists (setq filename (expand-file-name filename)))
   (let ((pr-spool-p t))
     (pr-file-list dir file-regexp
-                 #'(lambda ()
-                     (if (pr-auto-mode-p)
-                         (pr-ps-mode n-up filename)
-                       (pr-text2ps 'buffer n-up filename)))))
+                  (lambda ()
+                    (if (pr-auto-mode-p)
+                        (pr-ps-mode n-up filename)
+                      (pr-text2ps 'buffer n-up filename)))))
   (or pr-spool-p
       (pr-despool-print filename)))
 
@@ -5672,44 +5672,44 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
        (pr-insert-checkbox
        "\n               "
        'pr-i-region
-       #'(lambda (widget &rest _ignore)
-           (let ((region-p (pr-interface-save
-                            (ps-mark-active-p))))
-             (cond ((null (widget-value widget)) ; widget is nil
-                    (setq pr-i-region nil))
-                   (region-p           ; widget is true and there is a region
-                    (setq pr-i-region t)
-                    (widget-value-set widget t)
-                    (widget-setup))    ; MUST be called after widget-value-set
-                   (t                  ; widget is true and there is no region
-                    (ding)
-                    (message "There is no region active")
-                    (setq pr-i-region nil)
-                    (widget-value-set widget nil)
-                    (widget-setup))))) ; MUST be called after widget-value-set
+        (lambda (widget &rest _ignore)
+          (let ((region-p (pr-interface-save
+                           (ps-mark-active-p))))
+            (cond ((null (widget-value widget)) ; widget is nil
+                   (setq pr-i-region nil))
+                  (region-p            ; widget is true and there is a region
+                   (setq pr-i-region t)
+                   (widget-value-set widget t)
+                   (widget-setup))     ; MUST be called after widget-value-set
+                  (t                   ; widget is true and there is no region
+                   (ding)
+                   (message "There is no region active")
+                   (setq pr-i-region nil)
+                   (widget-value-set widget nil)
+                   (widget-setup)))))  ; MUST be called after widget-value-set
        " Region"))
   ;;    1a. Buffer: Mode
   (put 'pr-i-mode 'pr-widget
        (pr-insert-checkbox
        "    "
        'pr-i-mode
-       #'(lambda (widget &rest _ignore)
-           (let ((mode-p (pr-interface-save
-                          (pr-mode-alist-p))))
-             (cond
-              ((null (widget-value widget)) ; widget is nil
-               (setq pr-i-mode nil))
-              (mode-p                  ; widget is true and there is a `mode'
-               (setq pr-i-mode t)
-               (widget-value-set widget t)
-               (widget-setup))         ; MUST be called after widget-value-set
-              (t                       ; widget is true and there is no `mode'
-               (ding)
-               (message
-                "This buffer isn't in a mode that printing treats specially.")
-               (setq pr-i-mode nil)
-               (widget-value-set widget nil)
-               (widget-setup)))))      ; MUST be called after widget-value-set
+        (lambda (widget &rest _ignore)
+          (let ((mode-p (pr-interface-save
+                         (pr-mode-alist-p))))
+            (cond
+             ((null (widget-value widget)) ; widget is nil
+              (setq pr-i-mode nil))
+             (mode-p                   ; widget is true and there is a `mode'
+              (setq pr-i-mode t)
+              (widget-value-set widget t)
+              (widget-setup))          ; MUST be called after widget-value-set
+             (t                        ; widget is true and there is no `mode'
+              (ding)
+              (message
+               "This buffer isn't in a mode that printing treats specially.")
+              (setq pr-i-mode nil)
+              (widget-value-set widget nil)
+              (widget-setup)))))       ; MUST be called after widget-value-set
        " Mode\n"))
 
   ;;    1b. Directory:
@@ -5769,14 +5769,14 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
        (pr-insert-checkbox
        "    "
        'pr-i-despool
-       #'(lambda (widget &rest _ignore)
-           (if pr-spool-p
-               (setq pr-i-despool (not pr-i-despool))
-             (ding)
-             (message "Can despool only when spooling is actually selected")
-             (setq pr-i-despool nil))
-           (widget-value-set widget pr-i-despool)
-           (widget-setup))             ; MUST be called after widget-value-set
+        (lambda (widget &rest _ignore)
+          (if pr-spool-p
+              (setq pr-i-despool (not pr-i-despool))
+            (ding)
+            (message "Can despool only when spooling is actually selected")
+            (setq pr-i-despool nil))
+          (widget-value-set widget pr-i-despool)
+          (widget-setup))              ; MUST be called after widget-value-set
        " Despool   "))
   ;; 2. PostScript Printer: Preview    Print    Quit
   (pr-insert-button 'pr-interface-preview "Preview" "   ")
@@ -5835,9 +5835,9 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
   ;; 4. Settings:
   ;; 4. Settings: Landscape             Auto Region    Verbose
   (pr-insert-checkbox "\n\n  " 'ps-landscape-mode
-                     #'(lambda (&rest _ignore)
-                         (setq ps-landscape-mode (not ps-landscape-mode)
-                               pr-file-landscape ps-landscape-mode))
+                      (lambda (&rest _ignore)
+                        (setq ps-landscape-mode (not ps-landscape-mode)
+                              pr-file-landscape ps-landscape-mode))
                      " Landscape             ")
   (pr-insert-toggle 'pr-auto-region " Auto Region                ")
   (pr-insert-toggle 'pr-buffer-verbose " Verbose\n  ")
@@ -5857,28 +5857,28 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
   (pr-insert-toggle 'ps-zebra-stripes " Zebra Stripes")
   (pr-insert-checkbox "         "
                      'pr-spool-p
-                     #'(lambda (&rest _ignore)
-                         (setq pr-spool-p (not pr-spool-p))
-                         (unless pr-spool-p
-                           (setq pr-i-despool nil)
-                           (pr-update-checkbox 'pr-i-despool)))
+                      (lambda (&rest _ignore)
+                        (setq pr-spool-p (not pr-spool-p))
+                        (unless pr-spool-p
+                          (setq pr-i-despool nil)
+                          (pr-update-checkbox 'pr-i-despool)))
                      " Spool Buffer")
 
   ;; 4. Settings: Duplex                Print with faces
   (pr-insert-checkbox "\n  "
                      'ps-spool-duplex
-                     #'(lambda (&rest _ignore)
-                         (setq ps-spool-duplex (not ps-spool-duplex)
-                               pr-file-duplex  ps-spool-duplex))
+                      (lambda (&rest _ignore)
+                        (setq ps-spool-duplex (not ps-spool-duplex)
+                              pr-file-duplex  ps-spool-duplex))
                      " Duplex                ")
   (pr-insert-toggle 'pr-faces-p " Print with faces")
 
   ;; 4. Settings: Tumble                Print via Ghostscript
   (pr-insert-checkbox "\n  "
                      'ps-spool-tumble
-                     #'(lambda (&rest _ignore)
-                         (setq ps-spool-tumble (not ps-spool-tumble)
-                               pr-file-tumble  ps-spool-tumble))
+                      (lambda (&rest _ignore)
+                        (setq ps-spool-tumble (not ps-spool-tumble)
+                              pr-file-tumble  ps-spool-tumble))
                      " Tumble                ")
   (pr-insert-toggle 'pr-print-using-ghostscript " Print via Ghostscript\n  ")
 
@@ -5886,11 +5886,11 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
   (pr-insert-toggle 'ps-print-upside-down " Upside-Down")
   (pr-insert-italic "\n\nSelect Pages  :   " 2 14)
   (pr-insert-menu "Page Parity" 'ps-even-or-odd-pages
-                 (mapcar #'(lambda (alist)
-                              (list 'choice-item
-                                    :format "%[%t%]"
-                                    :tag (cdr alist)
-                                    :value (car alist)))
+                  (mapcar (lambda (alist)
+                            (list 'choice-item
+                                  :format "%[%t%]"
+                                  :tag (cdr alist)
+                                  :value (car alist)))
                          pr-even-or-odd-alist)))
 
 
@@ -5898,7 +5898,7 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
   ;; 5. Customize:
   (pr-insert-italic "\n\nCustomize     :   " 2 11)
   (pr-insert-button 'pr-customize "printing" "   ")
-  (pr-insert-button #'(lambda (&rest _ignore) (ps-print-customize))
+  (pr-insert-button (lambda (&rest _ignore) (ps-print-customize))
                    "ps-print" "   ")
   (pr-insert-button 'lpr-customize "lpr"))
 
@@ -6207,18 +6207,18 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
 
 
 (defun pr-choice-alist (alist)
-  (let ((max (apply #'max (mapcar #'(lambda (alist)
-                                      (length (symbol-name (car alist))))
+  (let ((max (apply #'max (mapcar (lambda (alist)
+                                    (length (symbol-name (car alist))))
                                   alist))))
-    (mapcar #'(lambda (alist)
-               (let* ((sym  (car alist))
-                      (name (symbol-name sym)))
-                  (list
-                   'choice-item
-                   :format "%[%t%]"
-                   :tag (concat name
-                                (make-string (- max (length name)) ?_))
-                   :value sym)))
+    (mapcar (lambda (alist)
+              (let* ((sym  (car alist))
+                     (name (symbol-name sym)))
+                (list
+                 'choice-item
+                 :format "%[%t%]"
+                 :tag (concat name
+                              (make-string (- max (length name)) ?_))
+                 :value sym)))
            alist)))
 
 
@@ -6227,5 +6227,4 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
 
 (provide 'printing)
 
-
 ;;; printing.el ends here
diff --git a/lisp/progmodes/cc-bytecomp.el b/lisp/progmodes/cc-bytecomp.el
index b3f7020..edbac64 100644
--- a/lisp/progmodes/cc-bytecomp.el
+++ b/lisp/progmodes/cc-bytecomp.el
@@ -97,6 +97,8 @@
   ;; compilation can trigger loading (various `require' type forms)
   ;; and loading can trigger compilation (the package manager does
   ;; this).  We walk the lisp stack if necessary.
+  ;; Never native compile to allow cc-defs.el:2345 hack.
+  (declare (speed -1))
   (cond
    ((and load-in-progress
         (boundp 'byte-compile-dest-file)
@@ -108,14 +110,15 @@
                        (memq (cadr elt)
                              '(load require
                                byte-compile-file byte-recompile-directory
-                               batch-byte-compile)))))
+                               batch-byte-compile batch-native-compile)))))
        (setq n (1+ n)))
       (cond
        ((memq (cadr elt) '(load require))
        'loading)
        ((memq (cadr elt) '(byte-compile-file
                           byte-recompile-directory
-                          batch-byte-compile))
+                          batch-byte-compile
+                          batch-native-compile))
        'compiling)
        (t                              ; Can't happen.
        (message "cc-bytecomp-compiling-or-loading: System flags spuriously 
set")
@@ -284,7 +287,9 @@ perhaps a `cc-bytecomp-restore-environment' is forgotten 
somewhere"))
                    (cons cc-file cc-bytecomp-loaded-files))
              (cc-bytecomp-debug-msg
               "cc-bytecomp-load: Loading %S" cc-file)
-             (load cc-file nil t t)
+             ;; native-comp may async compile also intalled el.gz
+             ;; files therefore we may have to load here other el.gz.
+             (load cc-part nil t)
              (cc-bytecomp-debug-msg
               "cc-bytecomp-load: Loaded %S" cc-file)))
          (cc-bytecomp-setup-environment)
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index fdef084..a7c8712 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1669,9 +1669,7 @@ casts and declarations are fontified.  Used on level 2 
and higher."
          c-recognize-knr-p)            ; Strictly speaking, bogus, but it
                                        ; speeds up lisp.h tremendously.
       (save-excursion
-       (when (not (c-back-over-member-initializers
-                   (max (- (point) 2000) (point-min)))) ; c-determine-limit
-                                                        ; is too slow, here.
+       (when (not (c-back-over-member-initializers decl-search-lim))
          (unless (or (eobp)
                      (looking-at "\\s(\\|\\s)"))
            (forward-char))
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index 70b0d13..f664849 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -333,7 +333,8 @@ the evaluated constant value at compile time."
 This includes setting \\=' and \" as string delimiters, and setting up
 the comment syntax to handle both line style \"//\" and block style
 \"/*\" \"*/\" comments."
-
+  ;; Never native compile to allow cc-mode.el:467 hack.
+  (declare (speed -1))
   (modify-syntax-entry ?_  "_"     table)
   (modify-syntax-entry ?\\ "\\"    table)
   (modify-syntax-entry ?+  "."     table)
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 203712f..a690d4b 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -160,19 +160,35 @@ All commands in `lisp-mode-shared-map' are inherited by 
this map.")
       (byte-compile-file buffer-file-name)
     (error "The buffer must be saved in a file first")))
 
-(defun emacs-lisp-byte-compile-and-load ()
-  "Byte-compile the current file (if it has changed), then load compiled code."
-  (interactive nil emacs-lisp-mode)
+(defun emacs-lisp--before-compile-buffer ()
+  "Make sure the buffer is saved before compiling."
   (or buffer-file-name
       (error "The buffer must be saved in a file first"))
-  (require 'bytecomp)
   ;; Recompile if file or buffer has changed since last compilation.
   (if (and (buffer-modified-p)
           (y-or-n-p (format "Save buffer %s first? " (buffer-name))))
-      (save-buffer))
+      (save-buffer)))
+
+(defun emacs-lisp-byte-compile-and-load ()
+  "Byte-compile the current file (if it has changed), then load compiled code."
+  (interactive nil emacs-lisp-mode)
+  (emacs-lisp--before-compile-buffer)
+  (require 'bytecomp)
   (byte-recompile-file buffer-file-name nil 0)
   (load buffer-file-name))
 
+(declare-function native-compile "comp")
+(defun emacs-lisp-native-compile-and-load ()
+  "Native-compile synchronously the current file (if it has changed).
+Load the compiled code when finished.
+
+Use `emacs-lisp-byte-compile-and-load' in combination with
+`comp-deferred-compilation' set to `t' to achieve asynchronous
+native compilation."
+  (interactive nil emacs-lisp-mode)
+  (emacs-lisp--before-compile-buffer)
+  (load (native-compile buffer-file-name)))
+
 (defun emacs-lisp-macroexpand ()
   "Macroexpand the form after point.
 Comments in the form will be lost."
diff --git a/lisp/ses.el b/lisp/ses.el
index 98785b6..bc3c2de 100644
--- a/lisp/ses.el
+++ b/lisp/ses.el
@@ -172,14 +172,14 @@ Each function is called with ARG=1."
 
 (defvar ses--completion-table nil
   "Set globally to what completion table to use depending on type
-  of completion (local printers, cells, etc.). We need to go
-  through a local variable to pass the SES buffer local variable
-  to completing function while the current buffer is the
-  minibuffer.")
+of completion (local printers, cells, etc.).  We need to go
+through a local variable to pass the SES buffer local variable
+to completing function while the current buffer is the
+minibuffer.")
 
 (defvar ses--list-orig-buffer nil
-  "Calling buffer for SES listing help. Used for listing local
-  printers or renamed cells.")
+  "Calling buffer for SES listing help.
+Used for listing local printers or renamed cells.")
 
 
 (defconst ses-mode-edit-map
@@ -395,8 +395,9 @@ left-justification of the result.  Set to error-signal if 
`ses-call-printer'
 encountered an error during printing.  Otherwise nil.")
 
 (defvar ses-start-time nil
-  "Time when current operation started.  Used by `ses--time-check' to decide
-when to emit a progress message.")
+  "Time when current operation started.
+Used by `ses--time-check' to decide when to emit a progress
+message.")
 
 
 ;;----------------------------------------------------------------------------
@@ -560,9 +561,10 @@ the corresponding cell with name PROPERTY-NAME."
                (eq (ses-cell-symbol (car rowcol) (cdr rowcol)) sym))))))
 
 (defun ses--cell (sym value formula printer references)
-  "Load a cell SYM from the spreadsheet file.  Does not recompute VALUE from
-FORMULA, does not reprint using PRINTER, does not check REFERENCES.
-Safety-checking for FORMULA and PRINTER are deferred until first use."
+  "Load a cell SYM from the spreadsheet file.
+Does not recompute VALUE from FORMULA, does not reprint using
+PRINTER, does not check REFERENCES.  Safety-checking for FORMULA
+and PRINTER are deferred until first use."
   (let ((rowcol (ses-sym-rowcol sym)))
     (ses-formula-record formula)
     (ses-printer-record printer)
@@ -580,8 +582,7 @@ Safety-checking for FORMULA and PRINTER are deferred until 
first use."
   (set sym value))
 
 (defun ses-local-printer-compile (printer)
-  "Convert local printer function into faster printer
-definition."
+  "Convert local printer function into faster printer definition."
   (cond
    ((functionp printer) printer)
    ((stringp printer)
@@ -610,8 +611,8 @@ Return the printer info."
           ses--local-printer-hashmap))
 
 (defmacro ses-column-widths (widths)
-  "Load the vector of column widths from the spreadsheet file.  This is a
-macro to prevent propagate-on-load viruses."
+  "Load the vector of column widths from the spreadsheet file.
+This is a macro to prevent propagate-on-load viruses."
   (or (and (vectorp widths) (= (length widths) ses--numcols))
       (error "Bad column-width vector"))
   ;;To save time later, we also calculate the total width of each line in the
@@ -748,8 +749,8 @@ for this spreadsheet."
   (intern (concat (ses-column-letter col) (number-to-string (1+ row)))))
 
 (defun ses-decode-cell-symbol (str)
-  "Decode a symbol \"A1\" => (0,0).  Return nil if STR is not a
-canonical cell name."
+  "Decode a symbol \"A1\" => (0,0).
+Return nil if STR is not a canonical cell name."
   (let (case-fold-search)
     (and (string-match "\\`\\([A-Z]+\\)\\([0-9]+\\)\\'" str)
         (let* ((col-str (match-string-no-properties 1 str))
@@ -1061,15 +1062,15 @@ the old and FORCE is nil."
   (ses-cell-set-formula row col nil))
 
 (defcustom ses-self-reference-early-detection nil
-  "True if cycle detection is early for cells that refer to themselves."
+  "Non-nil if cycle detection is early for cells that refer to themselves."
   :version "24.1"
   :type 'boolean
   :group 'ses)
 
 (defun ses-update-cells (list &optional force)
-  "Recalculate cells in LIST, checking for dependency loops.  Prints
-progress messages every second.  Dependent cells are not recalculated
-if the cell's value is unchanged and FORCE is nil."
+  "Recalculate cells in LIST, checking for dependency loops.
+Print progress messages every second.  Dependent cells are not
+recalculated if the cell's value is unchanged and FORCE is nil."
   (let ((ses--deferred-recalc list)
        (nextlist             list)
        (pos                  (point))
@@ -2025,7 +2026,7 @@ Delete overlays, remove special text properties."
 When you invoke SES in a new buffer, it is divided into cells
 that you can enter data into.  You can navigate the cells with
 the arrow keys and add more cells with the tab key.  The contents
-of these cells can be numbers, text, or Lisp expressions. (To
+of these cells can be numbers, text, or Lisp expressions.  (To
 enter text, enclose it in double quotes.)
 
 In an expression, you can use cell coordinates to refer to the
@@ -2131,9 +2132,9 @@ formula:
 
 (defun ses-command-hook ()
   "Invoked from `post-command-hook'.  If point has moved to a different cell,
-moves the underlining overlay.  Performs any recalculations or cell-data
+move the underlining overlay.  Perform any recalculations or cell-data
 writes that have been deferred.  If buffer-narrowing has been deferred,
-narrows the buffer now."
+narrow the buffer now."
   (condition-case err
       (when (eq major-mode 'ses-mode)  ; Otherwise, not our buffer anymore.
        (when ses--deferred-recalc
@@ -2267,8 +2268,8 @@ Based on the current set of columns and `window-hscroll' 
position."
     (ses-jump cell)))
 
 (defun ses-reprint-all (&optional nonarrow)
-  "Recreate the display area.  Calls all printer functions.  Narrows to
-print area if NONARROW is nil."
+  "Recreate the display area.  Call all printer functions.
+Narrow to print area if optional argument NONARROW is nil."
   (interactive "*P")
   (widen)
   (unless nonarrow
@@ -2495,8 +2496,8 @@ to are recalculated first."
       (and collection (list start end collection))))))
 
 (defun ses-edit-cell (row col newval)
-  "Display current cell contents in minibuffer, for editing.  Returns nil if
-cell formula was unsafe and user declined confirmation."
+  "Display current cell contents in minibuffer, for editing.
+Return nil if cell formula was unsafe and user declined confirmation."
   (interactive
    (progn
      (barf-if-buffer-read-only)
@@ -2559,8 +2560,9 @@ cell formula was unsafe and user declined confirmation."
       (funcall x 1))))
 
 (defun ses-read-symbol (row col symb)
-  "Self-insert for a symbol as a cell formula.  The set of all symbols that
-have been used as formulas in this spreadsheet is available for completions."
+  "Self-insert for a symbol as a cell formula.
+The set of all symbols that have been used as formulas in this
+spreadsheet is available for completions."
   (interactive
    (let ((rowcol (progn (ses-check-curcell) (ses-sym-rowcol ses--curcell)))
         newval)
@@ -2593,7 +2595,7 @@ With prefix, deletes several cells."
       (forward-char 1))))
 
 (defun ses-clear-cell-backward (count)
-  "Move to previous cell and then delete it.  With prefix, deletes several
+  "Move to previous cell and then delete it.  With prefix, delete several
 cells."
   (interactive "*p")
   (if (< count 0)
@@ -3371,9 +3373,9 @@ is non-nil.  Newlines and tabs in the export text are 
escaped."
 ;;----------------------------------------------------------------------------
 
 (defun ses-list-local-printers (&optional local-printer-hashmap)
-  "List local printers in a help buffer. Can be called either
-during editing a printer or a formula, or while in the SES
-buffer."
+  "List local printers in a help buffer.
+Can be called either during editing a printer or a formula, or
+while in the SES buffer."
   (interactive
    (list (cond
           ((derived-mode-p 'ses-mode) ses--local-printer-hashmap)
@@ -3405,9 +3407,9 @@ buffer."
             (buffer-string)))))))
 
 (defun ses-list-named-cells (&optional named-cell-hashmap)
-  "List named cells in a help buffer. Can be called either
-during editing a printer or a formula, or while in the SES
-buffer."
+  "List named cells in a help buffer.
+Can be called either during editing a printer or a formula, or
+while in the SES buffer."
   (interactive
    (list (cond
           ((derived-mode-p 'ses-mode) ses--named-cell-hashmap)
diff --git a/lisp/startup.el b/lisp/startup.el
index cf536b3..65d50d8 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -519,6 +519,7 @@ DIRS are relative."
       xdg-dir)
      (t emacs-d-dir))))
 
+(defvar comp-eln-load-path)
 (defun normal-top-level ()
   "Emacs calls this function when it first starts up.
 It sets `command-line-processed', processes the command-line,
@@ -536,6 +537,20 @@ It is the default value of the variable `top-level'."
     (setq user-emacs-directory
          (startup--xdg-or-homedot startup--xdg-config-home-emacs nil))
 
+    (when (featurep 'nativecomp)
+      ;; Form `comp-eln-load-path'.
+      (let ((path-env (getenv "EMACSNATIVELOADPATH")))
+        (when path-env
+          (dolist (path (split-string path-env path-separator))
+            (unless (string= "" path)
+              (push path comp-eln-load-path)))))
+      (push (expand-file-name "eln-cache/" user-emacs-directory)
+            comp-eln-load-path)
+      ;; When $HOME is set to '/nonexistent' means we are running the
+      ;; testsuite, add a temporary folder in front to produce there
+      ;; new compilations.
+      (when (equal (getenv "HOME") "/nonexistent")
+        (push (make-temp-file "emacs-testsuite-" t) comp-eln-load-path)))
     ;; Look in each dir in load-path for a subdirs.el file.  If we
     ;; find one, load it, which will add the appropriate subdirs of
     ;; that dir into load-path.  This needs to be done before setting
@@ -622,6 +637,16 @@ It is the default value of the variable `top-level'."
                (set pathsym (mapcar (lambda (dir)
                                       (decode-coding-string dir coding t))
                                     path)))))
+        (when (featurep 'nativecomp)
+          (let ((npath (symbol-value 'comp-eln-load-path)))
+            (set 'comp-eln-load-path
+                 (mapcar (lambda (dir)
+                           ;; Call expand-file-name to remove all the
+                           ;; pesky ".." from the directyory names in
+                           ;; comp-eln-load-path.
+                           (expand-file-name
+                            (decode-coding-string dir coding t)))
+                         npath))))
        (dolist (filesym '(data-directory doc-directory exec-directory
                                          installation-directory
                                          invocation-directory invocation-name
diff --git a/lisp/subr.el b/lisp/subr.el
index 40ca934..964eb8f 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -246,6 +246,11 @@ value of last one, or nil if there are none.
   (declare (indent 1) (debug t))
   (cons 'if (cons cond (cons nil body))))
 
+(defsubst subr-primitive-p (object)
+  "Return t if OBJECT is a built-in primitive function."
+  (and (subrp object)
+       (not (subr-native-elisp-p object))))
+
 (defsubst xor (cond1 cond2)
   "Return the boolean exclusive-or of COND1 and COND2.
 If only one of the arguments is non-nil, return it; otherwise
@@ -5529,7 +5534,7 @@ command is called from a keyboard macro?"
       ;; Now `frame' should be "the function from which we were called".
       (pcase (cons frame nextframe)
         ;; No subr calls `interactive-p', so we can rule that out.
-        (`((,_ ,(pred (lambda (f) (subrp (indirect-function f)))) . ,_) . ,_) 
nil)
+        (`((,_ ,(pred (lambda (f) (subr-primitive-p (indirect-function f)))) . 
,_) . ,_) nil)
         ;; In case #<subr funcall-interactively> without going through the
         ;; `funcall-interactively' symbol (bug#3984).
         (`(,_ . (t ,(pred (lambda (f)
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index 687250f..6b84916 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -284,7 +284,8 @@ See the documentation of `create-fontset-from-fontset-spec' 
for the format.")
        '(libxml2 "libxml2-2.dll" "libxml2.dll")
        '(zlib "zlib1.dll" "libz-1.dll")
        '(lcms2 "liblcms2-2.dll")
-       '(json "libjansson-4.dll")))
+       '(json "libjansson-4.dll")
+       '(gccjit "libgccjit-0.dll")))
 
 ;;; multi-tty support
 (defvar w32-initialized nil
diff --git a/lisp/textmodes/fill.el b/lisp/textmodes/fill.el
index 61514d6..3914bde 100644
--- a/lisp/textmodes/fill.el
+++ b/lisp/textmodes/fill.el
@@ -49,10 +49,12 @@ A value of nil means that any change in indentation starts 
a new paragraph."
 
 (defcustom fill-separate-heterogeneous-words-with-space nil
   "Non-nil means to use a space to separate words of a different kind.
-This will be done with a word in the end of a line and a word in
-the beginning of the next line when concatenating them for
-filling those lines.  Whether to use a space depends on how the
-words are categorized."
+For example, when an English word at the end of a line and a CJK word
+at the beginning of the next line are joined into a single line, they
+will be separated by a space if this variable is non-nil.
+Whether to use a space to separate such words also depends on the entry
+in `fill-nospace-between-words-table' for the characters before and
+after the newline."
   :type 'boolean
   :version "26.1")
 
diff --git a/lisp/url/url-proxy.el b/lisp/url/url-proxy.el
index 8436c7a..c89c1b6 100644
--- a/lisp/url/url-proxy.el
+++ b/lisp/url/url-proxy.el
@@ -49,14 +49,12 @@
     ;; Not sure how I should handle gracefully degrading from one proxy to
     ;; another, so for now just deal with the first one
     ;; (while proxies
-    (if (listp proxies)
-       (setq proxy (car proxies))
-      (setq proxy proxies))
+    (setq proxy (if (listp proxies) (car proxies) proxies))
     (cond
-     ((string-match "^direct" proxy) nil)
-     ((string-match "^proxy +" proxy)
+     ((string-match "^DIRECT" proxy) nil)
+     ((string-match "^PROXY +" proxy)
       (concat "http://"; (substring proxy (match-end 0)) "/"))
-     ((string-match "^socks +" proxy)
+     ((string-match "^SOCKS +" proxy)
       (concat "socks://" (substring proxy (match-end 0))))
      (t
       (display-warning 'url (format "Unknown proxy directive: %s" proxy) 
:error)
diff --git a/lisp/window.el b/lisp/window.el
index 06d3e43..036eb27 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -3755,8 +3755,6 @@ WINDOW must be a valid window and defaults to the 
selected one.
 If the option `window-resize-pixelwise' is non-nil minimize
 WINDOW pixelwise."
   (interactive)
-  (when switch-to-buffer-preserve-window-point
-    (window--before-delete-windows window))
   (setq window (window-normalize-window window))
   (window-resize
    window
@@ -4142,41 +4140,6 @@ frame can be safely deleted."
                (throw 'done t)
              (setq parent (window-parent parent))))))))
 
-;; This function is called by `delete-window' and
-;; `delete-other-windows' when `switch-to-buffer-preserve-window-point'
-;; evaluates non-nil: it allows `winner-undo' to restore the
-;; buffer point from deleted windows (Bug#23621).
-(defun window--before-delete-windows (&optional window)
-  "Update `window-prev-buffers' before delete a window.
-Optional arg WINDOW, if non-nil, update WINDOW-START and POS
-in `window-prev-buffers' for all windows displaying same
-buffer as WINDOW.  Otherwise, update `window-prev-buffers' for
-all windows.
-
-The new values for WINDOW-START and POS are those
-returned by `window-start' and `window-point' respectively.
-
-This function is called only if `switch-to-buffer-preserve-window-point'
-evaluates non-nil."
-  (dolist (win (window-list nil 'no-minibuf))
-    (let* ((buf   (window-buffer (or window win)))
-           (start (window-start win))
-           (pos   (window-point win))
-           (entry (assq buf (window-prev-buffers win))))
-      (cond (entry
-             (let ((marker (nth 2 entry)))
-               (unless (= pos marker)
-                 (set-marker (nth 1 entry) start buf)
-                 (set-marker marker pos buf))))
-            (t
-             (let ((prev-buf (window-prev-buffers win))
-                   (start-m  (make-marker))
-                   (pos-m    (make-marker)))
-               (set-marker start-m start buf)
-               (set-marker pos-m pos buf)
-               (push (list buf start-m pos-m) prev-buf)
-               (set-window-prev-buffers win prev-buf)))))))
-
 (defun delete-window (&optional window)
   "Delete WINDOW.
 WINDOW must be a valid window and defaults to the selected one.
@@ -4195,8 +4158,6 @@ argument.  Signal an error if WINDOW is either the only 
window on
 its frame, the last non-side window, or part of an atomic window
 that is its frame's root window."
   (interactive)
-  (when switch-to-buffer-preserve-window-point
-    (window--before-delete-windows))
   (setq window (window-normalize-window window))
   (let* ((frame (window-frame window))
         (function (window-parameter window 'delete-window))
diff --git a/lisp/winner.el b/lisp/winner.el
index f30fa6c..8062fba 100644
--- a/lisp/winner.el
+++ b/lisp/winner.el
@@ -283,17 +283,8 @@ You may want to include buffer names such as *Help*, 
*Apropos*,
       ;; Restore points
       (dolist (win (winner-sorted-window-list))
         (unless (and (pop alive)
-                     (let* ((buf   (window-buffer win))
-                            (pos   (winner-get-point (window-buffer win) win))
-                            (entry (assq buf (window-prev-buffers win))))
-                       ;; Try to restore point of buffer in the selected
-                       ;; window (Bug#23621).
-                       (let ((marker (nth 2 entry)))
-                         (when (and switch-to-buffer-preserve-window-point
-                                    marker
-                                    (not (= marker pos)))
-                           (setq pos marker))
-                         (setf (window-point win) pos)))
+                     (setf (window-point win)
+                           (winner-get-point (window-buffer win) win))
                     (not (or (member (buffer-name (window-buffer win))
                                      winner-boring-buffers)
                              (and winner-boring-buffers-regexp
diff --git a/nt/epaths.nt b/nt/epaths.nt
index ad60f6c..a75ed52 100644
--- a/nt/epaths.nt
+++ b/nt/epaths.nt
@@ -49,6 +49,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 */
 #define PATH_SITELOADSEARCH 
"%emacs_dir%/share/emacs/@VER@/site-lisp;%emacs_dir%/share/emacs/site-lisp"
 
+/* Like PATH_LOADSEARCH, but contains the relative path from the
+   installation directory.
+*/
+#define PATH_REL_LOADSEARCH ""
+
 /* Like PATH_LOADSEARCH, but used only during the build process
    when Emacs is dumping.  Configure (using "make epaths-force-w32") sets
    this to $buildlisppath, which normally has the value: <srcdir>/lisp.
diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site
index 9630077..431fdab 100644
--- a/nt/mingw-cfg.site
+++ b/nt/mingw-cfg.site
@@ -158,6 +158,10 @@ gl_cv_func_copy_file_range=yes
 # We don't want to build Emacs so it depends on bcrypt.dll, since then
 # it will refuse to start on systems where that DLL is absent.
 gl_cv_lib_assume_bcrypt=no
+# Force 'ac_cv_func_strsignal' to no as mingw64 libgccjit exports this
+# symbol erroneously
+# <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=45303#83>.
+ac_cv_func_strsignal=no
 # Don't build the Gnulib free.c: it is not needed, since the w32
 # implementation of 'free' doesn't touch errno, and it emits a
 # compilation warning.
diff --git a/src/Makefile.in b/src/Makefile.in
index 39c077b..5d0d1fb 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -242,7 +242,7 @@ LIBZ = @LIBZ@
 
 ## system-specific libs for dynamic modules, else empty
 LIBMODULES = @LIBMODULES@
-## dynlib.o emacs-module.o if modules enabled, else empty
+## emacs-module.o if modules enabled, else empty
 MODULES_OBJ = @MODULES_OBJ@
 
 XRANDR_LIBS = @XRANDR_LIBS@
@@ -329,6 +329,11 @@ GETLOADAVG_LIBS = @GETLOADAVG_LIBS@
 
 LIBGMP = @LIBGMP@
 
+LIBGCCJIT = @LIBGCCJIT_LIB@
+
+## dynlib.o if necessary, else empty
+DYNLIB_OBJ = @DYNLIB_OBJ@
+
 RUN_TEMACS = ./temacs
 
 # Whether builds should contain details. '--no-build-details' or empty.
@@ -395,7 +400,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o 
$(XMENU_OBJ) window.o \
        cmds.o casetab.o casefiddle.o indent.o search.o regex-emacs.o undo.o \
        alloc.o pdumper.o data.o doc.o editfns.o callint.o \
        eval.o floatfns.o fns.o font.o print.o lread.o $(MODULES_OBJ) \
-       syntax.o $(UNEXEC_OBJ) bytecode.o \
+       syntax.o $(UNEXEC_OBJ) bytecode.o comp.o $(DYNLIB_OBJ) \
        process.o gnutls.o callproc.o \
        region-cache.o sound.o timefns.o atimer.o \
        doprnt.o intervals.o textprop.o composite.o xml.o lcms.o $(NOTIFY_OBJ) \
@@ -498,6 +503,7 @@ shortlisp := $(filter-out ${shortlisp_filter},${shortlisp})
 ## the critical path (relevant in parallel compilations).
 ## We don't really need to sort, but may as well use it to remove duplicates.
 shortlisp := loaddefs.el loadup.el $(sort ${shortlisp})
+export LISP_PRELOADED = ${shortlisp}
 lisp = $(addprefix ${lispsource}/,${shortlisp})
 
 ## Construct full set of libraries to be linked.
@@ -513,7 +519,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) 
$(LIBX_BASE) $(LIBIMAGE
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) 
$(M17N_FLT_LIBS) \
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
-   $(JSON_LIBS) $(LIBGMP)
+   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT)
 
 ## FORCE it so that admin/unidata can decide whether this file is
 ## up-to-date.  Although since charprop depends on bootstrap-emacs,
@@ -563,7 +569,8 @@ endif
 
 ifeq ($(DUMPING),pdumper)
 $(pdmp): emacs$(EXEEXT)
-       LC_ALL=C $(RUN_TEMACS) -batch $(BUILD_DETAILS) -l loadup --temacs=pdump
+       LC_ALL=C $(RUN_TEMACS) -batch $(BUILD_DETAILS) -l loadup --temacs=pdump 
\
+               --bin-dest $(BIN_DESTDIR) --eln-dest $(ELN_DESTDIR)
        cp -f $@ $(bootstrap_pdmp)
 endif
 
@@ -794,7 +801,8 @@ endif
 ifeq ($(DUMPING),pdumper)
 $(bootstrap_pdmp): bootstrap-emacs$(EXEEXT)
        rm -f $@
-       $(RUN_TEMACS) --batch $(BUILD_DETAILS) -l loadup --temacs=pbootstrap
+       $(RUN_TEMACS) --batch $(BUILD_DETAILS) -l loadup --temacs=pbootstrap \
+               --bin-dest $(BIN_DESTDIR) --eln-dest $(ELN_DESTDIR)
        @: Compile some files earlier to speed up further compilation.
        $(MAKE) -C ../lisp compile-first EMACS="$(bootstrap_exe)"
 endif
diff --git a/src/alloc.c b/src/alloc.c
index efcf0a5..e57bec1 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3152,6 +3152,26 @@ cleanup_vector (struct Lisp_Vector *vector)
       module_finalize_function (function);
     }
 #endif
+  else if (NATIVE_COMP_FLAG
+          && PSEUDOVECTOR_TYPEP (&vector->header, PVEC_NATIVE_COMP_UNIT))
+    {
+      struct Lisp_Native_Comp_Unit *cu =
+       PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
+      unload_comp_unit (cu);
+    }
+  else if (NATIVE_COMP_FLAG
+          && PSEUDOVECTOR_TYPEP (&vector->header, PVEC_SUBR))
+    {
+      struct Lisp_Subr *subr =
+       PSEUDOVEC_STRUCT (vector, Lisp_Subr);
+      if (!NILP (subr->native_comp_u[0]))
+       {
+         /* FIXME Alternative and non invasive solution to this
+            cast?  */
+         xfree ((char *)subr->symbol_name);
+         xfree (subr->native_c_name[0]);
+       }
+    }
 }
 
 /* Reclaim space used by unmarked vectors.  */
@@ -6728,6 +6748,15 @@ mark_object (Lisp_Object arg)
            break;
 
          case PVEC_SUBR:
+           if (SUBR_NATIVE_COMPILEDP (obj))
+             {
+               set_vector_marked (ptr);
+               struct Lisp_Subr *subr = XSUBR (obj);
+               mark_object (subr->native_intspec);
+               mark_object (subr->native_comp_u[0]);
+               mark_object (subr->lambda_list[0]);
+               mark_object (subr->type[0]);
+             }
            break;
 
          case PVEC_FREE:
@@ -6872,7 +6901,9 @@ survives_gc_p (Lisp_Object obj)
       break;
 
     case Lisp_Vectorlike:
-      survives_p = SUBRP (obj) || vector_marked_p (XVECTOR (obj));
+      survives_p =
+       (SUBRP (obj) && !SUBR_NATIVE_COMPILEDP (obj)) ||
+       vector_marked_p (XVECTOR (obj));
       break;
 
     case Lisp_Cons:
@@ -7632,14 +7663,14 @@ N should be nonnegative.  */);
   static union Aligned_Lisp_Subr Swatch_gc_cons_threshold =
      {{{ PSEUDOVECTOR_FLAG | (PVEC_SUBR << PSEUDOVECTOR_AREA_BITS) },
        { .a4 = watch_gc_cons_threshold },
-       4, 4, "watch_gc_cons_threshold", 0, 0}};
+       4, 4, "watch_gc_cons_threshold", {0}, 0}};
   XSETSUBR (watcher, &Swatch_gc_cons_threshold.s);
   Fadd_variable_watcher (Qgc_cons_threshold, watcher);
 
   static union Aligned_Lisp_Subr Swatch_gc_cons_percentage =
      {{{ PSEUDOVECTOR_FLAG | (PVEC_SUBR << PSEUDOVECTOR_AREA_BITS) },
        { .a4 = watch_gc_cons_percentage },
-       4, 4, "watch_gc_cons_percentage", 0, 0}};
+       4, 4, "watch_gc_cons_percentage", {0}, 0}};
   XSETSUBR (watcher, &Swatch_gc_cons_percentage.s);
   Fadd_variable_watcher (Qgc_cons_percentage, watcher);
 }
diff --git a/src/callproc.c b/src/callproc.c
index cd0f67f..5aa2cba 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -457,7 +457,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int 
filefd,
     int ok;
 
     ok = openp (Vexec_path, args[0], Vexec_suffixes, &path,
-               make_fixnum (X_OK), false);
+               make_fixnum (X_OK), false, false);
     if (ok < 0)
       report_file_error ("Searching for program", args[0]);
   }
diff --git a/src/charset.c b/src/charset.c
index eb388d1..7cd0fa7 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -486,7 +486,7 @@ load_charset_map_from_file (struct charset *charset, 
Lisp_Object mapfile,
   ptrdiff_t count = SPECPDL_INDEX ();
   record_unwind_protect_nothing ();
   specbind (Qfile_name_handler_alist, Qnil);
-  fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false);
+  fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false);
   fp = fd < 0 ? 0 : fdopen (fd, "r");
   if (!fp)
     {
diff --git a/src/comp.c b/src/comp.c
new file mode 100644
index 0000000..5309be4
--- /dev/null
+++ b/src/comp.c
@@ -0,0 +1,5410 @@
+/* Compile elisp into native code.
+   Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+Author: Andrea Corallo <akrl@sdf.org>
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include "lisp.h"
+
+#ifdef HAVE_NATIVE_COMP
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <libgccjit.h>
+#include <epaths.h>
+
+#include "puresize.h"
+#include "window.h"
+#include "dynlib.h"
+#include "buffer.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "md5.h"
+#include "sysstdio.h"
+#include "zlib.h"
+
+
+/********************************/
+/* Dynamic loading of libgccjit */
+/********************************/
+
+#ifdef WINDOWSNT
+# include "w32common.h"
+
+#undef gcc_jit_block_add_assignment
+#undef gcc_jit_block_add_comment
+#undef gcc_jit_block_add_eval
+#undef gcc_jit_block_end_with_conditional
+#undef gcc_jit_block_end_with_jump
+#undef gcc_jit_block_end_with_return
+#undef gcc_jit_block_end_with_void_return
+#undef gcc_jit_context_acquire
+#undef gcc_jit_context_add_command_line_option
+#undef gcc_jit_context_add_driver_option
+#undef gcc_jit_context_compile_to_file
+#undef gcc_jit_context_dump_reproducer_to_file
+#undef gcc_jit_context_dump_to_file
+#undef gcc_jit_context_get_builtin_function
+#undef gcc_jit_context_get_first_error
+#undef gcc_jit_context_get_int_type
+#undef gcc_jit_context_get_type
+#undef gcc_jit_context_new_array_access
+#undef gcc_jit_context_new_array_type
+#undef gcc_jit_context_new_binary_op
+#undef gcc_jit_context_new_call
+#undef gcc_jit_context_new_call_through_ptr
+#undef gcc_jit_context_new_comparison
+#undef gcc_jit_context_new_field
+#undef gcc_jit_context_new_function
+#undef gcc_jit_context_new_function_ptr_type
+#undef gcc_jit_context_new_global
+#undef gcc_jit_context_new_opaque_struct
+#undef gcc_jit_context_new_param
+#undef gcc_jit_context_new_rvalue_from_int
+#undef gcc_jit_context_new_rvalue_from_long
+#undef gcc_jit_context_new_rvalue_from_ptr
+#undef gcc_jit_context_new_string_literal
+#undef gcc_jit_context_new_struct_type
+#undef gcc_jit_context_new_unary_op
+#undef gcc_jit_context_new_union_type
+#undef gcc_jit_context_release
+#undef gcc_jit_context_set_bool_option
+#undef gcc_jit_context_set_int_option
+#undef gcc_jit_context_set_logfile
+#undef gcc_jit_context_set_str_option
+#undef gcc_jit_function_get_param
+#undef gcc_jit_function_new_block
+#undef gcc_jit_function_new_local
+#undef gcc_jit_global_set_initializer
+#undef gcc_jit_lvalue_access_field
+#undef gcc_jit_lvalue_as_rvalue
+#undef gcc_jit_lvalue_get_address
+#undef gcc_jit_param_as_lvalue
+#undef gcc_jit_param_as_rvalue
+#undef gcc_jit_rvalue_access_field
+#undef gcc_jit_rvalue_dereference
+#undef gcc_jit_rvalue_dereference_field
+#undef gcc_jit_rvalue_get_type
+#undef gcc_jit_struct_as_type
+#undef gcc_jit_struct_set_fields
+#undef gcc_jit_type_get_const
+#undef gcc_jit_type_get_pointer
+#undef gcc_jit_version_major
+#undef gcc_jit_version_minor
+#undef gcc_jit_version_patchlevel
+
+/* In alphabetical order */
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_rvalue_from_int,
+            (gcc_jit_context *ctxt, gcc_jit_type *numeric_type, int value));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_lvalue_as_rvalue,
+            (gcc_jit_lvalue *lvalue));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_rvalue_access_field,
+            (gcc_jit_rvalue *struct_or_union, gcc_jit_location *loc,
+             gcc_jit_field *field));
+DEF_DLL_FN (void, gcc_jit_block_add_comment,
+            (gcc_jit_block *block, gcc_jit_location *loc, const char *text));
+DEF_DLL_FN (void, gcc_jit_context_release, (gcc_jit_context *ctxt));
+DEF_DLL_FN (const char *, gcc_jit_context_get_first_error,
+            (gcc_jit_context *ctxt));
+DEF_DLL_FN (gcc_jit_block *, gcc_jit_function_new_block,
+            (gcc_jit_function *func, const char *name));
+DEF_DLL_FN (gcc_jit_context *, gcc_jit_context_acquire, (void));
+DEF_DLL_FN (void, gcc_jit_context_add_command_line_option,
+            (gcc_jit_context *ctxt, const char *optname));
+DEF_DLL_FN (void, gcc_jit_context_add_driver_option,
+            (gcc_jit_context *ctxt, const char *optname));
+DEF_DLL_FN (gcc_jit_field *, gcc_jit_context_new_field,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, gcc_jit_type *type,
+             const char *name));
+DEF_DLL_FN (gcc_jit_function *, gcc_jit_context_get_builtin_function,
+            (gcc_jit_context *ctxt, const char *name));
+DEF_DLL_FN (gcc_jit_function *, gcc_jit_context_new_function,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             enum gcc_jit_function_kind kind, gcc_jit_type *return_type,
+             const char *name, int num_params, gcc_jit_param **params,
+             int is_variadic));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_context_new_array_access,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, gcc_jit_rvalue *ptr,
+             gcc_jit_rvalue *index));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_context_new_global,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             enum gcc_jit_global_kind kind, gcc_jit_type *type,
+             const char *name));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_function_new_local,
+            (gcc_jit_function *func, gcc_jit_location *loc, gcc_jit_type *type,
+             const char *name));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_global_set_initializer,
+           (gcc_jit_lvalue *global, const void *blob, size_t num_bytes));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_lvalue_access_field,
+            (gcc_jit_lvalue *struct_or_union, gcc_jit_location *loc,
+             gcc_jit_field *field));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_param_as_lvalue, (gcc_jit_param *param));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_rvalue_dereference,
+            (gcc_jit_rvalue *rvalue, gcc_jit_location *loc));
+DEF_DLL_FN (gcc_jit_lvalue *, gcc_jit_rvalue_dereference_field,
+            (gcc_jit_rvalue *ptr, gcc_jit_location *loc, gcc_jit_field 
*field));
+DEF_DLL_FN (gcc_jit_param *, gcc_jit_context_new_param,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, gcc_jit_type *type,
+             const char *name));
+DEF_DLL_FN (gcc_jit_param *, gcc_jit_function_get_param,
+            (gcc_jit_function *func, int index));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_binary_op,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             enum gcc_jit_binary_op op, gcc_jit_type *result_type,
+             gcc_jit_rvalue *a, gcc_jit_rvalue *b));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_call,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             gcc_jit_function *func, int numargs , gcc_jit_rvalue **args));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_call_through_ptr,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             gcc_jit_rvalue *fn_ptr, int numargs, gcc_jit_rvalue **args));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_comparison,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             enum gcc_jit_comparison op, gcc_jit_rvalue *a, gcc_jit_rvalue 
*b));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_rvalue_from_long,
+            (gcc_jit_context *ctxt, gcc_jit_type *numeric_type, long value));
+#if LISP_WORDS_ARE_POINTERS
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_rvalue_from_ptr,
+            (gcc_jit_context *ctxt, gcc_jit_type *pointer_type, void *value));
+#endif
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_string_literal,
+            (gcc_jit_context *ctxt, const char *value));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_unary_op,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             enum gcc_jit_unary_op op, gcc_jit_type *result_type,
+             gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_lvalue_get_address,
+            (gcc_jit_lvalue *lvalue, gcc_jit_location *loc));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_param_as_rvalue, (gcc_jit_param *param));
+DEF_DLL_FN (gcc_jit_struct *, gcc_jit_context_new_opaque_struct,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, const char *name));
+DEF_DLL_FN (gcc_jit_struct *, gcc_jit_context_new_struct_type,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, const char *name,
+             int num_fields, gcc_jit_field **fields));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_context_get_int_type,
+            (gcc_jit_context *ctxt, int num_bytes, int is_signed));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_context_get_type,
+            (gcc_jit_context *ctxt, enum gcc_jit_types type_));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_context_new_array_type,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             gcc_jit_type *element_type, int num_elements));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_context_new_function_ptr_type,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc,
+             gcc_jit_type *return_type, int num_params,
+             gcc_jit_type **param_types, int is_variadic));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_context_new_union_type,
+            (gcc_jit_context *ctxt, gcc_jit_location *loc, const char *name,
+             int num_fields, gcc_jit_field **fields));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_rvalue_get_type, (gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_struct_as_type,
+            (gcc_jit_struct *struct_type));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_get_const, (gcc_jit_type *type));
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_get_pointer, (gcc_jit_type *type));
+DEF_DLL_FN (void, gcc_jit_block_add_assignment,
+            (gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_lvalue 
*lvalue,
+             gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (void, gcc_jit_block_add_eval,
+            (gcc_jit_block *block, gcc_jit_location *loc,
+             gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (void, gcc_jit_block_end_with_conditional,
+            (gcc_jit_block *block, gcc_jit_location *loc,
+             gcc_jit_rvalue *boolval, gcc_jit_block *on_true,
+             gcc_jit_block *on_false));
+DEF_DLL_FN (void, gcc_jit_block_end_with_jump,
+            (gcc_jit_block *block, gcc_jit_location *loc,
+             gcc_jit_block *target));
+DEF_DLL_FN (void, gcc_jit_block_end_with_return,
+            (gcc_jit_block *block, gcc_jit_location *loc,
+             gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (void, gcc_jit_block_end_with_void_return,
+            (gcc_jit_block *block, gcc_jit_location *loc));
+DEF_DLL_FN (void, gcc_jit_context_compile_to_file,
+            (gcc_jit_context *ctxt, enum gcc_jit_output_kind output_kind,
+             const char *output_path));
+DEF_DLL_FN (void, gcc_jit_context_dump_reproducer_to_file,
+            (gcc_jit_context *ctxt, const char *path));
+DEF_DLL_FN (void, gcc_jit_context_dump_to_file,
+            (gcc_jit_context *ctxt, const char *path, int update_locations));
+DEF_DLL_FN (void, gcc_jit_context_set_bool_option,
+            (gcc_jit_context *ctxt, enum gcc_jit_bool_option opt, int value));
+DEF_DLL_FN (void, gcc_jit_context_set_int_option,
+            (gcc_jit_context *ctxt, enum gcc_jit_int_option opt, int value));
+DEF_DLL_FN (void, gcc_jit_context_set_logfile,
+            (gcc_jit_context *ctxt, FILE *logfile, int flags, int verbosity));
+DEF_DLL_FN (void, gcc_jit_context_set_str_option,
+           (gcc_jit_context *ctxt, enum gcc_jit_str_option opt,
+            const char *value));
+DEF_DLL_FN (void, gcc_jit_struct_set_fields,
+            (gcc_jit_struct *struct_type, gcc_jit_location *loc, int 
num_fields,
+             gcc_jit_field **fields));
+DEF_DLL_FN (int, gcc_jit_version_major, (void));
+DEF_DLL_FN (int, gcc_jit_version_minor, (void));
+DEF_DLL_FN (int, gcc_jit_version_patchlevel, (void));
+
+static bool
+init_gccjit_functions (void)
+{
+  HMODULE library = w32_delayed_load (Qgccjit);
+
+  if (!library)
+    return false;
+
+  /* In alphabetical order */
+  LOAD_DLL_FN (library, gcc_jit_block_add_assignment);
+  LOAD_DLL_FN (library, gcc_jit_block_add_comment);
+  LOAD_DLL_FN (library, gcc_jit_block_add_eval);
+  LOAD_DLL_FN (library, gcc_jit_block_end_with_conditional);
+  LOAD_DLL_FN (library, gcc_jit_block_end_with_jump);
+  LOAD_DLL_FN (library, gcc_jit_block_end_with_return);
+  LOAD_DLL_FN (library, gcc_jit_block_end_with_void_return);
+  LOAD_DLL_FN (library, gcc_jit_context_acquire);
+  LOAD_DLL_FN (library, gcc_jit_context_compile_to_file);
+  LOAD_DLL_FN (library, gcc_jit_context_dump_reproducer_to_file);
+  LOAD_DLL_FN (library, gcc_jit_context_dump_to_file);
+  LOAD_DLL_FN (library, gcc_jit_context_get_builtin_function);
+  LOAD_DLL_FN (library, gcc_jit_context_get_first_error);
+  LOAD_DLL_FN (library, gcc_jit_context_get_int_type);
+  LOAD_DLL_FN (library, gcc_jit_context_get_type);
+  LOAD_DLL_FN (library, gcc_jit_context_new_array_access);
+  LOAD_DLL_FN (library, gcc_jit_context_new_array_type);
+  LOAD_DLL_FN (library, gcc_jit_context_new_binary_op);
+  LOAD_DLL_FN (library, gcc_jit_context_new_call);
+  LOAD_DLL_FN (library, gcc_jit_context_new_call_through_ptr);
+  LOAD_DLL_FN (library, gcc_jit_context_new_comparison);
+  LOAD_DLL_FN (library, gcc_jit_context_new_field);
+  LOAD_DLL_FN (library, gcc_jit_context_new_function);
+  LOAD_DLL_FN (library, gcc_jit_context_new_function_ptr_type);
+  LOAD_DLL_FN (library, gcc_jit_context_new_global);
+  LOAD_DLL_FN (library, gcc_jit_context_new_opaque_struct);
+  LOAD_DLL_FN (library, gcc_jit_context_new_param);
+  LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_int);
+  LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_long);
+#if LISP_WORDS_ARE_POINTERS
+  LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_ptr);
+#endif
+  LOAD_DLL_FN (library, gcc_jit_context_new_string_literal);
+  LOAD_DLL_FN (library, gcc_jit_context_new_struct_type);
+  LOAD_DLL_FN (library, gcc_jit_context_new_unary_op);
+  LOAD_DLL_FN (library, gcc_jit_context_new_union_type);
+  LOAD_DLL_FN (library, gcc_jit_context_release);
+  LOAD_DLL_FN (library, gcc_jit_context_set_bool_option);
+  LOAD_DLL_FN (library, gcc_jit_context_set_int_option);
+  LOAD_DLL_FN (library, gcc_jit_context_set_logfile);
+  LOAD_DLL_FN (library, gcc_jit_context_set_str_option);
+  LOAD_DLL_FN (library, gcc_jit_function_get_param);
+  LOAD_DLL_FN (library, gcc_jit_function_new_block);
+  LOAD_DLL_FN (library, gcc_jit_function_new_local);
+  LOAD_DLL_FN (library, gcc_jit_lvalue_access_field);
+  LOAD_DLL_FN (library, gcc_jit_lvalue_as_rvalue);
+  LOAD_DLL_FN (library, gcc_jit_lvalue_get_address);
+  LOAD_DLL_FN (library, gcc_jit_param_as_lvalue);
+  LOAD_DLL_FN (library, gcc_jit_param_as_rvalue);
+  LOAD_DLL_FN (library, gcc_jit_rvalue_access_field);
+  LOAD_DLL_FN (library, gcc_jit_rvalue_dereference);
+  LOAD_DLL_FN (library, gcc_jit_rvalue_dereference_field);
+  LOAD_DLL_FN (library, gcc_jit_rvalue_get_type);
+  LOAD_DLL_FN (library, gcc_jit_struct_as_type);
+  LOAD_DLL_FN (library, gcc_jit_struct_set_fields);
+  LOAD_DLL_FN (library, gcc_jit_type_get_const);
+  LOAD_DLL_FN (library, gcc_jit_type_get_pointer);
+  LOAD_DLL_FN_OPT (library, gcc_jit_context_add_command_line_option);
+  LOAD_DLL_FN_OPT (library, gcc_jit_context_add_driver_option);
+  LOAD_DLL_FN_OPT (library, gcc_jit_global_set_initializer);
+  LOAD_DLL_FN_OPT (library, gcc_jit_version_major);
+  LOAD_DLL_FN_OPT (library, gcc_jit_version_minor);
+  LOAD_DLL_FN_OPT (library, gcc_jit_version_patchlevel);
+
+  return true;
+}
+
+/* In alphabetical order */
+#define gcc_jit_block_add_assignment fn_gcc_jit_block_add_assignment
+#define gcc_jit_block_add_comment fn_gcc_jit_block_add_comment
+#define gcc_jit_block_add_eval fn_gcc_jit_block_add_eval
+#define gcc_jit_block_end_with_conditional 
fn_gcc_jit_block_end_with_conditional
+#define gcc_jit_block_end_with_jump fn_gcc_jit_block_end_with_jump
+#define gcc_jit_block_end_with_return fn_gcc_jit_block_end_with_return
+#define gcc_jit_block_end_with_void_return 
fn_gcc_jit_block_end_with_void_return
+#define gcc_jit_context_acquire fn_gcc_jit_context_acquire
+#define gcc_jit_context_add_command_line_option 
fn_gcc_jit_context_add_command_line_option
+#define gcc_jit_context_add_driver_option fn_gcc_jit_context_add_driver_option
+#define gcc_jit_context_compile_to_file fn_gcc_jit_context_compile_to_file
+#define gcc_jit_context_dump_reproducer_to_file 
fn_gcc_jit_context_dump_reproducer_to_file
+#define gcc_jit_context_dump_to_file fn_gcc_jit_context_dump_to_file
+#define gcc_jit_context_get_builtin_function 
fn_gcc_jit_context_get_builtin_function
+#define gcc_jit_context_get_first_error fn_gcc_jit_context_get_first_error
+#define gcc_jit_context_get_int_type fn_gcc_jit_context_get_int_type
+#define gcc_jit_context_get_type fn_gcc_jit_context_get_type
+#define gcc_jit_context_new_array_access fn_gcc_jit_context_new_array_access
+#define gcc_jit_context_new_array_type fn_gcc_jit_context_new_array_type
+#define gcc_jit_context_new_binary_op fn_gcc_jit_context_new_binary_op
+#define gcc_jit_context_new_call fn_gcc_jit_context_new_call
+#define gcc_jit_context_new_call_through_ptr 
fn_gcc_jit_context_new_call_through_ptr
+#define gcc_jit_context_new_comparison fn_gcc_jit_context_new_comparison
+#define gcc_jit_context_new_field fn_gcc_jit_context_new_field
+#define gcc_jit_context_new_function fn_gcc_jit_context_new_function
+#define gcc_jit_context_new_function_ptr_type 
fn_gcc_jit_context_new_function_ptr_type
+#define gcc_jit_context_new_global fn_gcc_jit_context_new_global
+#define gcc_jit_context_new_opaque_struct fn_gcc_jit_context_new_opaque_struct
+#define gcc_jit_context_new_param fn_gcc_jit_context_new_param
+#define gcc_jit_context_new_rvalue_from_int 
fn_gcc_jit_context_new_rvalue_from_int
+#define gcc_jit_context_new_rvalue_from_long 
fn_gcc_jit_context_new_rvalue_from_long
+#if LISP_WORDS_ARE_POINTERS
+# define gcc_jit_context_new_rvalue_from_ptr 
fn_gcc_jit_context_new_rvalue_from_ptr
+#endif
+#define gcc_jit_context_new_string_literal 
fn_gcc_jit_context_new_string_literal
+#define gcc_jit_context_new_struct_type fn_gcc_jit_context_new_struct_type
+#define gcc_jit_context_new_unary_op fn_gcc_jit_context_new_unary_op
+#define gcc_jit_context_new_union_type fn_gcc_jit_context_new_union_type
+#define gcc_jit_context_release fn_gcc_jit_context_release
+#define gcc_jit_context_set_bool_option fn_gcc_jit_context_set_bool_option
+#define gcc_jit_context_set_int_option fn_gcc_jit_context_set_int_option
+#define gcc_jit_context_set_logfile fn_gcc_jit_context_set_logfile
+#define gcc_jit_context_set_str_option fn_gcc_jit_context_set_str_option
+#define gcc_jit_function_get_param fn_gcc_jit_function_get_param
+#define gcc_jit_function_new_block fn_gcc_jit_function_new_block
+#define gcc_jit_function_new_local fn_gcc_jit_function_new_local
+#define gcc_jit_global_set_initializer fn_gcc_jit_global_set_initializer
+#define gcc_jit_lvalue_access_field fn_gcc_jit_lvalue_access_field
+#define gcc_jit_lvalue_as_rvalue fn_gcc_jit_lvalue_as_rvalue
+#define gcc_jit_lvalue_get_address fn_gcc_jit_lvalue_get_address
+#define gcc_jit_param_as_lvalue fn_gcc_jit_param_as_lvalue
+#define gcc_jit_param_as_rvalue fn_gcc_jit_param_as_rvalue
+#define gcc_jit_rvalue_access_field fn_gcc_jit_rvalue_access_field
+#define gcc_jit_rvalue_dereference fn_gcc_jit_rvalue_dereference
+#define gcc_jit_rvalue_dereference_field fn_gcc_jit_rvalue_dereference_field
+#define gcc_jit_rvalue_get_type fn_gcc_jit_rvalue_get_type
+#define gcc_jit_struct_as_type fn_gcc_jit_struct_as_type
+#define gcc_jit_struct_set_fields fn_gcc_jit_struct_set_fields
+#define gcc_jit_type_get_const fn_gcc_jit_type_get_const
+#define gcc_jit_type_get_pointer fn_gcc_jit_type_get_pointer
+#define gcc_jit_version_major fn_gcc_jit_version_major
+#define gcc_jit_version_minor fn_gcc_jit_version_minor
+#define gcc_jit_version_patchlevel fn_gcc_jit_version_patchlevel
+
+#endif
+
+static bool
+load_gccjit_if_necessary (bool mandatory)
+{
+#ifdef WINDOWSNT
+  static bool tried_to_initialize_once;
+  static bool gccjit_initialized;
+
+  if (!tried_to_initialize_once)
+    {
+      tried_to_initialize_once = true;
+      Lisp_Object status;
+      gccjit_initialized = init_gccjit_functions ();
+      status = gccjit_initialized ? Qt : Qnil;
+      Vlibrary_cache = Fcons (Fcons (Qgccjit, status), Vlibrary_cache);
+    }
+
+  if (mandatory && !gccjit_initialized)
+    xsignal1 (Qnative_compiler_error, build_string ("libgccjit not found"));
+
+  return gccjit_initialized;
+#else
+  return true;
+#endif
+}
+
+
+/* Increase this number to force a new Vcomp_abi_hash to be generated.  */
+#define ABI_VERSION "4"
+
+/* Length of the hashes used for eln file naming.  */
+#define HASH_LENGTH 8
+
+/* C symbols emitted for the load relocation mechanism.  */
+#define CURRENT_THREAD_RELOC_SYM "current_thread_reloc"
+#define PURE_RELOC_SYM "pure_reloc"
+#define DATA_RELOC_SYM "d_reloc"
+#define DATA_RELOC_IMPURE_SYM "d_reloc_imp"
+#define DATA_RELOC_EPHEMERAL_SYM "d_reloc_eph"
+
+#define FUNC_LINK_TABLE_SYM "freloc_link_table"
+#define LINK_TABLE_HASH_SYM "freloc_hash"
+#define COMP_UNIT_SYM "comp_unit"
+#define TEXT_DATA_RELOC_SYM "text_data_reloc"
+#define TEXT_DATA_RELOC_IMPURE_SYM "text_data_reloc_imp"
+#define TEXT_DATA_RELOC_EPHEMERAL_SYM "text_data_reloc_eph"
+
+#define TEXT_OPTIM_QLY_SYM "text_optim_qly"
+#define TEXT_FDOC_SYM "text_data_fdoc"
+
+#define STR_VALUE(s) #s
+#define STR(s) STR_VALUE (s)
+
+#define FIRST(x)                               \
+  XCAR(x)
+#define SECOND(x)                              \
+  XCAR (XCDR (x))
+#define THIRD(x)                               \
+  XCAR (XCDR (XCDR (x)))
+
+/* Like call1 but stringify and intern.  */
+#define CALL1I(fun, arg)                               \
+  CALLN (Ffuncall, intern_c_string (STR (fun)), arg)
+
+/* Like call2 but stringify and intern.  */
+#define CALL2I(fun, arg1, arg2)                                \
+  CALLN (Ffuncall, intern_c_string (STR (fun)), arg1, arg2)
+
+#define DECL_BLOCK(name, func)                         \
+  gcc_jit_block *(name) =                              \
+    gcc_jit_function_new_block ((func), STR (name))
+
+#ifndef WINDOWSNT
+# ifdef HAVE__SETJMP
+#  define SETJMP _setjmp
+# else
+#  define SETJMP setjmp
+# endif
+#else
+/* snippet from MINGW-64 setjmp.h */
+# define SETJMP _setjmp
+#endif
+#define SETJMP_NAME SETJMP
+
+/* Max number function importable by native compiled code.  */
+#define F_RELOC_MAX_SIZE 1500
+
+typedef struct {
+  void *link_table[F_RELOC_MAX_SIZE];
+  ptrdiff_t size;
+} f_reloc_t;
+
+sigset_t saved_sigset;
+
+static f_reloc_t freloc;
+
+#define NUM_CAST_TYPES 15
+
+enum cast_kind_of_type
+  {
+    kind_unsigned,
+    kind_signed,
+    kind_pointer
+  };
+
+typedef struct {
+  EMACS_INT len;
+  gcc_jit_rvalue *r_val;
+} reloc_array_t;
+
+/* C side of the compiler context.  */
+
+typedef struct {
+  EMACS_INT speed;
+  EMACS_INT debug;
+  Lisp_Object driver_options;
+  gcc_jit_context *ctxt;
+  gcc_jit_type *void_type;
+  gcc_jit_type *bool_type;
+  gcc_jit_type *char_type;
+  gcc_jit_type *int_type;
+  gcc_jit_type *unsigned_type;
+  gcc_jit_type *long_type;
+  gcc_jit_type *unsigned_long_type;
+  gcc_jit_type *long_long_type;
+  gcc_jit_type *unsigned_long_long_type;
+  gcc_jit_type *emacs_int_type;
+  gcc_jit_type *emacs_uint_type;
+  gcc_jit_type *void_ptr_type;
+  gcc_jit_type *char_ptr_type;
+  gcc_jit_type *ptrdiff_type;
+  gcc_jit_type *uintptr_type;
+  gcc_jit_type *size_t_type;
+  gcc_jit_type *lisp_word_type;
+  gcc_jit_type *lisp_word_tag_type;
+#ifdef LISP_OBJECT_IS_STRUCT
+  gcc_jit_field *lisp_obj_i;
+  gcc_jit_struct *lisp_obj_s;
+#endif
+  gcc_jit_type *lisp_obj_type;
+  gcc_jit_type *lisp_obj_ptr_type;
+  /* struct Lisp_Cons */
+  gcc_jit_struct *lisp_cons_s;
+  gcc_jit_field *lisp_cons_u;
+  gcc_jit_field *lisp_cons_u_s;
+  gcc_jit_field *lisp_cons_u_s_car;
+  gcc_jit_field *lisp_cons_u_s_u;
+  gcc_jit_field *lisp_cons_u_s_u_cdr;
+  gcc_jit_type *lisp_cons_type;
+  gcc_jit_type *lisp_cons_ptr_type;
+  /* struct jmp_buf.  */
+  gcc_jit_struct *jmp_buf_s;
+  /* struct handler.  */
+  gcc_jit_struct *handler_s;
+  gcc_jit_field *handler_jmp_field;
+  gcc_jit_field *handler_val_field;
+  gcc_jit_field *handler_next_field;
+  gcc_jit_type *handler_ptr_type;
+  gcc_jit_lvalue *loc_handler;
+  /* struct thread_state.  */
+  gcc_jit_struct *thread_state_s;
+  gcc_jit_field *m_handlerlist;
+  gcc_jit_type *thread_state_ptr_type;
+  gcc_jit_rvalue *current_thread_ref;
+  /* Other globals.  */
+  gcc_jit_rvalue *pure_ptr;
+  /* libgccjit has really limited support for casting therefore this union will
+     be used for the scope.  */
+  gcc_jit_type *cast_union_type;
+  gcc_jit_function *cast_functions_from_to[NUM_CAST_TYPES][NUM_CAST_TYPES];
+  /*  We add one to make space for the last member which is the "biggest_type"
+      member.  */
+  gcc_jit_type *cast_types[NUM_CAST_TYPES + 1];
+  size_t cast_type_sizes[NUM_CAST_TYPES + 1];
+  enum cast_kind_of_type cast_type_kind[NUM_CAST_TYPES + 1];
+  const char *cast_type_names[NUM_CAST_TYPES + 1];
+  gcc_jit_field *cast_union_fields[NUM_CAST_TYPES + 1];
+  size_t cast_union_field_biggest_type;
+  gcc_jit_function *func; /* Current function being compiled.  */
+  bool func_has_non_local; /* From comp-func has-non-local slot.  */
+  EMACS_INT func_speed; /* From comp-func speed slot.  */
+  gcc_jit_block *block;  /* Current basic block being compiled.  */
+  gcc_jit_lvalue *scratch; /* Used as scratch slot for some code sequence 
(switch).  */
+  ptrdiff_t frame_size; /* Size of the following array in elements. */
+  gcc_jit_lvalue **frame; /* Frame slot n -> gcc_jit_lvalue *.  */
+  gcc_jit_rvalue *zero;
+  gcc_jit_rvalue *one;
+  gcc_jit_rvalue *inttypebits;
+  gcc_jit_rvalue *lisp_int0;
+  gcc_jit_function *pseudovectorp;
+  gcc_jit_function *bool_to_lisp_obj;
+  gcc_jit_function *add1;
+  gcc_jit_function *sub1;
+  gcc_jit_function *negate;
+  gcc_jit_function *car;
+  gcc_jit_function *cdr;
+  gcc_jit_function *setcar;
+  gcc_jit_function *setcdr;
+  gcc_jit_function *check_type;
+  gcc_jit_function *check_impure;
+  gcc_jit_function *maybe_gc_or_quit;
+  Lisp_Object func_blocks_h; /* blk_name -> gcc_block.  */
+  Lisp_Object exported_funcs_h; /* c-func-name -> gcc_jit_function *.  */
+  Lisp_Object imported_funcs_h; /* subr_name -> gcc_jit_field *reloc_field.  */
+  Lisp_Object emitter_dispatcher;
+  /* Synthesized struct holding data relocs.  */
+  reloc_array_t data_relocs;
+  /* Same as before but can't go in pure space. */
+  reloc_array_t data_relocs_impure;
+  /* Same as before but content does not survive load phase. */
+  reloc_array_t data_relocs_ephemeral;
+  /* Global structure holding function relocations.  */
+  gcc_jit_lvalue *func_relocs;
+  gcc_jit_type *func_relocs_ptr_type;
+  /* Pointer to this structure local to each function.  */
+  gcc_jit_lvalue *func_relocs_local;
+  gcc_jit_function *memcpy;
+  Lisp_Object d_default_idx;
+  Lisp_Object d_impure_idx;
+  Lisp_Object d_ephemeral_idx;
+} comp_t;
+
+static comp_t comp;
+
+FILE *logfile = NULL;
+
+/* This is used for serialized objects by the reload mechanism.  */
+typedef struct {
+  ptrdiff_t len;
+  char data[];
+} static_obj_t;
+
+typedef struct {
+  reloc_array_t array;
+  gcc_jit_rvalue *idx;
+} imm_reloc_t;
+
+
+/*
+   Helper functions called by the run-time.
+*/
+
+void helper_unwind_protect (Lisp_Object handler);
+Lisp_Object helper_temp_output_buffer_setup (Lisp_Object x);
+Lisp_Object helper_unbind_n (Lisp_Object n);
+void helper_save_restriction (void);
+bool helper_PSEUDOVECTOR_TYPEP_XUNTAG (Lisp_Object a, enum pvec_type code);
+
+void *helper_link_table[] =
+  { wrong_type_argument,
+    helper_PSEUDOVECTOR_TYPEP_XUNTAG,
+    pure_write_error,
+    push_handler,
+    record_unwind_protect_excursion,
+    helper_unbind_n,
+    helper_save_restriction,
+    record_unwind_current_buffer,
+    set_internal,
+    helper_unwind_protect,
+    specbind,
+    maybe_gc,
+    maybe_quit };
+
+
+static char * ATTRIBUTE_FORMAT_PRINTF (1, 2)
+format_string (const char *format, ...)
+{
+  static char scratch_area[512];
+  va_list va;
+  va_start (va, format);
+  int res = vsnprintf (scratch_area, sizeof (scratch_area), format, va);
+  if (res >= sizeof (scratch_area))
+    {
+      scratch_area[sizeof (scratch_area) - 4] = '.';
+      scratch_area[sizeof (scratch_area) - 3] = '.';
+      scratch_area[sizeof (scratch_area) - 2] = '.';
+    }
+  va_end (va);
+  return scratch_area;
+}
+
+static Lisp_Object
+comp_hash_string (Lisp_Object string)
+{
+  Lisp_Object digest = make_uninit_string (MD5_DIGEST_SIZE * 2);
+  md5_buffer (SSDATA (string), SCHARS (string), SSDATA (digest));
+  hexbuf_digest (SSDATA (digest), SDATA (digest), MD5_DIGEST_SIZE);
+
+  return Fsubstring (digest, Qnil, make_fixnum (HASH_LENGTH));
+}
+
+static Lisp_Object
+comp_hash_source_file (Lisp_Object filename)
+{
+  /* Can't use Finsert_file_contents + Fbuffer_hash as this is called
+     by Fcomp_el_to_eln_filename too early during bootstrap.  */
+  bool is_gz = suffix_p (filename, ".gz");
+  Lisp_Object encoded_filename = ENCODE_FILE (filename);
+  FILE *f = emacs_fopen (SSDATA (encoded_filename), is_gz ? "rb" : "r");
+
+  if (!f)
+    report_file_error ("Opening source file", filename);
+
+  Lisp_Object digest = make_uninit_string (MD5_DIGEST_SIZE * 2);
+
+  int res = is_gz
+    ? md5_gz_stream (f, SSDATA (digest))
+    : md5_stream (f, SSDATA (digest));
+  fclose (f);
+
+  if (res)
+    xsignal2 (Qfile_notify_error, build_string ("hashing failed"), filename);
+
+  hexbuf_digest (SSDATA (digest), SSDATA (digest), MD5_DIGEST_SIZE);
+
+  return Fsubstring (digest, Qnil, make_fixnum (HASH_LENGTH));
+}
+
+DEFUN ("comp--subr-signature", Fcomp__subr_signature,
+       Scomp__subr_signature, 1, 1, 0,
+       doc: /* Support function to 'hash_native_abi'.
+For internal use.  */)
+  (Lisp_Object subr)
+{
+  return concat2 (Fsubr_name (subr),
+                 Fprin1_to_string (Fsubr_arity (subr), Qnil));
+}
+
+/* Produce a key hashing Vcomp_subr_list.  */
+
+void
+hash_native_abi (void)
+{
+  /* Check runs once.  */
+  eassert (NILP (Vcomp_abi_hash));
+
+  Vcomp_abi_hash =
+    comp_hash_string (
+      concat3 (build_string (ABI_VERSION),
+              concat3 (Vemacs_version, Vsystem_configuration,
+                       Vsystem_configuration_options),
+              Fmapconcat (intern_c_string ("comp--subr-signature"),
+                          Vcomp_subr_list, build_string (""))));
+  Vcomp_native_version_dir =
+    concat3 (Vemacs_version, build_string ("-"), Vcomp_abi_hash);
+}
+
+static void
+freloc_check_fill (void)
+{
+  if (freloc.size)
+    return;
+
+  eassert (!NILP (Vcomp_subr_list));
+
+  if (ARRAYELTS (helper_link_table) > F_RELOC_MAX_SIZE)
+    goto overflow;
+  memcpy (freloc.link_table, helper_link_table, sizeof (helper_link_table));
+  freloc.size = ARRAYELTS (helper_link_table);
+
+  Lisp_Object subr_l = Vcomp_subr_list;
+  FOR_EACH_TAIL (subr_l)
+    {
+      if (freloc.size == F_RELOC_MAX_SIZE)
+       goto overflow;
+      struct Lisp_Subr *subr = XSUBR (XCAR (subr_l));
+      freloc.link_table[freloc.size] = subr->function.a0;
+      freloc.size++;
+    }
+  return;
+
+ overflow:
+  fatal ("Overflowing function relocation table, increase F_RELOC_MAX_SIZE");
+}
+
+static void
+bcall0 (Lisp_Object f)
+{
+  Ffuncall (1, &f);
+}
+
+static gcc_jit_block *
+retrive_block (Lisp_Object block_name)
+{
+  Lisp_Object value = Fgethash (block_name, comp.func_blocks_h, Qnil);
+
+  if (NILP (value))
+    xsignal2 (Qnative_ice, build_string ("missing basic block"), block_name);
+
+  return (gcc_jit_block *) xmint_pointer (value);
+}
+
+static void
+declare_block (Lisp_Object block_name)
+{
+  char *name_str = SSDATA (SYMBOL_NAME (block_name));
+  gcc_jit_block *block = gcc_jit_function_new_block (comp.func, name_str);
+  Lisp_Object value = make_mint_ptr (block);
+
+  if (!NILP (Fgethash (block_name, comp.func_blocks_h, Qnil)))
+    xsignal1 (Qnative_ice, build_string ("double basic block declaration"));
+
+  Fputhash (block_name, value, comp.func_blocks_h);
+}
+
+static gcc_jit_lvalue *
+emit_mvar_lval (Lisp_Object mvar)
+{
+  Lisp_Object mvar_slot = CALL1I (comp-mvar-slot, mvar);
+
+  if (EQ (mvar_slot, Qscratch))
+    {
+      if (!comp.scratch)
+       comp.scratch = gcc_jit_function_new_local (comp.func,
+                                                  NULL,
+                                                  comp.lisp_obj_type,
+                                                  "scratch");
+      return comp.scratch;
+    }
+
+  EMACS_INT slot_n = XFIXNUM (mvar_slot);
+  eassert (slot_n < comp.frame_size);
+  return comp.frame[slot_n];
+}
+
+static void
+register_emitter (Lisp_Object key, void *func)
+{
+  Lisp_Object value = make_mint_ptr (func);
+  Fputhash (key, value, comp.emitter_dispatcher);
+}
+
+static imm_reloc_t
+obj_to_reloc (Lisp_Object obj)
+{
+  imm_reloc_t reloc;
+  Lisp_Object idx;
+
+  idx = Fgethash (obj, comp.d_default_idx, Qnil);
+  if (!NILP (idx)) {
+      reloc.array = comp.data_relocs;
+      goto found;
+  }
+
+  idx = Fgethash (obj, comp.d_impure_idx, Qnil);
+  if (!NILP (idx))
+    {
+      reloc.array = comp.data_relocs_impure;
+      goto found;
+    }
+
+  idx = Fgethash (obj, comp.d_ephemeral_idx, Qnil);
+  if (!NILP (idx))
+    {
+      reloc.array = comp.data_relocs_ephemeral;
+      goto found;
+    }
+
+  xsignal1 (Qnative_ice,
+           build_string ("cant't find data in relocation containers"));
+  assume (false);
+
+ found:
+  eassert (XFIXNUM (idx) < reloc.array.len);
+  if (!FIXNUMP (idx))
+    xsignal1 (Qnative_ice,
+             build_string ("inconsistent data relocation container"));
+  reloc.idx = gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                  comp.ptrdiff_type,
+                                                  XFIXNUM (idx));
+  return reloc;
+}
+
+static void
+emit_comment (const char *str)
+{
+  if (comp.debug)
+    gcc_jit_block_add_comment (comp.block,
+                              NULL,
+                              str);
+}
+
+/*
+  Declare an imported function.
+  When nargs is MANY (ptrdiff_t nargs, Lisp_Object *args) signature is assumed.
+  When types is NULL args are assumed to be all Lisp_Objects.
+*/
+static gcc_jit_field *
+declare_imported_func (Lisp_Object subr_sym, gcc_jit_type *ret_type,
+                      int nargs, gcc_jit_type **types)
+{
+  USE_SAFE_ALLOCA;
+  /* Don't want to declare the same function two times.  */
+  if (!NILP (Fgethash (subr_sym, comp.imported_funcs_h, Qnil)))
+    xsignal2 (Qnative_ice,
+             build_string ("unexpected double function declaration"),
+             subr_sym);
+
+  if (nargs == MANY)
+    {
+      nargs = 2;
+      types = SAFE_ALLOCA (nargs * sizeof (* types));
+      types[0] = comp.ptrdiff_type;
+      types[1] = comp.lisp_obj_ptr_type;
+    }
+  else if (nargs == UNEVALLED)
+    {
+      nargs = 1;
+      types = SAFE_ALLOCA (nargs * sizeof (* types));
+      types[0] = comp.lisp_obj_type;
+    }
+  else if (!types)
+    {
+      types = SAFE_ALLOCA (nargs * sizeof (* types));
+      for (ptrdiff_t i = 0; i < nargs; i++)
+       types[i] = comp.lisp_obj_type;
+    }
+
+  /* String containing the function ptr name.  */
+  Lisp_Object f_ptr_name =
+    CALLN (Ffuncall, intern_c_string ("comp-c-func-name"),
+          subr_sym, make_string ("R", 1));
+
+  gcc_jit_type *f_ptr_type =
+    gcc_jit_type_get_const (
+      gcc_jit_context_new_function_ptr_type (comp.ctxt,
+                                            NULL,
+                                            ret_type,
+                                            nargs,
+                                            types,
+                                            0));
+  gcc_jit_field *field =
+    gcc_jit_context_new_field (comp.ctxt,
+                              NULL,
+                              f_ptr_type,
+                              SSDATA (f_ptr_name));
+
+  Fputhash (subr_sym, make_mint_ptr (field), comp.imported_funcs_h);
+  SAFE_FREE ();
+  return field;
+}
+
+/* Emit calls fetching from existing declarations.  */
+
+static gcc_jit_rvalue *
+emit_call (Lisp_Object func, gcc_jit_type *ret_type, ptrdiff_t nargs,
+          gcc_jit_rvalue **args, bool direct)
+{
+  Lisp_Object gcc_func =
+    Fgethash (func,
+             direct ? comp.exported_funcs_h : comp.imported_funcs_h,
+             Qnil);
+
+  if (NILP (gcc_func))
+      xsignal2 (Qnative_ice,
+               build_string ("missing function declaration"),
+               func);
+
+  if (direct)
+    {
+      emit_comment (format_string ("direct call to: %s",
+                                  SSDATA (func)));
+      return gcc_jit_context_new_call (comp.ctxt,
+                                      NULL,
+                                      xmint_pointer (gcc_func),
+                                      nargs,
+                                      args);
+    }
+  else
+    {
+      /* Inline functions so far don't have a local variable for
+        function reloc table so we fall back to the global one.  Even
+        if this is not aesthetic calling into C from open-code is
+        always a fallback and therefore not be performance critical.
+        To fix this could think do the inline our-self without
+        relying on GCC. */
+      gcc_jit_lvalue *f_ptr =
+       gcc_jit_rvalue_dereference_field (
+         gcc_jit_lvalue_as_rvalue (comp.func_relocs_local
+                                   ? comp.func_relocs_local
+                                   : comp.func_relocs),
+         NULL,
+         (gcc_jit_field *) xmint_pointer (gcc_func));
+
+      if (!f_ptr)
+       xsignal2 (Qnative_ice,
+                 build_string ("missing function relocation"),
+                 func);
+      emit_comment (format_string ("calling subr: %s",
+                                  SSDATA (SYMBOL_NAME (func))));
+      return gcc_jit_context_new_call_through_ptr (comp.ctxt,
+                                                  NULL,
+                                                  gcc_jit_lvalue_as_rvalue 
(f_ptr),
+                                                  nargs,
+                                                  args);
+    }
+}
+
+static gcc_jit_rvalue *
+emit_call_ref (Lisp_Object func, ptrdiff_t nargs,
+              gcc_jit_lvalue *base_arg, bool direct)
+{
+  gcc_jit_rvalue *args[] =
+    { gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.ptrdiff_type,
+                                          nargs),
+      gcc_jit_lvalue_get_address (base_arg, NULL) };
+  return emit_call (func, comp.lisp_obj_type, 2, args, direct);
+}
+
+/* Close current basic block emitting a conditional.  */
+
+static void
+emit_cond_jump (gcc_jit_rvalue *test,
+               gcc_jit_block *then_target, gcc_jit_block *else_target)
+{
+  if (gcc_jit_rvalue_get_type (test) == comp.bool_type)
+    gcc_jit_block_end_with_conditional (comp.block,
+                                     NULL,
+                                     test,
+                                     then_target,
+                                     else_target);
+  else
+    /* In case test is not bool we do a logical negation to obtain a bool as
+       result.  */
+    gcc_jit_block_end_with_conditional (
+      comp.block,
+      NULL,
+      gcc_jit_context_new_unary_op (comp.ctxt,
+                                   NULL,
+                                   GCC_JIT_UNARY_OP_LOGICAL_NEGATE,
+                                   comp.bool_type,
+                                   test),
+      else_target,
+      then_target);
+
+}
+
+static int
+type_to_cast_index (gcc_jit_type * type)
+{
+  for (int i = 0; i < NUM_CAST_TYPES; ++i)
+    if (type == comp.cast_types[i])
+      return i;
+
+  xsignal1 (Qnative_ice, build_string ("unsupported cast"));
+}
+
+static gcc_jit_rvalue *
+emit_coerce (gcc_jit_type *new_type, gcc_jit_rvalue *obj)
+{
+  gcc_jit_type *old_type = gcc_jit_rvalue_get_type (obj);
+
+  if (new_type == old_type)
+    return obj;
+
+#ifdef LISP_OBJECT_IS_STRUCT
+  if (old_type == comp.lisp_obj_type)
+    {
+      gcc_jit_rvalue *lwordobj =
+        gcc_jit_rvalue_access_field (obj, NULL, comp.lisp_obj_i);
+      return emit_coerce (new_type, lwordobj);
+    }
+
+  if (new_type == comp.lisp_obj_type)
+    {
+      gcc_jit_rvalue *lwordobj =
+        emit_coerce (comp.lisp_word_type, obj);
+
+      static ptrdiff_t i;
+      gcc_jit_lvalue *tmp_s =
+       gcc_jit_function_new_local (comp.func, NULL, comp.lisp_obj_type,
+                                   format_string ("lisp_obj_%td", i++));
+
+      gcc_jit_block_add_assignment (
+       comp.block, NULL,
+       gcc_jit_lvalue_access_field (tmp_s, NULL,
+                                    comp.lisp_obj_i),
+       lwordobj);
+      return gcc_jit_lvalue_as_rvalue (tmp_s);
+    }
+#endif
+
+  int old_index = type_to_cast_index (old_type);
+  int new_index = type_to_cast_index (new_type);
+
+  if (comp.cast_type_sizes[old_index] < comp.cast_type_sizes[new_index]
+      && comp.cast_type_kind[new_index] == kind_signed)
+    xsignal3 (Qnative_ice,
+              build_string ("FIXME: sign extension not implemented"),
+              build_string (comp.cast_type_names[old_index]),
+              build_string (comp.cast_type_names[new_index]));
+
+  /* Lookup the appropriate cast function in the cast matrix.  */
+  return gcc_jit_context_new_call (comp.ctxt,
+           NULL,
+           comp.cast_functions_from_to[old_index][new_index],
+           1, &obj);
+}
+
+static gcc_jit_rvalue *
+emit_binary_op (enum gcc_jit_binary_op op,
+               gcc_jit_type *result_type,
+               gcc_jit_rvalue *a, gcc_jit_rvalue *b)
+{
+  /* FIXME Check here for possible UB.  */
+  return gcc_jit_context_new_binary_op (comp.ctxt, NULL,
+                                       op,
+                                       result_type,
+                                       emit_coerce (result_type, a),
+                                       emit_coerce (result_type, b));
+}
+
+/* Should come with libgccjit.  */
+
+static gcc_jit_rvalue *
+emit_rvalue_from_long_long (gcc_jit_type *type, long long n)
+{
+  emit_comment (format_string ("emit long long: %lld", n));
+
+  gcc_jit_rvalue *high =
+    gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                         comp.unsigned_long_long_type,
+                                         (unsigned long long)n >> 32);
+  gcc_jit_rvalue *low =
+    emit_binary_op (GCC_JIT_BINARY_OP_RSHIFT,
+                   comp.unsigned_long_long_type,
+                   emit_binary_op (GCC_JIT_BINARY_OP_LSHIFT,
+                                   comp.unsigned_long_long_type,
+                                   gcc_jit_context_new_rvalue_from_long (
+                                     comp.ctxt,
+                                     comp.unsigned_long_long_type,
+                                     n),
+                                   gcc_jit_context_new_rvalue_from_int (
+                                     comp.ctxt,
+                                     comp.unsigned_long_long_type,
+                                     32)),
+                   gcc_jit_context_new_rvalue_from_int (
+                     comp.ctxt,
+                     comp.unsigned_long_long_type,
+                     32));
+
+  return
+    emit_coerce (type,
+      emit_binary_op (
+       GCC_JIT_BINARY_OP_BITWISE_OR,
+       comp.unsigned_long_long_type,
+       emit_binary_op (
+         GCC_JIT_BINARY_OP_LSHIFT,
+         comp.unsigned_long_long_type,
+         high,
+         gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                              comp.unsigned_long_long_type,
+                                              32)),
+       low));
+}
+
+static gcc_jit_rvalue *
+emit_rvalue_from_emacs_uint (EMACS_UINT val)
+{
+#ifdef WIDE_EMACS_INT
+  if (val > ULONG_MAX)
+    return emit_rvalue_from_long_long (comp.emacs_uint_type, val);
+#endif
+  return gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                              comp.emacs_uint_type,
+                                              val);
+}
+
+static gcc_jit_rvalue *
+emit_rvalue_from_emacs_int (EMACS_INT val)
+{
+  if (val > LONG_MAX || val < LONG_MIN)
+    return emit_rvalue_from_long_long (comp.emacs_int_type, val);
+  else
+    return gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                                comp.emacs_int_type, val);
+}
+
+static gcc_jit_rvalue *
+emit_rvalue_from_lisp_word_tag (Lisp_Word_tag val)
+{
+#ifdef WIDE_EMACS_INT
+  if (val > ULONG_MAX)
+    return emit_rvalue_from_long_long (comp.lisp_word_tag_type, val);
+#endif
+  return gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                              comp.lisp_word_tag_type,
+                                              val);
+}
+
+static gcc_jit_rvalue *
+emit_rvalue_from_lisp_word (Lisp_Word val)
+{
+#if LISP_WORDS_ARE_POINTERS
+  return gcc_jit_context_new_rvalue_from_ptr (comp.ctxt,
+                                              comp.lisp_word_type,
+                                              val);
+#else
+  if (val > LONG_MAX || val < LONG_MIN)
+    return emit_rvalue_from_long_long (comp.lisp_word_type, val);
+  else
+    return gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                                comp.lisp_word_type,
+                                                val);
+#endif
+}
+
+static gcc_jit_rvalue *
+emit_rvalue_from_lisp_obj (Lisp_Object obj)
+{
+#ifdef LISP_OBJECT_IS_STRUCT
+  return emit_coerce (comp.lisp_obj_type,
+                      emit_rvalue_from_lisp_word (obj.i));
+#else
+  return emit_rvalue_from_lisp_word (obj);
+#endif
+}
+
+/*
+   Emit the equivalent of:
+   (typeof_ptr) ((uintptr) ptr + size_of_ptr_ref * i)
+*/
+
+static gcc_jit_rvalue *
+emit_ptr_arithmetic (gcc_jit_rvalue *ptr, gcc_jit_type *ptr_type,
+                    int size_of_ptr_ref, gcc_jit_rvalue *i)
+{
+  emit_comment ("ptr_arithmetic");
+
+  gcc_jit_rvalue *offset =
+    emit_binary_op (
+      GCC_JIT_BINARY_OP_MULT,
+      comp.uintptr_type,
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.uintptr_type,
+                                          size_of_ptr_ref),
+       i);
+
+  return
+    emit_coerce (
+      ptr_type,
+      emit_binary_op (
+       GCC_JIT_BINARY_OP_PLUS,
+       comp.uintptr_type,
+       ptr,
+       offset));
+}
+
+static gcc_jit_rvalue *
+emit_XLI (gcc_jit_rvalue *obj)
+{
+  emit_comment ("XLI");
+  return emit_coerce (comp.emacs_int_type, obj);
+}
+
+static gcc_jit_rvalue *
+emit_XLP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("XLP");
+
+  return emit_coerce (comp.void_ptr_type, obj);
+}
+
+static gcc_jit_rvalue *
+emit_XUNTAG (gcc_jit_rvalue *a, gcc_jit_type *type, Lisp_Word_tag 
lisp_word_tag)
+{
+  /* #define XUNTAG(a, type, ctype) ((ctype *)
+     ((char *) XLP (a) - LISP_WORD_TAG (type))) */
+  emit_comment ("XUNTAG");
+
+  return emit_coerce (
+          gcc_jit_type_get_pointer (type),
+          emit_binary_op (
+            GCC_JIT_BINARY_OP_MINUS,
+            comp.uintptr_type,
+            emit_XLP (a),
+            emit_rvalue_from_lisp_word_tag (lisp_word_tag)));
+}
+
+static gcc_jit_rvalue *
+emit_XCONS (gcc_jit_rvalue *a)
+{
+  emit_comment ("XCONS");
+
+  return emit_XUNTAG (a,
+                     gcc_jit_struct_as_type (comp.lisp_cons_s),
+                     LISP_WORD_TAG (Lisp_Cons));
+}
+
+static gcc_jit_rvalue *
+emit_EQ (gcc_jit_rvalue *x, gcc_jit_rvalue *y)
+{
+  emit_comment ("EQ");
+
+  return gcc_jit_context_new_comparison (
+          comp.ctxt,
+          NULL,
+          GCC_JIT_COMPARISON_EQ,
+          emit_XLI (x),
+          emit_XLI (y));
+}
+
+static gcc_jit_rvalue *
+emit_TAGGEDP (gcc_jit_rvalue *obj, Lisp_Word_tag tag)
+{
+   /* (! (((unsigned) (XLI (a) >> (USE_LSB_TAG ? 0 : VALBITS)) \
+       - (unsigned) (tag)) \
+       & ((1 << GCTYPEBITS) - 1))) */
+  emit_comment ("TAGGEDP");
+
+  gcc_jit_rvalue *sh_res =
+    emit_binary_op (
+      GCC_JIT_BINARY_OP_RSHIFT,
+      comp.emacs_int_type,
+      emit_XLI (obj),
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.emacs_int_type,
+                                          (USE_LSB_TAG ? 0 : VALBITS)));
+
+  gcc_jit_rvalue *minus_res =
+    emit_binary_op (
+      GCC_JIT_BINARY_OP_MINUS,
+          comp.unsigned_type,
+          sh_res,
+          gcc_jit_context_new_rvalue_from_int (
+            comp.ctxt,
+            comp.unsigned_type,
+            tag));
+
+  gcc_jit_rvalue *res =
+   gcc_jit_context_new_unary_op (
+     comp.ctxt,
+     NULL,
+     GCC_JIT_UNARY_OP_LOGICAL_NEGATE,
+     comp.int_type,
+     emit_binary_op (
+       GCC_JIT_BINARY_OP_BITWISE_AND,
+       comp.unsigned_type,
+       minus_res,
+       gcc_jit_context_new_rvalue_from_int (
+        comp.ctxt,
+        comp.unsigned_type,
+        ((1 << GCTYPEBITS) - 1))));
+
+  return res;
+}
+
+static gcc_jit_rvalue *
+emit_VECTORLIKEP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("VECTORLIKEP");
+
+  return emit_TAGGEDP (obj, Lisp_Vectorlike);
+}
+
+static gcc_jit_rvalue *
+emit_CONSP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("CONSP");
+
+  return emit_TAGGEDP (obj, Lisp_Cons);
+}
+
+static gcc_jit_rvalue *
+emit_FLOATP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("FLOATP");
+
+  return emit_TAGGEDP (obj, Lisp_Float);
+}
+
+static gcc_jit_rvalue *
+emit_BIGNUMP (gcc_jit_rvalue *obj)
+{
+  /* PSEUDOVECTORP (x, PVEC_BIGNUM); */
+  emit_comment ("BIGNUMP");
+
+  gcc_jit_rvalue *args[] =
+    { obj,
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.int_type,
+                                          PVEC_BIGNUM) };
+
+  return gcc_jit_context_new_call (comp.ctxt,
+                                  NULL,
+                                  comp.pseudovectorp,
+                                  2,
+                                  args);
+}
+
+static gcc_jit_rvalue *
+emit_FIXNUMP (gcc_jit_rvalue *obj)
+{
+  /* (! (((unsigned) (XLI (x) >> (USE_LSB_TAG ? 0 : FIXNUM_BITS))
+       - (unsigned) (Lisp_Int0 >> !USE_LSB_TAG))
+       & ((1 << INTTYPEBITS) - 1)))  */
+  emit_comment ("FIXNUMP");
+
+  gcc_jit_rvalue *sh_res =
+    USE_LSB_TAG ? obj
+    : emit_binary_op (GCC_JIT_BINARY_OP_RSHIFT,
+                     comp.emacs_int_type,
+                     emit_XLI (obj),
+                     gcc_jit_context_new_rvalue_from_int (
+                       comp.ctxt,
+                       comp.emacs_int_type,
+                       FIXNUM_BITS));
+
+  gcc_jit_rvalue *minus_res =
+    emit_binary_op (
+      GCC_JIT_BINARY_OP_MINUS,
+          comp.unsigned_type,
+          sh_res,
+          gcc_jit_context_new_rvalue_from_int (
+            comp.ctxt,
+            comp.unsigned_type,
+            (Lisp_Int0 >> !USE_LSB_TAG)));
+
+  gcc_jit_rvalue *res =
+   gcc_jit_context_new_unary_op (
+     comp.ctxt,
+     NULL,
+     GCC_JIT_UNARY_OP_LOGICAL_NEGATE,
+     comp.int_type,
+     emit_binary_op (
+       GCC_JIT_BINARY_OP_BITWISE_AND,
+       comp.unsigned_type,
+       minus_res,
+       gcc_jit_context_new_rvalue_from_int (
+        comp.ctxt,
+        comp.unsigned_type,
+        ((1 << INTTYPEBITS) - 1))));
+
+  return res;
+}
+
+static gcc_jit_rvalue *
+emit_XFIXNUM (gcc_jit_rvalue *obj)
+{
+  emit_comment ("XFIXNUM");
+  gcc_jit_rvalue *i = emit_coerce (comp.emacs_uint_type, emit_XLI (obj));
+
+  /* FIXME: Implementation dependent (both RSHIFT are arithmetic).  */
+
+  if (!USE_LSB_TAG)
+    {
+      i = emit_binary_op (GCC_JIT_BINARY_OP_LSHIFT,
+                         comp.emacs_uint_type,
+                         i,
+                         comp.inttypebits);
+
+      return emit_binary_op (GCC_JIT_BINARY_OP_RSHIFT,
+                            comp.emacs_int_type,
+                            i,
+                            comp.inttypebits);
+    }
+  else
+    return emit_coerce (comp.emacs_int_type,
+                       emit_binary_op (GCC_JIT_BINARY_OP_RSHIFT,
+                                       comp.emacs_int_type,
+                                       i,
+                                       comp.inttypebits));
+}
+
+static gcc_jit_rvalue *
+emit_INTEGERP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("INTEGERP");
+
+  return emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_OR,
+                        comp.bool_type,
+                        emit_FIXNUMP (obj),
+                        emit_BIGNUMP (obj));
+}
+
+static gcc_jit_rvalue *
+emit_NUMBERP (gcc_jit_rvalue *obj)
+{
+  emit_comment ("NUMBERP");
+
+  return emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_OR,
+                        comp.bool_type,
+                        emit_INTEGERP (obj),
+                        emit_FLOATP (obj));
+}
+
+static gcc_jit_rvalue *
+emit_make_fixnum_LSB_TAG (gcc_jit_rvalue *n)
+{
+  /*
+    EMACS_UINT u = n;
+    n = u << INTTYPEBITS;
+    n += int0;
+  */
+
+  gcc_jit_rvalue *tmp =
+    emit_binary_op (GCC_JIT_BINARY_OP_LSHIFT,
+                   comp.emacs_int_type,
+                   n, comp.inttypebits);
+
+  tmp = emit_binary_op (GCC_JIT_BINARY_OP_PLUS,
+                       comp.emacs_int_type,
+                       tmp, comp.lisp_int0);
+
+  return emit_coerce (comp.lisp_obj_type, tmp);
+}
+
+static gcc_jit_rvalue *
+emit_make_fixnum_MSB_TAG (gcc_jit_rvalue *n)
+{
+  /*
+    n &= INTMASK;
+    n += (int0 << VALBITS);
+    return XIL (n);
+  */
+
+  gcc_jit_rvalue *intmask = emit_rvalue_from_emacs_uint (INTMASK);
+
+  n = emit_binary_op (GCC_JIT_BINARY_OP_BITWISE_AND,
+                     comp.emacs_uint_type,
+                     intmask, n);
+
+  n =
+    emit_binary_op (GCC_JIT_BINARY_OP_PLUS,
+                   comp.emacs_uint_type,
+                   emit_binary_op (GCC_JIT_BINARY_OP_LSHIFT,
+                                   comp.emacs_uint_type,
+                                   comp.lisp_int0,
+                                    emit_rvalue_from_emacs_uint (VALBITS)),
+                   n);
+
+  return emit_coerce (comp.lisp_obj_type, n);
+}
+
+
+static gcc_jit_rvalue *
+emit_make_fixnum (gcc_jit_rvalue *obj)
+{
+  emit_comment ("make_fixnum");
+  return USE_LSB_TAG
+    ? emit_make_fixnum_LSB_TAG (obj)
+    : emit_make_fixnum_MSB_TAG (obj);
+}
+
+static gcc_jit_lvalue *
+emit_lisp_obj_reloc_lval (Lisp_Object obj)
+{
+  emit_comment (format_string ("l-value for lisp obj: %s",
+                              SSDATA (Fprin1_to_string (obj, Qnil))));
+
+  imm_reloc_t reloc = obj_to_reloc (obj);
+  return gcc_jit_context_new_array_access (comp.ctxt,
+                                          NULL,
+                                          reloc.array.r_val,
+                                          reloc.idx);
+}
+
+static gcc_jit_rvalue *
+emit_lisp_obj_rval (Lisp_Object obj)
+{
+  emit_comment (format_string ("const lisp obj: %s",
+                              SSDATA (Fprin1_to_string (obj, Qnil))));
+
+  if (EQ (obj, Qnil))
+    {
+      gcc_jit_rvalue *n;
+      n = emit_rvalue_from_lisp_word ((Lisp_Word) iQnil);
+      return emit_coerce (comp.lisp_obj_type, n);
+    }
+
+  return gcc_jit_lvalue_as_rvalue (emit_lisp_obj_reloc_lval (obj));
+}
+
+static gcc_jit_rvalue *
+emit_NILP (gcc_jit_rvalue *x)
+{
+  emit_comment ("NILP");
+  return emit_EQ (x, emit_lisp_obj_rval (Qnil));
+}
+
+static gcc_jit_rvalue *
+emit_XCAR (gcc_jit_rvalue *c)
+{
+  emit_comment ("XCAR");
+
+  /* XCONS (c)->u.s.car */
+  return
+    gcc_jit_rvalue_access_field (
+      /* XCONS (c)->u.s */
+      gcc_jit_rvalue_access_field (
+       /* XCONS (c)->u */
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_rvalue_dereference_field (
+           emit_XCONS (c),
+           NULL,
+           comp.lisp_cons_u)),
+       NULL,
+       comp.lisp_cons_u_s),
+      NULL,
+      comp.lisp_cons_u_s_car);
+}
+
+static gcc_jit_lvalue *
+emit_lval_XCAR (gcc_jit_rvalue *c)
+{
+  emit_comment ("lval_XCAR");
+
+  /* XCONS (c)->u.s.car */
+  return
+    gcc_jit_lvalue_access_field (
+      /* XCONS (c)->u.s */
+      gcc_jit_lvalue_access_field (
+       /* XCONS (c)->u */
+       gcc_jit_rvalue_dereference_field (
+         emit_XCONS (c),
+         NULL,
+         comp.lisp_cons_u),
+       NULL,
+       comp.lisp_cons_u_s),
+      NULL,
+      comp.lisp_cons_u_s_car);
+}
+
+static gcc_jit_rvalue *
+emit_XCDR (gcc_jit_rvalue *c)
+{
+  emit_comment ("XCDR");
+  /* XCONS (c)->u.s.u.cdr */
+  return
+    gcc_jit_rvalue_access_field (
+      /* XCONS (c)->u.s.u */
+      gcc_jit_rvalue_access_field (
+       /* XCONS (c)->u.s */
+       gcc_jit_rvalue_access_field (
+         /* XCONS (c)->u */
+         gcc_jit_lvalue_as_rvalue (
+           gcc_jit_rvalue_dereference_field (
+             emit_XCONS (c),
+             NULL,
+             comp.lisp_cons_u)),
+         NULL,
+         comp.lisp_cons_u_s),
+       NULL,
+       comp.lisp_cons_u_s_u),
+      NULL,
+      comp.lisp_cons_u_s_u_cdr);
+}
+
+static gcc_jit_lvalue *
+emit_lval_XCDR (gcc_jit_rvalue *c)
+{
+  emit_comment ("lval_XCDR");
+
+  /* XCONS (c)->u.s.u.cdr */
+  return
+    gcc_jit_lvalue_access_field (
+      /* XCONS (c)->u.s.u */
+      gcc_jit_lvalue_access_field (
+       /* XCONS (c)->u.s */
+       gcc_jit_lvalue_access_field (
+         /* XCONS (c)->u */
+         gcc_jit_rvalue_dereference_field (
+           emit_XCONS (c),
+           NULL,
+           comp.lisp_cons_u),
+         NULL,
+         comp.lisp_cons_u_s),
+       NULL,
+       comp.lisp_cons_u_s_u),
+      NULL,
+      comp.lisp_cons_u_s_u_cdr);
+}
+
+static void
+emit_CHECK_CONS (gcc_jit_rvalue *x)
+{
+  emit_comment ("CHECK_CONS");
+
+  gcc_jit_rvalue *args[] =
+    { emit_CONSP (x),
+      emit_lisp_obj_rval (Qconsp),
+      x };
+
+  gcc_jit_block_add_eval (
+    comp.block,
+    NULL,
+    gcc_jit_context_new_call (comp.ctxt,
+                             NULL,
+                             comp.check_type,
+                             3,
+                             args));
+}
+
+static gcc_jit_rvalue *
+emit_car_addr (gcc_jit_rvalue *c)
+{
+  emit_comment ("car_addr");
+
+  return gcc_jit_lvalue_get_address (emit_lval_XCAR (c), NULL);
+}
+
+static gcc_jit_rvalue *
+emit_cdr_addr (gcc_jit_rvalue *c)
+{
+  emit_comment ("cdr_addr");
+
+  return gcc_jit_lvalue_get_address (emit_lval_XCDR (c), NULL);
+}
+
+static void
+emit_XSETCAR (gcc_jit_rvalue *c, gcc_jit_rvalue *n)
+{
+  emit_comment ("XSETCAR");
+
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    gcc_jit_rvalue_dereference (
+      emit_car_addr (c),
+      NULL),
+    n);
+}
+
+static void
+emit_XSETCDR (gcc_jit_rvalue *c, gcc_jit_rvalue *n)
+{
+  emit_comment ("XSETCDR");
+
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    gcc_jit_rvalue_dereference (
+      emit_cdr_addr (c),
+      NULL),
+    n);
+}
+
+static gcc_jit_rvalue *
+emit_PURE_P (gcc_jit_rvalue *ptr)
+{
+
+  emit_comment ("PURE_P");
+
+  return
+    gcc_jit_context_new_comparison (
+      comp.ctxt,
+      NULL,
+      GCC_JIT_COMPARISON_LE,
+      emit_binary_op (
+       GCC_JIT_BINARY_OP_MINUS,
+       comp.uintptr_type,
+       ptr,
+        comp.pure_ptr),
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.uintptr_type,
+                                          PURESIZE));
+}
+
+
+/*************************************/
+/* Code emitted by LIMPLE statemes.  */
+/*************************************/
+
+/* Emit an r-value from an mvar meta variable.
+   In case this is a constant that was propagated return it otherwise load it
+   from frame.  */
+
+static gcc_jit_rvalue *
+emit_mvar_rval (Lisp_Object mvar)
+{
+  Lisp_Object const_vld = CALL1I (comp-cstr-imm-vld-p, mvar);
+
+  if (!NILP (const_vld))
+    {
+      Lisp_Object value = CALL1I (comp-cstr-imm, mvar);
+      if (comp.debug > 1)
+       {
+         Lisp_Object func =
+           Fgethash (value,
+                     CALL1I (comp-ctxt-byte-func-to-func-h, Vcomp_ctxt),
+                     Qnil);
+
+         emit_comment (
+           SSDATA (
+             Fprin1_to_string (
+               NILP (func) ? value : CALL1I (comp-func-c-name, func),
+               Qnil)));
+       }
+      if (FIXNUMP (value))
+       {
+         /* We can still emit directly objects that are self-contained in a
+            word (read fixnums).  */
+          return emit_rvalue_from_lisp_obj (value);
+       }
+      /* Other const objects are fetched from the reloc array.  */
+      return emit_lisp_obj_rval (value);
+    }
+
+  return gcc_jit_lvalue_as_rvalue (emit_mvar_lval (mvar));
+}
+
+static void
+emit_frame_assignment (Lisp_Object dst_mvar, gcc_jit_rvalue *val)
+{
+
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    emit_mvar_lval (dst_mvar),
+    val);
+}
+
+static gcc_jit_rvalue *
+emit_set_internal (Lisp_Object args)
+{
+  /*
+    Ex: (set_internal #s(comp-mvar nil nil t comp-test-up-val nil nil)
+                      #s(comp-mvar 1 4 t nil symbol nil)).
+  */
+  /* TODO: Inline the most common case.  */
+  if (list_length (args) != 3)
+    xsignal2 (Qnative_ice,
+             build_string ("unexpected arg length for insns"),
+             args);
+
+  args = XCDR (args);
+  int i = 0;
+  gcc_jit_rvalue *gcc_args[4];
+  FOR_EACH_TAIL (args)
+    gcc_args[i++] = emit_mvar_rval (XCAR (args));
+  gcc_args[2] = emit_lisp_obj_rval (Qnil);
+  gcc_args[3] = gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                    comp.int_type,
+                                                    SET_INTERNAL_SET);
+  return emit_call (intern_c_string ("set_internal"), comp.void_type , 4,
+                   gcc_args, false);
+}
+
+/* This is for a regular function with arguments as m-var.  */
+
+static gcc_jit_rvalue *
+emit_simple_limple_call (Lisp_Object args, gcc_jit_type *ret_type, bool direct)
+{
+  USE_SAFE_ALLOCA;
+  int i = 0;
+  Lisp_Object callee = FIRST (args);
+  args = XCDR (args);
+  ptrdiff_t nargs = list_length (args);
+  gcc_jit_rvalue **gcc_args = SAFE_ALLOCA (nargs * sizeof (*gcc_args));
+  FOR_EACH_TAIL (args)
+    gcc_args[i++] = emit_mvar_rval (XCAR (args));
+
+  SAFE_FREE ();
+  return emit_call (callee, ret_type, nargs, gcc_args, direct);
+}
+
+static gcc_jit_rvalue *
+emit_simple_limple_call_lisp_ret (Lisp_Object args)
+{
+  /*
+    Ex: (call Fcons #s(comp-mvar 3 0 t 1 nil) #s(comp-mvar 4 nil t nil nil)).
+  */
+  return emit_simple_limple_call (args, comp.lisp_obj_type, false);
+}
+
+static gcc_jit_rvalue *
+emit_simple_limple_call_void_ret (Lisp_Object args)
+{
+  return emit_simple_limple_call (args, comp.void_type, false);
+}
+
+/* Entry point to dispatch emitting (call fun ...).  */
+
+static gcc_jit_rvalue *
+emit_limple_call (Lisp_Object insn)
+{
+  Lisp_Object callee_sym = FIRST (insn);
+  Lisp_Object emitter = Fgethash (callee_sym, comp.emitter_dispatcher, Qnil);
+
+  if (!NILP (emitter))
+    {
+      gcc_jit_rvalue * (* emitter_ptr) (Lisp_Object) = xmint_pointer (emitter);
+      return emitter_ptr (insn);
+    }
+
+  return emit_simple_limple_call_lisp_ret (insn);
+}
+
+static gcc_jit_rvalue *
+emit_limple_call_ref (Lisp_Object insn, bool direct)
+{
+  /* Ex: (funcall #s(comp-mvar 1 5 t eql symbol t)
+                  #s(comp-mvar 2 6 nil nil nil t)
+                 #s(comp-mvar 3 7 t 0 fixnum t)).  */
+  static int i = 0;
+  Lisp_Object callee = FIRST (insn);
+  EMACS_INT nargs = XFIXNUM (Flength (CDR (insn)));
+
+  if (!nargs)
+    return emit_call_ref (callee, 0, comp.frame[0], direct);
+
+  if (comp.func_has_non_local || !comp.func_speed)
+    {
+      /* FIXME: See bug#42360.  */
+      Lisp_Object first_arg = SECOND (insn);
+      EMACS_INT first_slot = XFIXNUM (CALL1I (comp-mvar-slot, first_arg));
+      return emit_call_ref (callee, nargs, comp.frame[first_slot], direct);
+    }
+
+  gcc_jit_lvalue *tmp_arr =
+    gcc_jit_function_new_local (
+      comp.func,
+      NULL,
+      gcc_jit_context_new_array_type (comp.ctxt,
+                                     NULL,
+                                     comp.lisp_obj_type,
+                                     nargs),
+      format_string ("call_arr_%d", i++));
+
+  ptrdiff_t j = 0;
+  Lisp_Object arg = CDR (insn);
+  FOR_EACH_TAIL (arg)
+    {
+      gcc_jit_block_add_assignment (
+        comp.block,
+       NULL,
+       gcc_jit_context_new_array_access (
+         comp.ctxt,
+         NULL,
+         gcc_jit_lvalue_as_rvalue (tmp_arr),
+         gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                              comp.int_type,
+                                              j)),
+       emit_mvar_rval (XCAR (arg)));
+      ++j;
+    }
+
+  return emit_call_ref (
+          callee,
+          nargs,
+          gcc_jit_context_new_array_access (comp.ctxt,
+                                            NULL,
+                                            gcc_jit_lvalue_as_rvalue (tmp_arr),
+                                            comp.zero),
+          direct);
+}
+
+static gcc_jit_rvalue *
+emit_setjmp (gcc_jit_rvalue *buf)
+{
+#ifndef WINDOWSNT
+  gcc_jit_rvalue *args[] = {buf};
+  gcc_jit_param *params[] =
+  {
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.void_ptr_type, "buf"),
+  };
+  /* Don't call setjmp through a function pointer (Bug#46824) */
+  gcc_jit_function *f =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_IMPORTED,
+                                 comp.int_type, STR (SETJMP_NAME),
+                                 ARRAYELTS (params), params,
+                                 false);
+
+  return gcc_jit_context_new_call (comp.ctxt, NULL, f, 1, args);
+#else
+  /* _setjmp (buf, __builtin_frame_address (0)) */
+  gcc_jit_param *params[] =
+  {
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.void_ptr_type, "buf"),
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.void_ptr_type, "frame"),
+  };
+  gcc_jit_rvalue *args[2];
+
+  args[0] =
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt, comp.unsigned_type, 0);
+
+  args[1] =
+    gcc_jit_context_new_call (
+      comp.ctxt,
+      NULL,
+      gcc_jit_context_get_builtin_function (comp.ctxt,
+                                           "__builtin_frame_address"),
+      1, args);
+  args[0] = buf;
+  gcc_jit_function *f =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_IMPORTED,
+                                 comp.int_type, STR (SETJMP_NAME),
+                                 ARRAYELTS (params), params,
+                                 false);
+
+  return gcc_jit_context_new_call (comp.ctxt, NULL, f, 2, args);
+#endif
+}
+
+/* Register an handler for a non local exit.  */
+
+static void
+emit_limple_push_handler (gcc_jit_rvalue *handler, gcc_jit_rvalue 
*handler_type,
+                         gcc_jit_block *handler_bb, gcc_jit_block *guarded_bb,
+                         Lisp_Object clobbered_mvar)
+{
+   /* struct handler *c = push_handler (POP, type);  */
+
+  gcc_jit_rvalue *args[] = { handler, handler_type };
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    comp.loc_handler,
+    emit_call (intern_c_string ("push_handler"),
+              comp.handler_ptr_type, 2, args, false));
+
+  args[0] =
+    gcc_jit_lvalue_get_address (
+       gcc_jit_rvalue_dereference_field (
+         gcc_jit_lvalue_as_rvalue (comp.loc_handler),
+         NULL,
+         comp.handler_jmp_field),
+       NULL);
+
+  gcc_jit_rvalue *res;
+  res = emit_setjmp (args[0]);
+  emit_cond_jump (res, handler_bb, guarded_bb);
+}
+
+static void
+emit_limple_insn (Lisp_Object insn)
+{
+  Lisp_Object op = XCAR (insn);
+  Lisp_Object args = XCDR (insn);
+  gcc_jit_rvalue *res;
+  Lisp_Object arg[6];
+
+  Lisp_Object p = XCDR (insn);
+  ptrdiff_t i = 0;
+  FOR_EACH_TAIL (p)
+    {
+      if (i == sizeof (arg) / sizeof (Lisp_Object))
+       break;
+      arg[i++] = XCAR (p);
+    }
+
+  if (EQ (op, Qjump))
+    {
+      /* Unconditional branch.  */
+      gcc_jit_block *target = retrive_block (arg[0]);
+      gcc_jit_block_end_with_jump (comp.block, NULL, target);
+    }
+  else if (EQ (op, Qcond_jump))
+    {
+      /* Conditional branch.  */
+      gcc_jit_rvalue *a = emit_mvar_rval (arg[0]);
+      gcc_jit_rvalue *b = emit_mvar_rval (arg[1]);
+      gcc_jit_block *target1 = retrive_block (arg[2]);
+      gcc_jit_block *target2 = retrive_block (arg[3]);
+
+      emit_cond_jump (emit_EQ (a, b), target1, target2);
+    }
+  else if (EQ (op, Qcond_jump_narg_leq))
+    {
+      /*
+        Limple: (cond-jump-narg-less 2 entry_2 entry_fallback_2)
+        C: if (nargs < 2) goto entry2_fallback; else goto entry_2;
+      */
+      gcc_jit_lvalue *nargs =
+       gcc_jit_param_as_lvalue (gcc_jit_function_get_param (comp.func, 0));
+      eassert (XFIXNUM (arg[0]) < INT_MAX);
+      gcc_jit_rvalue *n =
+       gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                            comp.ptrdiff_type,
+                                            XFIXNUM (arg[0]));
+      gcc_jit_block *target1 = retrive_block (arg[1]);
+      gcc_jit_block *target2 = retrive_block (arg[2]);
+      gcc_jit_rvalue *test = gcc_jit_context_new_comparison (
+                              comp.ctxt,
+                              NULL,
+                              GCC_JIT_COMPARISON_LE,
+                              gcc_jit_lvalue_as_rvalue (nargs),
+                              n);
+      emit_cond_jump (test, target1, target2);
+    }
+  else if (EQ (op, Qphi) || EQ (op, Qassume))
+    {
+      /* Nothing to do for phis or assumes in the backend.  */
+    }
+  else if (EQ (op, Qpush_handler))
+    {
+      /* (push-handler condition-case #s(comp-mvar 0 3 t (arith-error) cons 
nil) 1 bb_2 bb_1) */
+      int h_num UNINIT;
+      Lisp_Object handler_spec = arg[0];
+      gcc_jit_rvalue *handler = emit_mvar_rval (arg[1]);
+      if (EQ (handler_spec, Qcatcher))
+       h_num = CATCHER;
+      else if (EQ (handler_spec, Qcondition_case))
+       h_num = CONDITION_CASE;
+      else
+       xsignal2 (Qnative_ice, build_string ("incoherent insn"), insn);
+      gcc_jit_rvalue *handler_type =
+       gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                            comp.int_type,
+                                            h_num);
+      gcc_jit_block *handler_bb = retrive_block (arg[2]);
+      gcc_jit_block *guarded_bb = retrive_block (arg[3]);
+      emit_limple_push_handler (handler, handler_type, handler_bb, guarded_bb,
+                               arg[0]);
+    }
+  else if (EQ (op, Qpop_handler))
+    {
+      /*
+       C: current_thread->m_handlerlist =
+            current_thread->m_handlerlist->next;
+      */
+      gcc_jit_lvalue *m_handlerlist =
+       gcc_jit_rvalue_dereference_field (
+         gcc_jit_lvalue_as_rvalue (
+           gcc_jit_rvalue_dereference (comp.current_thread_ref, NULL)),
+         NULL,
+         comp.m_handlerlist);
+
+      gcc_jit_block_add_assignment (
+       comp.block,
+       NULL,
+       m_handlerlist,
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_rvalue_dereference_field (
+           gcc_jit_lvalue_as_rvalue (m_handlerlist),
+           NULL,
+           comp.handler_next_field)));
+
+    }
+  else if (EQ (op, Qfetch_handler))
+    {
+      gcc_jit_lvalue *m_handlerlist =
+       gcc_jit_rvalue_dereference_field (
+         gcc_jit_lvalue_as_rvalue (
+           gcc_jit_rvalue_dereference (comp.current_thread_ref, NULL)),
+         NULL,
+         comp.m_handlerlist);
+      gcc_jit_block_add_assignment (comp.block,
+                                   NULL,
+                                   comp.loc_handler,
+                                   gcc_jit_lvalue_as_rvalue (m_handlerlist));
+
+      gcc_jit_block_add_assignment (
+       comp.block,
+       NULL,
+       m_handlerlist,
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_rvalue_dereference_field (
+           gcc_jit_lvalue_as_rvalue (comp.loc_handler),
+           NULL,
+           comp.handler_next_field)));
+      emit_frame_assignment (
+       arg[0],
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_rvalue_dereference_field (
+           gcc_jit_lvalue_as_rvalue (comp.loc_handler),
+           NULL,
+           comp.handler_val_field)));
+    }
+  else if (EQ (op, Qcall))
+    {
+      gcc_jit_block_add_eval (comp.block, NULL,
+                             emit_limple_call (args));
+    }
+  else if (EQ (op, Qcallref))
+    {
+      gcc_jit_block_add_eval (comp.block, NULL,
+                             emit_limple_call_ref (args, false));
+    }
+  else if (EQ (op, Qdirect_call))
+    {
+      gcc_jit_block_add_eval (
+        comp.block, NULL,
+       emit_simple_limple_call (XCDR (insn), comp.lisp_obj_type, true));
+    }
+  else if (EQ (op, Qdirect_callref))
+    {
+      gcc_jit_block_add_eval (comp.block, NULL,
+                             emit_limple_call_ref (XCDR (insn), true));
+    }
+  else if (EQ (op, Qset))
+    {
+      Lisp_Object arg1 = arg[1];
+
+      if (EQ (Ftype_of (arg1), Qcomp_mvar))
+       res = emit_mvar_rval (arg1);
+      else if (EQ (FIRST (arg1), Qcall))
+       res = emit_limple_call (XCDR (arg1));
+      else if (EQ (FIRST (arg1), Qcallref))
+       res = emit_limple_call_ref (XCDR (arg1), false);
+      else if (EQ (FIRST (arg1), Qdirect_call))
+       res = emit_simple_limple_call (XCDR (arg1), comp.lisp_obj_type, true);
+      else if (EQ (FIRST (arg1), Qdirect_callref))
+       res = emit_limple_call_ref (XCDR (arg1), true);
+      else
+       xsignal2 (Qnative_ice,
+                 build_string ("LIMPLE inconsistent arg1 for insn"),
+                 insn);
+
+      if (!res)
+       xsignal1 (Qnative_ice,
+                 build_string (gcc_jit_context_get_first_error (comp.ctxt)));
+
+      emit_frame_assignment (arg[0], res);
+    }
+  else if (EQ (op, Qset_par_to_local))
+    {
+      /* Ex: (set-par-to-local #s(comp-mvar 0 3 nil nil nil nil) 0).  */
+      EMACS_INT param_n = XFIXNUM (arg[1]);
+      eassert (param_n < INT_MAX);
+      gcc_jit_rvalue *param =
+       gcc_jit_param_as_rvalue (gcc_jit_function_get_param (comp.func,
+                                                            param_n));
+      emit_frame_assignment (arg[0], param);
+    }
+  else if (EQ (op, Qset_args_to_local))
+    {
+      /*
+       Ex: (set-args-to-local #s(comp-mvar 1 6 nil nil nil nil))
+       C: local[1] = *args;
+      */
+      gcc_jit_rvalue *gcc_args =
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_param_as_lvalue (gcc_jit_function_get_param (comp.func, 1)));
+
+      gcc_jit_rvalue *res =
+       gcc_jit_lvalue_as_rvalue (gcc_jit_rvalue_dereference (gcc_args, NULL));
+
+      emit_frame_assignment (arg[0], res);
+    }
+  else if (EQ (op, Qset_rest_args_to_local))
+    {
+      /*
+        Ex: (set-rest-args-to-local #s(comp-mvar 2 9 nil nil nil nil))
+        C: local[2] = list (nargs - 2, args);
+      */
+
+      EMACS_INT slot_n = XFIXNUM (CALL1I (comp-mvar-slot, arg[0]));
+      eassert (slot_n < INT_MAX);
+      gcc_jit_rvalue *n =
+       gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                            comp.ptrdiff_type,
+                                            slot_n);
+      gcc_jit_lvalue *nargs =
+       gcc_jit_param_as_lvalue (gcc_jit_function_get_param (comp.func, 0));
+      gcc_jit_lvalue *args =
+       gcc_jit_param_as_lvalue (gcc_jit_function_get_param (comp.func, 1));
+
+      gcc_jit_rvalue *list_args[] =
+       { emit_binary_op (GCC_JIT_BINARY_OP_MINUS,
+                         comp.ptrdiff_type,
+                         gcc_jit_lvalue_as_rvalue (nargs),
+                         n),
+         gcc_jit_lvalue_as_rvalue (args) };
+
+      res = emit_call (Qlist, comp.lisp_obj_type, 2,
+                      list_args, false);
+
+      emit_frame_assignment (arg[0], res);
+    }
+  else if (EQ (op, Qinc_args))
+    {
+      /*
+       Ex: (inc-args)
+       C: ++args;
+      */
+      gcc_jit_lvalue *args =
+       gcc_jit_param_as_lvalue (gcc_jit_function_get_param (comp.func, 1));
+
+      gcc_jit_block_add_assignment (comp.block,
+                                   NULL,
+                                   args,
+                                   emit_ptr_arithmetic (
+                                     gcc_jit_lvalue_as_rvalue (args),
+                                     comp.lisp_obj_ptr_type,
+                                     sizeof (Lisp_Object),
+                                     comp.one));
+    }
+  else if (EQ (op, Qsetimm))
+    {
+      /* Ex: (setimm #s(comp-mvar 9 1 t 3 nil) a).  */
+      emit_comment (SSDATA (Fprin1_to_string (arg[1], Qnil)));
+      imm_reloc_t reloc = obj_to_reloc (arg[1]);
+      emit_frame_assignment (
+       arg[0],
+       gcc_jit_lvalue_as_rvalue (
+         gcc_jit_context_new_array_access (comp.ctxt,
+                                           NULL,
+                                           reloc.array.r_val,
+                                           reloc.idx)));
+    }
+  else if (EQ (op, Qcomment))
+    {
+      /* Ex: (comment "Function: foo").  */
+      emit_comment (SSDATA (arg[0]));
+    }
+  else if (EQ (op, Qreturn))
+    {
+      gcc_jit_block_end_with_return (comp.block,
+                                    NULL,
+                                    emit_mvar_rval (arg[0]));
+    }
+  else if (EQ (op, Qunreachable))
+    {
+      /* Libgccjit has no __builtin_unreachable.  */
+      gcc_jit_block_end_with_return (comp.block,
+                                    NULL,
+                                    emit_lisp_obj_rval (Qnil));
+    }
+  else
+    {
+      xsignal2 (Qnative_ice,
+               build_string ("LIMPLE op inconsistent"),
+               op);
+    }
+}
+
+
+/**************/
+/* Inliners.  */
+/**************/
+
+static gcc_jit_rvalue *
+emit_call_with_type_hint (gcc_jit_function *func, Lisp_Object insn,
+                         Lisp_Object type)
+{
+  bool hint_match =
+    !NILP (CALL2I (comp-mvar-type-hint-match-p, SECOND (insn), type));
+  gcc_jit_rvalue *args[] =
+    { emit_mvar_rval (SECOND (insn)),
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.bool_type,
+                                          hint_match) };
+
+  return gcc_jit_context_new_call (comp.ctxt, NULL, func, 2, args);
+}
+
+/* Same as before but with two args. The type hint is on the 2th.  */
+static gcc_jit_rvalue *
+emit_call2_with_type_hint (gcc_jit_function *func, Lisp_Object insn,
+                          Lisp_Object type)
+{
+  bool hint_match =
+    !NILP (CALL2I (comp-mvar-type-hint-match-p, SECOND (insn), type));
+  gcc_jit_rvalue *args[] =
+    { emit_mvar_rval (SECOND (insn)),
+      emit_mvar_rval (THIRD (insn)),
+      gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                          comp.bool_type,
+                                          hint_match) };
+
+  return gcc_jit_context_new_call (comp.ctxt, NULL, func, 3, args);
+}
+
+
+static gcc_jit_rvalue *
+emit_add1 (Lisp_Object insn)
+{
+  return emit_call_with_type_hint (comp.add1, insn, Qfixnum);
+}
+
+static gcc_jit_rvalue *
+emit_sub1 (Lisp_Object insn)
+{
+  return emit_call_with_type_hint (comp.sub1, insn, Qfixnum);
+}
+
+static gcc_jit_rvalue *
+emit_negate (Lisp_Object insn)
+{
+  return emit_call_with_type_hint (comp.negate, insn, Qfixnum);
+}
+
+static gcc_jit_rvalue *
+emit_consp (Lisp_Object insn)
+{
+  gcc_jit_rvalue *x = emit_mvar_rval (SECOND (insn));
+  gcc_jit_rvalue *res = emit_coerce (comp.bool_type,
+                                  emit_CONSP (x));
+  return gcc_jit_context_new_call (comp.ctxt,
+                                  NULL,
+                                  comp.bool_to_lisp_obj,
+                                  1, &res);
+}
+
+static gcc_jit_rvalue *
+emit_car (Lisp_Object insn)
+{
+  return emit_call_with_type_hint (comp.car, insn, Qcons);
+}
+
+static gcc_jit_rvalue *
+emit_cdr (Lisp_Object insn)
+{
+  return emit_call_with_type_hint (comp.cdr, insn, Qcons);
+}
+
+static gcc_jit_rvalue *
+emit_setcar (Lisp_Object insn)
+{
+  return emit_call2_with_type_hint (comp.setcar, insn, Qcons);
+}
+
+static gcc_jit_rvalue *
+emit_setcdr (Lisp_Object insn)
+{
+  return emit_call2_with_type_hint (comp.setcdr, insn, Qcons);
+}
+
+static gcc_jit_rvalue *
+emit_numperp (Lisp_Object insn)
+{
+  gcc_jit_rvalue *x = emit_mvar_rval (SECOND (insn));
+  gcc_jit_rvalue *res = emit_NUMBERP (x);
+  return gcc_jit_context_new_call (comp.ctxt, NULL, comp.bool_to_lisp_obj, 1,
+                                  &res);
+}
+
+static gcc_jit_rvalue *
+emit_integerp (Lisp_Object insn)
+{
+  gcc_jit_rvalue *x = emit_mvar_rval (SECOND (insn));
+  gcc_jit_rvalue *res = emit_INTEGERP (x);
+  return gcc_jit_context_new_call (comp.ctxt, NULL, comp.bool_to_lisp_obj, 1,
+                                  &res);
+}
+
+static gcc_jit_rvalue *
+emit_maybe_gc_or_quit (Lisp_Object insn)
+{
+  return gcc_jit_context_new_call (comp.ctxt, NULL, comp.maybe_gc_or_quit, 0,
+                                  NULL);
+}
+
+/* This is in charge of serializing an object and export a function to
+   retrieve it at load time.  */
+#pragma GCC diagnostic ignored "-Waddress"
+static void
+emit_static_object (const char *name, Lisp_Object obj)
+{
+  /* libgccjit has no support for initialized static data.
+     The mechanism below is certainly not aesthetic but I assume the bottle 
neck
+     in terms of performance at load time will still be the reader.
+     NOTE: we can not rely on libgccjit even for valid NULL terminated C
+     strings cause of this funny bug that will affect all pre gcc10 era gccs:
+     https://gcc.gnu.org/ml/jit/2019-q3/msg00013.html  */
+
+  ptrdiff_t count = SPECPDL_INDEX ();
+  /* Preserve uninterned symbols, this is specifically necessary for
+     CL macro expansion in dynamic scope code (bug#42088).  See
+     `byte-compile-output-file-form'.  */
+  specbind (intern_c_string ("print-escape-newlines"), Qt);
+  specbind (intern_c_string ("print-length"), Qnil);
+  specbind (intern_c_string ("print-level"), Qnil);
+  specbind (intern_c_string ("print-quoted"), Qt);
+  specbind (intern_c_string ("print-gensym"), Qt);
+  specbind (intern_c_string ("print-circle"), Qt);
+  Lisp_Object str = Fprin1_to_string (obj, Qnil);
+  unbind_to (count, Qnil);
+
+  ptrdiff_t len = SBYTES (str);
+  const char *p = SSDATA (str);
+
+#if defined (LIBGCCJIT_HAVE_gcc_jit_global_set_initializer) \
+  || defined (WINDOWSNT)
+  if (gcc_jit_global_set_initializer)
+    {
+      ptrdiff_t str_size = len + 1;
+      ptrdiff_t size = sizeof (static_obj_t) + str_size;
+      static_obj_t *static_obj = xmalloc (size);
+      static_obj->len = str_size;
+      memcpy (static_obj->data, p, str_size);
+      gcc_jit_lvalue *blob =
+       gcc_jit_context_new_global (
+         comp.ctxt,
+         NULL,
+         GCC_JIT_GLOBAL_EXPORTED,
+         gcc_jit_context_new_array_type (comp.ctxt, NULL,
+                                         comp.char_type,
+                                         size),
+         format_string ("%s_blob", name));
+      gcc_jit_global_set_initializer (blob, static_obj, size);
+      xfree (static_obj);
+
+      return;
+    }
+#endif
+
+  gcc_jit_type *a_type =
+    gcc_jit_context_new_array_type (comp.ctxt,
+                                   NULL,
+                                   comp.char_type,
+                                   len + 1);
+  gcc_jit_field *fields[] =
+    { gcc_jit_context_new_field (comp.ctxt,
+                                NULL,
+                                comp.ptrdiff_type,
+                                "len"),
+      gcc_jit_context_new_field (comp.ctxt,
+                                NULL,
+                                a_type,
+                                "data") };
+
+  gcc_jit_type *data_struct_t =
+    gcc_jit_struct_as_type (
+      gcc_jit_context_new_struct_type (comp.ctxt,
+                                      NULL,
+                                      format_string ("%s_struct", name),
+                                      ARRAYELTS (fields), fields));
+
+  gcc_jit_lvalue *data_struct =
+    gcc_jit_context_new_global (comp.ctxt,
+                               NULL,
+                               GCC_JIT_GLOBAL_INTERNAL,
+                               data_struct_t,
+                               format_string ("%s_s", name));
+
+  gcc_jit_function *f =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_EXPORTED,
+                                 gcc_jit_type_get_pointer (data_struct_t),
+                                 name,
+                                 0, NULL, 0);
+  DECL_BLOCK (block, f);
+
+  if (comp.debug > 1)
+    {
+      char *comment = memcpy (xmalloc (len), p, len);
+      for (ptrdiff_t i = 0; i < len - 1; i++)
+       if (!comment[i])
+         comment[i] = '\n';
+      gcc_jit_block_add_comment (block, NULL, comment);
+      xfree (comment);
+    }
+
+  gcc_jit_lvalue *arr =
+      gcc_jit_lvalue_access_field (data_struct, NULL, fields[1]);
+
+  gcc_jit_lvalue *ptrvar = gcc_jit_function_new_local (f, NULL,
+                                                       comp.char_ptr_type,
+                                                       "ptr");
+
+  gcc_jit_block_add_assignment (
+    block,
+    NULL,
+    ptrvar,
+    gcc_jit_lvalue_get_address (
+      gcc_jit_context_new_array_access (
+        comp.ctxt,
+        NULL,
+        gcc_jit_lvalue_as_rvalue (arr),
+        gcc_jit_context_new_rvalue_from_int (comp.ctxt, comp.int_type, 0)),
+      NULL));
+
+  /* We can't use always string literals longer that 200 bytes because
+     they cause a crash in pre GCC 10 libgccjit.
+     <https://gcc.gnu.org/ml/jit/2019-q3/msg00013.html>.
+
+     Adjust if possible to reduce the number of function calls.  */
+  size_t chunck_size = NILP (Fcomp_libgccjit_version ()) ? 200 : 1024;
+  char *buff = xmalloc (chunck_size);
+  for (ptrdiff_t i = 0; i < len;)
+    {
+      strncpy (buff, p, chunck_size);
+      buff[chunck_size - 1] = 0;
+      uintptr_t l = strlen (buff);
+
+      if (l != 0)
+        {
+          p += l;
+          i += l;
+
+          gcc_jit_rvalue *args[] =
+           { gcc_jit_lvalue_as_rvalue (ptrvar),
+             gcc_jit_context_new_string_literal (comp.ctxt, buff),
+             gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                  comp.size_t_type,
+                                                  l) };
+
+          gcc_jit_block_add_eval (block, NULL,
+                                  gcc_jit_context_new_call (comp.ctxt, NULL,
+                                                            comp.memcpy,
+                                                            ARRAYELTS (args),
+                                                           args));
+          gcc_jit_block_add_assignment (block, NULL, ptrvar,
+            gcc_jit_lvalue_get_address (
+              gcc_jit_context_new_array_access (comp.ctxt, NULL,
+                gcc_jit_lvalue_as_rvalue (ptrvar),
+                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                     comp.uintptr_type,
+                                                     l)),
+              NULL));
+        }
+      else
+        {
+          /* If strlen returned 0 that means that the static object
+             contains a NULL byte.  In that case just move over to the
+             next block.  We can rely on the byte being zero because
+             of the previous call to bzero and because the dynamic
+             linker cleared it.  */
+          p++;
+          i++;
+          gcc_jit_block_add_assignment (
+            block, NULL, ptrvar,
+            gcc_jit_lvalue_get_address (
+              gcc_jit_context_new_array_access (
+                comp.ctxt, NULL, gcc_jit_lvalue_as_rvalue (ptrvar),
+                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                     comp.uintptr_type, 1)),
+              NULL));
+        }
+    }
+  xfree (buff);
+
+  gcc_jit_block_add_assignment (
+       block,
+       NULL,
+       gcc_jit_lvalue_access_field (data_struct, NULL, fields[0]),
+       gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                            comp.ptrdiff_type,
+                                            len));
+  gcc_jit_rvalue *res = gcc_jit_lvalue_get_address (data_struct, NULL);
+  gcc_jit_block_end_with_return (block, NULL, res);
+}
+#pragma GCC diagnostic pop
+
+static reloc_array_t
+declare_imported_data_relocs (Lisp_Object container, const char *code_symbol,
+                             const char *text_symbol)
+{
+  /* Imported objects.  */
+  reloc_array_t res;
+  res.len =
+    XFIXNUM (CALL1I (hash-table-count,
+                    CALL1I (comp-data-container-idx, container)));
+  Lisp_Object d_reloc = CALL1I (comp-data-container-l, container);
+  d_reloc = Fvconcat (1, &d_reloc);
+
+  res.r_val =
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_context_new_global (
+       comp.ctxt,
+       NULL,
+       GCC_JIT_GLOBAL_EXPORTED,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.lisp_obj_type,
+                                       res.len),
+       code_symbol));
+
+  emit_static_object (text_symbol, d_reloc);
+
+  return res;
+}
+
+static void
+declare_imported_data (void)
+{
+  /* Imported objects.  */
+  comp.data_relocs =
+    declare_imported_data_relocs (CALL1I (comp-ctxt-d-default, Vcomp_ctxt),
+                                 DATA_RELOC_SYM,
+                                 TEXT_DATA_RELOC_SYM);
+  comp.data_relocs_impure =
+    declare_imported_data_relocs (CALL1I (comp-ctxt-d-impure, Vcomp_ctxt),
+                                 DATA_RELOC_IMPURE_SYM,
+                                 TEXT_DATA_RELOC_IMPURE_SYM);
+  comp.data_relocs_ephemeral =
+    declare_imported_data_relocs (CALL1I (comp-ctxt-d-ephemeral, Vcomp_ctxt),
+                                 DATA_RELOC_EPHEMERAL_SYM,
+                                 TEXT_DATA_RELOC_EPHEMERAL_SYM);
+}
+
+/*
+  Declare as imported all the functions that are requested from the runtime.
+  These are either subrs or not.
+*/
+static Lisp_Object
+declare_runtime_imported_funcs (void)
+{
+  Lisp_Object field_list = Qnil;
+
+#define ADD_IMPORTED(f_name, ret_type, nargs, args)                           \
+  {                                                                           \
+    Lisp_Object name = intern_c_string (STR (f_name));                        \
+    Lisp_Object field =                                                        
       \
+      make_mint_ptr (declare_imported_func (name, ret_type, nargs, args));     
\
+    Lisp_Object el = Fcons (name, field);                                     \
+    field_list = Fcons (el, field_list);                                      \
+  } while (0)
+
+  gcc_jit_type *args[4];
+
+  ADD_IMPORTED (wrong_type_argument, comp.void_type, 2, NULL);
+
+  args[0] = comp.lisp_obj_type;
+  args[1] = comp.int_type;
+  ADD_IMPORTED (helper_PSEUDOVECTOR_TYPEP_XUNTAG, comp.bool_type, 2, args);
+
+  ADD_IMPORTED (pure_write_error, comp.void_type, 1, NULL);
+
+  args[0] = comp.lisp_obj_type;
+  args[1] = comp.int_type;
+  ADD_IMPORTED (push_handler, comp.handler_ptr_type, 2, args);
+
+  ADD_IMPORTED (record_unwind_protect_excursion, comp.void_type, 0, NULL);
+
+  args[0] = comp.lisp_obj_type;
+  ADD_IMPORTED (helper_unbind_n, comp.lisp_obj_type, 1, args);
+
+  ADD_IMPORTED (helper_save_restriction, comp.void_type, 0, NULL);
+
+  ADD_IMPORTED (record_unwind_current_buffer, comp.void_type, 0, NULL);
+
+  args[0] = args[1] = args[2] = comp.lisp_obj_type;
+  args[3] = comp.int_type;
+  ADD_IMPORTED (set_internal, comp.void_type, 4, args);
+
+  args[0] = comp.lisp_obj_type;
+  ADD_IMPORTED (helper_unwind_protect, comp.void_type, 1, args);
+
+  args[0] = args[1] = comp.lisp_obj_type;
+  ADD_IMPORTED (specbind, comp.void_type, 2, args);
+
+  ADD_IMPORTED (maybe_gc, comp.void_type, 0, NULL);
+
+  ADD_IMPORTED (maybe_quit, comp.void_type, 0, NULL);
+
+#undef ADD_IMPORTED
+
+  return Freverse (field_list);
+}
+
+/*
+  This emit the code needed by every compilation unit to be loaded.
+*/
+static void
+emit_ctxt_code (void)
+{
+  /* Emit optimize qualities.  */
+  Lisp_Object opt_qly[] =
+    { Fcons (Qcomp_speed, make_fixnum (comp.speed)),
+      Fcons (Qcomp_debug, make_fixnum (comp.debug)),
+      Fcons (Qgccjit,
+            Fcomp_libgccjit_version ()) };
+  emit_static_object (TEXT_OPTIM_QLY_SYM, Flist (ARRAYELTS (opt_qly), 
opt_qly));
+
+  emit_static_object (TEXT_FDOC_SYM,
+                     CALL1I (comp-ctxt-function-docs, Vcomp_ctxt));
+
+  comp.current_thread_ref =
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_context_new_global (
+       comp.ctxt,
+       NULL,
+       GCC_JIT_GLOBAL_EXPORTED,
+       gcc_jit_type_get_pointer (comp.thread_state_ptr_type),
+       CURRENT_THREAD_RELOC_SYM));
+
+  comp.pure_ptr =
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_context_new_global (
+       comp.ctxt,
+       NULL,
+       GCC_JIT_GLOBAL_EXPORTED,
+        comp.void_ptr_type,
+       PURE_RELOC_SYM));
+
+  gcc_jit_context_new_global (
+       comp.ctxt,
+       NULL,
+       GCC_JIT_GLOBAL_EXPORTED,
+       comp.lisp_obj_type,
+       COMP_UNIT_SYM);
+
+  declare_imported_data ();
+
+  /* Functions imported from Lisp code.         */
+  freloc_check_fill ();
+  gcc_jit_field **fields = xmalloc (freloc.size * sizeof (*fields));
+  ptrdiff_t n_frelocs = 0;
+  Lisp_Object f_runtime = declare_runtime_imported_funcs ();
+  FOR_EACH_TAIL (f_runtime)
+    {
+      Lisp_Object el = XCAR (f_runtime);
+      eassert (n_frelocs < freloc.size);
+      fields[n_frelocs++] = xmint_pointer (XCDR (el));
+    }
+
+  /* Sign the .eln for the exposed ABI it expects at load.  */
+  eassert (!NILP (Vcomp_abi_hash));
+  emit_static_object (LINK_TABLE_HASH_SYM, Vcomp_abi_hash);
+
+  Lisp_Object subr_l = Vcomp_subr_list;
+  FOR_EACH_TAIL (subr_l)
+    {
+      struct Lisp_Subr *subr = XSUBR (XCAR (subr_l));
+      Lisp_Object subr_sym = intern_c_string (subr->symbol_name);
+      eassert (n_frelocs < freloc.size);
+      fields[n_frelocs++] = declare_imported_func (subr_sym, 
comp.lisp_obj_type,
+                                                  subr->max_args, NULL);
+    }
+
+  gcc_jit_struct *f_reloc_struct =
+    gcc_jit_context_new_struct_type (comp.ctxt,
+                                    NULL,
+                                    "freloc_link_table",
+                                    n_frelocs, fields);
+  comp.func_relocs_ptr_type =
+    gcc_jit_type_get_pointer (
+      gcc_jit_struct_as_type (f_reloc_struct));
+
+  comp.func_relocs =
+    gcc_jit_context_new_global (comp.ctxt,
+                               NULL,
+                               GCC_JIT_GLOBAL_EXPORTED,
+                               comp.func_relocs_ptr_type,
+                               FUNC_LINK_TABLE_SYM);
+
+  xfree (fields);
+}
+
+
+/****************************************************************/
+/* Inline function definition and lisp data structure follows.  */
+/****************************************************************/
+
+/* struct Lisp_Cons definition.  */
+
+static void
+define_lisp_cons (void)
+{
+  /*
+    union cdr_u
+    {
+      Lisp_Object cdr;
+      struct Lisp_Cons *chain;
+    };
+
+    struct cons_s
+    {
+      Lisp_Object car;
+      union cdr_u u;
+    };
+
+    union cons_u
+    {
+      struct cons_s s;
+      char align_pad[sizeof (struct Lisp_Cons)];
+    };
+
+    struct Lisp_Cons
+    {
+      union cons_u u;
+    };
+  */
+
+  comp.lisp_cons_s =
+    gcc_jit_context_new_opaque_struct (comp.ctxt,
+                                      NULL,
+                                      "comp_Lisp_Cons");
+  comp.lisp_cons_type =
+    gcc_jit_struct_as_type (comp.lisp_cons_s);
+  comp.lisp_cons_ptr_type =
+    gcc_jit_type_get_pointer (comp.lisp_cons_type);
+
+  comp.lisp_cons_u_s_u_cdr =
+    gcc_jit_context_new_field (comp.ctxt,
+                              NULL,
+                              comp.lisp_obj_type,
+                              "cdr");
+
+  gcc_jit_field *cdr_u_fields[] =
+    { comp.lisp_cons_u_s_u_cdr,
+      gcc_jit_context_new_field (comp.ctxt,
+                                NULL,
+                                comp.lisp_cons_ptr_type,
+                                "chain") };
+
+  gcc_jit_type *cdr_u =
+    gcc_jit_context_new_union_type (comp.ctxt,
+                                   NULL,
+                                   "comp_cdr_u",
+                                   ARRAYELTS (cdr_u_fields),
+                                   cdr_u_fields);
+
+  comp.lisp_cons_u_s_car = gcc_jit_context_new_field (comp.ctxt,
+                                           NULL,
+                                           comp.lisp_obj_type,
+                                           "car");
+  comp.lisp_cons_u_s_u = gcc_jit_context_new_field (comp.ctxt,
+                                                   NULL,
+                                                   cdr_u,
+                                                   "u");
+  gcc_jit_field *cons_s_fields[] =
+    { comp.lisp_cons_u_s_car,
+      comp.lisp_cons_u_s_u };
+
+  gcc_jit_struct *cons_s =
+    gcc_jit_context_new_struct_type (comp.ctxt,
+                                    NULL,
+                                    "comp_cons_s",
+                                    ARRAYELTS (cons_s_fields),
+                                    cons_s_fields);
+
+  comp.lisp_cons_u_s = gcc_jit_context_new_field (comp.ctxt,
+                                NULL,
+                                gcc_jit_struct_as_type (cons_s),
+                                "s");
+
+  gcc_jit_field *cons_u_fields[] =
+    { comp.lisp_cons_u_s,
+      gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.char_type,
+                                       sizeof (struct Lisp_Cons)),
+       "align_pad") };
+
+  gcc_jit_type *lisp_cons_u_type =
+    gcc_jit_context_new_union_type (comp.ctxt,
+                                   NULL,
+                                   "comp_cons_u",
+                                   ARRAYELTS (cons_u_fields),
+                                   cons_u_fields);
+
+  comp.lisp_cons_u =
+    gcc_jit_context_new_field (comp.ctxt,
+                              NULL,
+                              lisp_cons_u_type,
+                              "u");
+  gcc_jit_struct_set_fields (comp.lisp_cons_s,
+                            NULL, 1, &comp.lisp_cons_u);
+
+}
+
+/* Opaque jmp_buf definition.  */
+
+static void
+define_jmp_buf (void)
+{
+  gcc_jit_field *field =
+    gcc_jit_context_new_field (
+      comp.ctxt,
+      NULL,
+      gcc_jit_context_new_array_type (comp.ctxt,
+                                     NULL,
+                                     comp.char_type,
+                                     sizeof (sys_jmp_buf)),
+      "stuff");
+  comp.jmp_buf_s =
+    gcc_jit_context_new_struct_type (comp.ctxt,
+                                    NULL,
+                                    "comp_jmp_buf",
+                                    1, &field);
+}
+
+static void
+define_memcpy (void)
+{
+
+  gcc_jit_param *params[] =
+    { gcc_jit_context_new_param (comp.ctxt, NULL, comp.void_ptr_type, "dest"),
+      gcc_jit_context_new_param (comp.ctxt, NULL, comp.void_ptr_type, "src"),
+      gcc_jit_context_new_param (comp.ctxt, NULL, comp.size_t_type, "n") };
+
+  comp.memcpy =
+    gcc_jit_context_new_function (comp.ctxt, NULL, GCC_JIT_FUNCTION_IMPORTED,
+                                 comp.void_ptr_type, "memcpy",
+                                 ARRAYELTS (params), params, false);
+}
+
+/* struct handler definition  */
+
+static void
+define_handler_struct (void)
+{
+  comp.handler_s =
+    gcc_jit_context_new_opaque_struct (comp.ctxt, NULL, "comp_handler");
+  comp.handler_ptr_type =
+    gcc_jit_type_get_pointer (gcc_jit_struct_as_type (comp.handler_s));
+
+  comp.handler_jmp_field = gcc_jit_context_new_field (comp.ctxt,
+                                                     NULL,
+                                                     gcc_jit_struct_as_type (
+                                                       comp.jmp_buf_s),
+                                                     "jmp");
+  comp.handler_val_field = gcc_jit_context_new_field (comp.ctxt,
+                                                     NULL,
+                                                     comp.lisp_obj_type,
+                                                     "val");
+  comp.handler_next_field = gcc_jit_context_new_field (comp.ctxt,
+                                                      NULL,
+                                                      comp.handler_ptr_type,
+                                                      "next");
+  gcc_jit_field *fields[] =
+    { gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.char_type,
+                                       offsetof (struct handler, val)),
+       "pad0"),
+      comp.handler_val_field,
+      comp.handler_next_field,
+      gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.char_type,
+                                       offsetof (struct handler, jmp)
+                                       - offsetof (struct handler, next)
+                                       - sizeof (((struct handler *) 
0)->next)),
+       "pad1"),
+      comp.handler_jmp_field,
+      gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.char_type,
+                                       sizeof (struct handler)
+                                       - offsetof (struct handler, jmp)
+                                       - sizeof (((struct handler *) 0)->jmp)),
+       "pad2") };
+  gcc_jit_struct_set_fields (comp.handler_s,
+                            NULL,
+                            ARRAYELTS (fields),
+                            fields);
+
+}
+
+static void
+define_thread_state_struct (void)
+{
+  /* Partially opaque definition for `thread_state'.
+     Because we need to access just m_handlerlist hopefully this is requires
+     less manutention then the full deifnition.         */
+
+  comp.m_handlerlist = gcc_jit_context_new_field (comp.ctxt,
+                                                 NULL,
+                                                 comp.handler_ptr_type,
+                                                 "m_handlerlist");
+  gcc_jit_field *fields[] =
+    { gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (comp.ctxt,
+                                       NULL,
+                                       comp.char_type,
+                                       offsetof (struct thread_state,
+                                                 m_handlerlist)),
+       "pad0"),
+      comp.m_handlerlist,
+      gcc_jit_context_new_field (
+       comp.ctxt,
+       NULL,
+       gcc_jit_context_new_array_type (
+         comp.ctxt,
+         NULL,
+         comp.char_type,
+         sizeof (struct thread_state)
+         - offsetof (struct thread_state,
+                     m_handlerlist)
+         - sizeof (((struct thread_state *) 0)->m_handlerlist)),
+       "pad1") };
+
+  comp.thread_state_s =
+    gcc_jit_context_new_struct_type (comp.ctxt,
+                                    NULL,
+                                    "comp_thread_state",
+                                    ARRAYELTS (fields),
+                                    fields);
+  comp.thread_state_ptr_type =
+    gcc_jit_type_get_pointer (gcc_jit_struct_as_type (comp.thread_state_s));
+}
+
+struct cast_type
+{
+  gcc_jit_type *type;
+  const char *name;
+  size_t bytes_size;
+  enum cast_kind_of_type kind;
+};
+
+static gcc_jit_function *
+define_cast_from_to (struct cast_type from, int from_index, struct cast_type 
to,
+                    int to_index)
+{
+  /*  FIXME: sign extension not implemented.  */
+  if (comp.cast_type_sizes[from_index] < comp.cast_type_sizes[to_index]
+      && comp.cast_type_kind[to_index] == kind_signed)
+    return NULL;
+
+  char *name = format_string ("cast_from_%s_to_%s", from.name, to.name);
+  gcc_jit_param *param = gcc_jit_context_new_param (comp.ctxt, NULL,
+                                                    from.type, "arg");
+  gcc_jit_function *result = gcc_jit_context_new_function (comp.ctxt,
+                               NULL,
+                               GCC_JIT_FUNCTION_INTERNAL,
+                               to.type,
+                               name,
+                               1,
+                               &param,
+                               0);
+
+  DECL_BLOCK (entry_block, result);
+
+  gcc_jit_lvalue *tmp_union
+    = gcc_jit_function_new_local (result,
+                                  NULL,
+                                  comp.cast_union_type,
+                                  "union_cast");
+
+  /*  Zero the union first.  */
+  gcc_jit_block_add_assignment (entry_block, NULL,
+                                gcc_jit_lvalue_access_field (tmp_union, NULL,
+                                  comp.cast_union_fields[NUM_CAST_TYPES]),
+                                  gcc_jit_context_new_rvalue_from_int (
+                                   comp.ctxt,
+                                   comp.cast_types[NUM_CAST_TYPES],
+                                    0));
+
+  gcc_jit_block_add_assignment (entry_block, NULL,
+                                gcc_jit_lvalue_access_field (tmp_union, NULL,
+                                  comp.cast_union_fields[from_index]),
+                                gcc_jit_param_as_rvalue (param));
+
+  gcc_jit_block_end_with_return (entry_block,
+                                 NULL,
+                                 gcc_jit_rvalue_access_field (
+                                   gcc_jit_lvalue_as_rvalue (tmp_union),
+                                   NULL,
+                                   comp.cast_union_fields[to_index]));
+
+  return result;
+}
+
+static void
+define_cast_functions (void)
+{
+  struct cast_type cast_types[NUM_CAST_TYPES]
+    = { { comp.bool_type, "bool", sizeof (bool), kind_unsigned },
+        { comp.char_ptr_type, "char_ptr", sizeof (char *), kind_pointer },
+        { comp.int_type, "int", sizeof (int), kind_signed },
+        { comp.lisp_cons_ptr_type, "cons_ptr", sizeof (struct Lisp_Cons *),
+          kind_pointer },
+        { comp.lisp_obj_ptr_type, "lisp_obj_ptr", sizeof (Lisp_Object *),
+          kind_pointer },
+        { comp.lisp_word_tag_type, "lisp_word_tag", sizeof (Lisp_Word_tag),
+          kind_unsigned },
+        { comp.lisp_word_type, "lisp_word", sizeof (Lisp_Word),
+          LISP_WORDS_ARE_POINTERS ? kind_pointer : kind_signed },
+        { comp.long_long_type, "long_long", sizeof (long long), kind_signed },
+        { comp.long_type, "long", sizeof (long), kind_signed },
+        { comp.ptrdiff_type, "ptrdiff", sizeof (ptrdiff_t), kind_signed },
+        { comp.uintptr_type, "uintptr", sizeof (uintptr_t), kind_unsigned },
+        { comp.unsigned_long_long_type, "unsigned_long_long",
+          sizeof (unsigned long long), kind_unsigned },
+        { comp.unsigned_long_type, "unsigned_long", sizeof (unsigned long),
+          kind_unsigned },
+        { comp.unsigned_type, "unsigned", sizeof (unsigned), kind_unsigned },
+        { comp.void_ptr_type, "void_ptr", sizeof (void*), kind_pointer } };
+
+  /* Find the biggest size.  It should be unsigned long long, but to be
+     sure we find it programmatically.  */
+  size_t biggest_size = 0;
+  for (int i = 0; i < NUM_CAST_TYPES; ++i)
+    biggest_size = max (biggest_size, cast_types[i].bytes_size);
+
+  /* Define the union used for casting.  */
+  for (int i = 0; i < NUM_CAST_TYPES; ++i)
+    {
+      comp.cast_types[i] = cast_types[i].type;
+      comp.cast_union_fields[i] = gcc_jit_context_new_field (comp.ctxt,
+                                    NULL,
+                                    cast_types[i].type,
+                                    cast_types[i].name);
+      comp.cast_type_names[i] = cast_types[i].name;
+      comp.cast_type_sizes[i] = cast_types[i].bytes_size;
+      comp.cast_type_kind[i] = cast_types[i].kind;
+    }
+
+  gcc_jit_type *biggest_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                             biggest_size,
+                                                             false);
+  comp.cast_types[NUM_CAST_TYPES] = biggest_type;
+  comp.cast_union_fields[NUM_CAST_TYPES] =
+    gcc_jit_context_new_field (comp.ctxt, NULL, biggest_type, "biggest_type");
+  comp.cast_type_names[NUM_CAST_TYPES] = "biggest_type";
+  comp.cast_type_sizes[NUM_CAST_TYPES] = biggest_size;
+  comp.cast_type_kind[NUM_CAST_TYPES] = kind_unsigned;
+
+  comp.cast_union_type =
+    gcc_jit_context_new_union_type (comp.ctxt,
+                                   NULL,
+                                   "cast_union",
+                                   NUM_CAST_TYPES + 1,
+                                   comp.cast_union_fields);
+
+  /* Define the cast functions using a matrix.  */
+  for (int i = 0; i < NUM_CAST_TYPES; ++i)
+    for (int j = 0; j < NUM_CAST_TYPES; ++j)
+        comp.cast_functions_from_to[i][j] =
+          define_cast_from_to (cast_types[i], i, cast_types[j], j);
+}
+
+static void
+define_CHECK_TYPE (void)
+{
+  gcc_jit_param *param[] =
+    { gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.int_type,
+                                "ok"),
+      gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.lisp_obj_type,
+                                "predicate"),
+      gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.lisp_obj_type,
+                                "x") };
+  comp.check_type =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.void_type,
+                                 "CHECK_TYPE",
+                                 3,
+                                 param,
+                                 0);
+  gcc_jit_rvalue *ok = gcc_jit_param_as_rvalue (param[0]);
+  gcc_jit_rvalue *predicate = gcc_jit_param_as_rvalue (param[1]);
+  gcc_jit_rvalue *x = gcc_jit_param_as_rvalue (param[2]);
+
+  DECL_BLOCK (entry_block, comp.check_type);
+  DECL_BLOCK (ok_block, comp.check_type);
+  DECL_BLOCK (not_ok_block, comp.check_type);
+
+  comp.block = entry_block;
+  comp.func = comp.check_type;
+
+  emit_cond_jump (ok, ok_block, not_ok_block);
+
+  gcc_jit_block_end_with_void_return (ok_block, NULL);
+
+  comp.block = not_ok_block;
+
+  gcc_jit_rvalue *wrong_type_args[] = { predicate, x };
+
+  gcc_jit_block_add_eval (comp.block,
+                         NULL,
+                         emit_call (intern_c_string ("wrong_type_argument"),
+                                    comp.void_type, 2, wrong_type_args,
+                                    false));
+
+  gcc_jit_block_end_with_void_return (not_ok_block, NULL);
+}
+
+/* Define a substitute for CAR as always inlined function.  */
+
+static void
+define_CAR_CDR (void)
+{
+  gcc_jit_function *func[2];
+  char const *f_name[] = { "CAR", "CDR" };
+  for (int i = 0; i < 2; i++)
+    {
+      gcc_jit_param *param[] =
+       { gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.lisp_obj_type,
+                                    "c"),
+         gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.bool_type,
+                                    "cert_cons") };
+      /* TODO: understand why after ipa-prop pass gcc is less keen on inlining
+        and as consequence can refuse to compile these. (see dhrystone.el)
+        Flag this and all the one involved in ipa-prop as
+        GCC_JIT_FUNCTION_INTERNAL not to fail compilation in case.
+        This seems at least to have no perf downside.  */
+      func[i] =
+       gcc_jit_context_new_function (comp.ctxt, NULL,
+                                     GCC_JIT_FUNCTION_INTERNAL,
+                                     comp.lisp_obj_type,
+                                     f_name[i],
+                                     2, param, 0);
+
+      gcc_jit_rvalue *c = gcc_jit_param_as_rvalue (param[0]);
+      DECL_BLOCK (entry_block, func[i]);
+      DECL_BLOCK (is_cons_b, func[i]);
+      DECL_BLOCK (not_a_cons_b, func[i]);
+      comp.block = entry_block;
+      comp.func = func[i];
+      emit_cond_jump (emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_OR,
+                                     comp.bool_type,
+                                     gcc_jit_param_as_rvalue (param[1]),
+                                     emit_CONSP (c)),
+                     is_cons_b,
+                     not_a_cons_b);
+      comp.block = is_cons_b;
+      if (i == 0)
+       gcc_jit_block_end_with_return (comp.block, NULL, emit_XCAR (c));
+      else
+       gcc_jit_block_end_with_return (comp.block, NULL, emit_XCDR (c));
+
+      comp.block = not_a_cons_b;
+
+      DECL_BLOCK (is_nil_b, func[i]);
+      DECL_BLOCK (not_nil_b, func[i]);
+
+      emit_cond_jump (emit_NILP (c), is_nil_b, not_nil_b);
+
+      comp.block = is_nil_b;
+      gcc_jit_block_end_with_return (comp.block,
+                                    NULL,
+                                    emit_lisp_obj_rval (Qnil));
+
+      comp.block = not_nil_b;
+      gcc_jit_rvalue *wrong_type_args[] =
+       { emit_lisp_obj_rval (Qlistp), c };
+
+      gcc_jit_block_add_eval (comp.block,
+                             NULL,
+                             emit_call (intern_c_string 
("wrong_type_argument"),
+                                        comp.void_type, 2, wrong_type_args,
+                                        false));
+      gcc_jit_block_end_with_return (comp.block,
+                                    NULL,
+                                    emit_lisp_obj_rval (Qnil));
+    }
+  comp.car = func[0];
+  comp.cdr = func[1];
+}
+
+static void
+define_setcar_setcdr (void)
+{
+  char const *f_name[] = { "setcar", "setcdr" };
+  char const *par_name[] = { "new_car", "new_cdr" };
+
+  for (int i = 0; i < 2; i++)
+    {
+      gcc_jit_param *cell =
+       gcc_jit_context_new_param (comp.ctxt,
+                                  NULL,
+                                  comp.lisp_obj_type,
+                                  "cell");
+      gcc_jit_param *new_el =
+       gcc_jit_context_new_param (comp.ctxt,
+                                  NULL,
+                                  comp.lisp_obj_type,
+                                  par_name[i]);
+
+      gcc_jit_param *param[] =
+       { cell,
+         new_el,
+         gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.bool_type,
+                                    "cert_cons") };
+
+      gcc_jit_function **f_ref = !i ? &comp.setcar : &comp.setcdr;
+      *f_ref = gcc_jit_context_new_function (comp.ctxt, NULL,
+                                            GCC_JIT_FUNCTION_INTERNAL,
+                                            comp.lisp_obj_type,
+                                            f_name[i],
+                                            3, param, 0);
+      DECL_BLOCK (entry_block, *f_ref);
+      comp.func = *f_ref;
+      comp.block = entry_block;
+
+      /* CHECK_CONS (cell);  */
+      emit_CHECK_CONS (gcc_jit_param_as_rvalue (cell));
+
+      /* CHECK_IMPURE (cell, XCONS (cell));  */
+      gcc_jit_rvalue *args[] =
+       { gcc_jit_param_as_rvalue (cell),
+         emit_XCONS (gcc_jit_param_as_rvalue (cell)) };
+
+      gcc_jit_block_add_eval (entry_block,
+                             NULL,
+                             gcc_jit_context_new_call (comp.ctxt,
+                                                       NULL,
+                                                       comp.check_impure,
+                                                       2,
+                                                       args));
+
+      /* XSETCDR (cell, newel);  */
+      if (!i)
+       emit_XSETCAR (gcc_jit_param_as_rvalue (cell),
+                     gcc_jit_param_as_rvalue (new_el));
+      else
+       emit_XSETCDR (gcc_jit_param_as_rvalue (cell),
+                     gcc_jit_param_as_rvalue (new_el));
+
+      /* return newel;  */
+      gcc_jit_block_end_with_return (entry_block,
+                                    NULL,
+                                    gcc_jit_param_as_rvalue (new_el));
+    }
+}
+
+/*
+   Define a substitute for Fadd1 Fsub1.
+   Currently expose just fixnum arithmetic.
+*/
+
+static void
+define_add1_sub1 (void)
+{
+  gcc_jit_block *bb_orig = comp.block;
+  gcc_jit_function *func[2];
+  char const *f_name[] = { "add1", "sub1" };
+  char const *fall_back_func[] = { "1+", "1-" };
+  enum gcc_jit_binary_op op[] =
+    { GCC_JIT_BINARY_OP_PLUS, GCC_JIT_BINARY_OP_MINUS };
+  for (ptrdiff_t i = 0; i < 2; i++)
+    {
+      gcc_jit_param *param[] =
+       { gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.lisp_obj_type,
+                                    "n"),
+         gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.bool_type,
+                                    "cert_fixnum") };
+      comp.func = func[i] =
+       gcc_jit_context_new_function (comp.ctxt, NULL,
+                                     GCC_JIT_FUNCTION_INTERNAL,
+                                     comp.lisp_obj_type,
+                                     f_name[i],
+                                     2,
+                                     param, 0);
+      DECL_BLOCK (entry_block, func[i]);
+      DECL_BLOCK (inline_block, func[i]);
+      DECL_BLOCK (fcall_block, func[i]);
+
+      comp.block = entry_block;
+
+      /* cert_fixnum ||
+        ((FIXNUMP (n) && XFIXNUM (n) != MOST_POSITIVE_FIXNUM
+        ? (XFIXNUM (n) + 1)
+        : Fadd1 (n)) */
+
+      gcc_jit_rvalue *n = gcc_jit_param_as_rvalue (param[0]);
+      gcc_jit_rvalue *n_fixnum = emit_XFIXNUM (n);
+      gcc_jit_rvalue *sure_fixnum =
+       emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_OR,
+                       comp.bool_type,
+                       gcc_jit_param_as_rvalue (param[1]),
+                       emit_FIXNUMP (n));
+      emit_cond_jump (
+       emit_binary_op (
+         GCC_JIT_BINARY_OP_LOGICAL_AND,
+         comp.bool_type,
+         sure_fixnum,
+         gcc_jit_context_new_comparison (
+           comp.ctxt,
+           NULL,
+           GCC_JIT_COMPARISON_NE,
+           n_fixnum,
+           i == 0
+           ? emit_rvalue_from_emacs_int (MOST_POSITIVE_FIXNUM)
+           : emit_rvalue_from_emacs_int (MOST_NEGATIVE_FIXNUM))),
+       inline_block,
+       fcall_block);
+
+      comp.block = inline_block;
+      gcc_jit_rvalue *inline_res =
+       emit_binary_op (op[i], comp.emacs_int_type, n_fixnum, comp.one);
+
+      gcc_jit_block_end_with_return (inline_block,
+                                    NULL,
+                                    emit_make_fixnum (inline_res));
+
+      comp.block = fcall_block;
+      gcc_jit_rvalue *call_res = emit_call (intern_c_string 
(fall_back_func[i]),
+                                           comp.lisp_obj_type, 1, &n, false);
+      gcc_jit_block_end_with_return (fcall_block,
+                                    NULL,
+                                    call_res);
+    }
+  comp.block = bb_orig;
+  comp.add1 = func[0];
+  comp.sub1 = func[1];
+}
+
+static void
+define_negate (void)
+{
+  gcc_jit_block *bb_orig = comp.block;
+  gcc_jit_param *param[] =
+       { gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.lisp_obj_type,
+                                    "n"),
+         gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.bool_type,
+                                    "cert_fixnum") };
+
+  comp.func = comp.negate =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.lisp_obj_type,
+                                 "negate",
+                                 2, param, 0);
+
+  DECL_BLOCK (entry_block, comp.negate);
+  DECL_BLOCK (inline_block, comp.negate);
+  DECL_BLOCK (fcall_block, comp.negate);
+
+  comp.block = entry_block;
+
+  /* (cert_fixnum || FIXNUMP (TOP)) && XFIXNUM (TOP) != MOST_NEGATIVE_FIXNUM
+     ? make_fixnum (- XFIXNUM (TOP)) : Fminus (1, &TOP))  */
+
+  gcc_jit_lvalue *n = gcc_jit_param_as_lvalue (param[0]);
+  gcc_jit_rvalue *n_fixnum = emit_XFIXNUM (gcc_jit_lvalue_as_rvalue (n));
+  gcc_jit_rvalue *sure_fixnum =
+    emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_OR,
+                   comp.bool_type,
+                   gcc_jit_param_as_rvalue (param[1]),
+                   emit_FIXNUMP (gcc_jit_lvalue_as_rvalue (n)));
+
+  emit_cond_jump (emit_binary_op (GCC_JIT_BINARY_OP_LOGICAL_AND,
+                                 comp.bool_type,
+                                 sure_fixnum,
+                                 gcc_jit_context_new_comparison (
+                                   comp.ctxt,
+                                   NULL,
+                                   GCC_JIT_COMPARISON_NE,
+                                   n_fixnum,
+                                   emit_rvalue_from_emacs_int (
+                                      MOST_NEGATIVE_FIXNUM))),
+                 inline_block,
+                 fcall_block);
+
+  comp.block = inline_block;
+  gcc_jit_rvalue *inline_res =
+    gcc_jit_context_new_unary_op (comp.ctxt,
+                                 NULL,
+                                 GCC_JIT_UNARY_OP_MINUS,
+                                 comp.emacs_int_type,
+                                 n_fixnum);
+
+  gcc_jit_block_end_with_return (inline_block,
+                                NULL,
+                                emit_make_fixnum (inline_res));
+
+  comp.block = fcall_block;
+  gcc_jit_rvalue *call_res = emit_call_ref (Qminus, 1, n, false);
+  gcc_jit_block_end_with_return (fcall_block,
+                                NULL,
+                                call_res);
+  comp.block = bb_orig;
+}
+
+/* Define a substitute for PSEUDOVECTORP as always inlined function.  */
+
+static void
+define_PSEUDOVECTORP (void)
+{
+  gcc_jit_param *param[] =
+    { gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.lisp_obj_type,
+                                "a"),
+      gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.int_type,
+                                "code") };
+
+  comp.pseudovectorp =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.bool_type,
+                                 "PSEUDOVECTORP",
+                                 2,
+                                 param,
+                                 0);
+
+  DECL_BLOCK (entry_block, comp.pseudovectorp);
+  DECL_BLOCK (ret_false_b, comp.pseudovectorp);
+  DECL_BLOCK (call_pseudovector_typep_b, comp.pseudovectorp);
+
+  comp.block = entry_block;
+  comp.func = comp.pseudovectorp;
+
+  emit_cond_jump (emit_VECTORLIKEP (gcc_jit_param_as_rvalue (param[0])),
+                 call_pseudovector_typep_b,
+                 ret_false_b);
+
+  comp.block = ret_false_b;
+  gcc_jit_block_end_with_return (ret_false_b,
+                                NULL,
+                                gcc_jit_context_new_rvalue_from_int (
+                                  comp.ctxt,
+                                  comp.bool_type,
+                                  false));
+
+  gcc_jit_rvalue *args[] =
+    { gcc_jit_param_as_rvalue (param[0]),
+      gcc_jit_param_as_rvalue (param[1]) };
+  comp.block = call_pseudovector_typep_b;
+  /* FIXME use XUNTAG now that's available.  */
+  gcc_jit_block_end_with_return (
+    call_pseudovector_typep_b,
+    NULL,
+    emit_call (intern_c_string ("helper_PSEUDOVECTOR_TYPEP_XUNTAG"),
+              comp.bool_type, 2, args, false));
+}
+
+static void
+define_CHECK_IMPURE (void)
+{
+  gcc_jit_param *param[] =
+    { gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.lisp_obj_type,
+                                "obj"),
+      gcc_jit_context_new_param (comp.ctxt,
+                                NULL,
+                                comp.void_ptr_type,
+                                "ptr") };
+  comp.check_impure =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.void_type,
+                                 "CHECK_IMPURE",
+                                 2,
+                                 param,
+                                 0);
+
+    DECL_BLOCK (entry_block, comp.check_impure);
+    DECL_BLOCK (err_block, comp.check_impure);
+    DECL_BLOCK (ok_block, comp.check_impure);
+
+    comp.block = entry_block;
+    comp.func = comp.check_impure;
+
+    emit_cond_jump (emit_PURE_P (gcc_jit_param_as_rvalue (param[0])), /* FIXME 
*/
+                   err_block,
+                   ok_block);
+    gcc_jit_block_end_with_void_return (ok_block, NULL);
+
+    gcc_jit_rvalue *pure_write_error_arg =
+      gcc_jit_param_as_rvalue (param[0]);
+
+    comp.block = err_block;
+    gcc_jit_block_add_eval (comp.block,
+                           NULL,
+                           emit_call (intern_c_string ("pure_write_error"),
+                                      comp.void_type, 1,&pure_write_error_arg,
+                                      false));
+
+    gcc_jit_block_end_with_void_return (err_block, NULL);
+}
+
+static void
+define_maybe_gc_or_quit (void)
+{
+
+  /*
+    void
+    maybe_gc_or_quit (void)
+    {
+      static unsigned quitcounter;
+     inc:
+      quitcounter++;
+      if (quitcounter >> 14) goto maybe_do_it else goto pass;
+     maybe_do_it:
+          quitcounter = 0;
+          maybe_gc ();
+          maybe_quit ();
+          return;
+     pass:
+          return;
+    }
+  */
+
+  gcc_jit_block *bb_orig = comp.block;
+
+  gcc_jit_lvalue *quitcounter =
+    gcc_jit_context_new_global (
+      comp.ctxt,
+      NULL,
+      GCC_JIT_GLOBAL_INTERNAL,
+      comp.unsigned_type,
+      "quitcounter");
+
+  comp.func = comp.maybe_gc_or_quit =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.void_type,
+                                 "maybe_gc_quit",
+                                 0, NULL, 0);
+  DECL_BLOCK (increment_block, comp.maybe_gc_or_quit);
+  DECL_BLOCK (maybe_do_it_block, comp.maybe_gc_or_quit);
+  DECL_BLOCK (pass_block, comp.maybe_gc_or_quit);
+
+  comp.block = increment_block;
+
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    quitcounter,
+    emit_binary_op (GCC_JIT_BINARY_OP_PLUS,
+                   comp.unsigned_type,
+                   gcc_jit_lvalue_as_rvalue (quitcounter),
+                   gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                        comp.unsigned_type,
+                                                        1)));
+  emit_cond_jump (
+    emit_binary_op (GCC_JIT_BINARY_OP_RSHIFT,
+                   comp.unsigned_type,
+                   gcc_jit_lvalue_as_rvalue (quitcounter),
+                   gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                        comp.unsigned_type,
+                                                        9)),
+    /* 9 translates into checking for GC or quit every 512 calls to
+       'maybe_gc_quit'.  This is the smallest value I could find with
+       no performance impact running elisp-banechmarks and the same
+       used by the byte interpreter (see 'exec_byte_code').  */
+    maybe_do_it_block,
+    pass_block);
+
+  comp.block = maybe_do_it_block;
+
+  gcc_jit_block_add_assignment (
+    comp.block,
+    NULL,
+    quitcounter,
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                        comp.unsigned_type,
+                                        0));
+  gcc_jit_block_add_eval (comp.block, NULL,
+                         emit_call (intern_c_string ("maybe_gc"),
+                                    comp.void_type, 0, NULL, false));
+  gcc_jit_block_add_eval (comp.block, NULL,
+                         emit_call (intern_c_string ("maybe_quit"),
+                                    comp.void_type, 0, NULL, false));
+  gcc_jit_block_end_with_void_return (comp.block, NULL);
+
+  gcc_jit_block_end_with_void_return (pass_block, NULL);
+
+  comp.block = bb_orig;
+}
+
+/* Define a function to convert boolean into t or nil */
+
+static void
+define_bool_to_lisp_obj (void)
+{
+  /* x ? Qt : Qnil */
+  gcc_jit_param *param = gcc_jit_context_new_param (comp.ctxt,
+                                                   NULL,
+                                                   comp.bool_type,
+                                                   "x");
+  comp.bool_to_lisp_obj =
+    gcc_jit_context_new_function (comp.ctxt, NULL,
+                                 GCC_JIT_FUNCTION_INTERNAL,
+                                 comp.lisp_obj_type,
+                                 "bool_to_lisp_obj",
+                                 1,
+                                 &param,
+                                 0);
+  DECL_BLOCK (entry_block, comp.bool_to_lisp_obj);
+  DECL_BLOCK (ret_t_block, comp.bool_to_lisp_obj);
+  DECL_BLOCK (ret_nil_block, comp.bool_to_lisp_obj);
+  comp.block = entry_block;
+  comp.func = comp.bool_to_lisp_obj;
+
+  emit_cond_jump (gcc_jit_param_as_rvalue (param),
+                 ret_t_block,
+                 ret_nil_block);
+
+  comp.block = ret_t_block;
+  gcc_jit_block_end_with_return (ret_t_block,
+                                NULL,
+                                emit_lisp_obj_rval (Qt));
+
+  comp.block = ret_nil_block;
+  gcc_jit_block_end_with_return (ret_nil_block,
+                                NULL,
+                                emit_lisp_obj_rval (Qnil));
+}
+
+static gcc_jit_function *
+declare_lex_function (Lisp_Object func)
+{
+  gcc_jit_function *res;
+  Lisp_Object c_name = CALL1I (comp-func-c-name, func);
+  Lisp_Object args = CALL1I (comp-func-l-args, func);
+  bool nargs = !NILP (CALL1I (comp-nargs-p, args));
+  USE_SAFE_ALLOCA;
+
+  if (!nargs)
+    {
+      EMACS_INT max_args = XFIXNUM (CALL1I (comp-args-max, args));
+      eassert (max_args < INT_MAX);
+      gcc_jit_type **type = SAFE_ALLOCA (max_args * sizeof (*type));
+      for (ptrdiff_t i = 0; i < max_args; i++)
+       type[i] = comp.lisp_obj_type;
+
+      gcc_jit_param **params = SAFE_ALLOCA (max_args * sizeof (*params));
+      for (int i = 0; i < max_args; ++i)
+       params[i] = gcc_jit_context_new_param (comp.ctxt,
+                                             NULL,
+                                             type[i],
+                                             format_string ("par_%d", i));
+      res = gcc_jit_context_new_function (comp.ctxt, NULL,
+                                         GCC_JIT_FUNCTION_EXPORTED,
+                                         comp.lisp_obj_type,
+                                         SSDATA (c_name),
+                                         max_args,
+                                         params,
+                                         0);
+    }
+  else
+    {
+      gcc_jit_param *params[] =
+       { gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.ptrdiff_type,
+                                    "nargs"),
+         gcc_jit_context_new_param (comp.ctxt,
+                                    NULL,
+                                    comp.lisp_obj_ptr_type,
+                                    "args") };
+      res =
+       gcc_jit_context_new_function (comp.ctxt,
+                                     NULL,
+                                     GCC_JIT_FUNCTION_EXPORTED,
+                                     comp.lisp_obj_type,
+                                     SSDATA (c_name),
+                                     ARRAYELTS (params), params, 0);
+    }
+  SAFE_FREE ();
+  return res;
+}
+
+/* Declare a function being compiled and add it to comp.exported_funcs_h.  */
+
+static void
+declare_function (Lisp_Object func)
+{
+  gcc_jit_function *gcc_func =
+    !NILP (CALL1I (comp-func-l-p, func))
+    ? declare_lex_function (func)
+    : gcc_jit_context_new_function (comp.ctxt,
+                                   NULL,
+                                   GCC_JIT_FUNCTION_EXPORTED,
+                                   comp.lisp_obj_type,
+                                   SSDATA (CALL1I (comp-func-c-name, func)),
+                                   0, NULL, 0);
+  Fputhash (CALL1I (comp-func-c-name, func),
+           make_mint_ptr (gcc_func),
+           comp.exported_funcs_h);
+}
+
+static void
+compile_function (Lisp_Object func)
+{
+  USE_SAFE_ALLOCA;
+  comp.frame_size = XFIXNUM (CALL1I (comp-func-frame-size, func));
+  eassert (comp.frame_size < INT_MAX);
+
+  comp.func = xmint_pointer (Fgethash (CALL1I (comp-func-c-name, func),
+                                      comp.exported_funcs_h, Qnil));
+
+  comp.func_has_non_local = !NILP (CALL1I (comp-func-has-non-local, func));
+  comp.func_speed = XFIXNUM (CALL1I (comp-func-speed, func));
+
+  comp.func_relocs_local =
+    gcc_jit_function_new_local (comp.func,
+                               NULL,
+                               comp.func_relocs_ptr_type,
+                               "freloc");
+
+  comp.frame = SAFE_ALLOCA (comp.frame_size * sizeof (*comp.frame));
+  if (comp.func_has_non_local || !comp.func_speed)
+    {
+      /* FIXME: See bug#42360.  */
+      gcc_jit_lvalue *arr =
+        gcc_jit_function_new_local (
+          comp.func,
+          NULL,
+          gcc_jit_context_new_array_type (comp.ctxt,
+                                          NULL,
+                                          comp.lisp_obj_type,
+                                          comp.frame_size),
+          "frame");
+
+      for (ptrdiff_t i = 0; i < comp.frame_size; ++i)
+       comp.frame[i] =
+          gcc_jit_context_new_array_access (
+            comp.ctxt,
+            NULL,
+            gcc_jit_lvalue_as_rvalue (arr),
+            gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                 comp.int_type,
+                                                 i));
+    }
+  else
+    for (ptrdiff_t i = 0; i < comp.frame_size; ++i)
+      comp.frame[i] =
+       gcc_jit_function_new_local (comp.func,
+                                   NULL,
+                                   comp.lisp_obj_type,
+                                   format_string ("slot_%td", i));
+
+  comp.scratch = NULL;
+
+  comp.loc_handler =  gcc_jit_function_new_local (comp.func,
+                                                 NULL,
+                                                 comp.handler_ptr_type,
+                                                 "c");
+
+  comp.func_blocks_h = CALLN (Fmake_hash_table);
+
+  /* Pre-declare all basic blocks to gcc.
+     The "entry" block must be declared as first.  */
+  declare_block (Qentry);
+  Lisp_Object blocks = CALL1I (comp-func-blocks, func);
+  struct Lisp_Hash_Table *ht = XHASH_TABLE (blocks);
+  for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (ht); i++)
+    {
+      Lisp_Object block_name = HASH_KEY (ht, i);
+      if (!EQ (block_name, Qentry)
+         && !EQ (block_name, Qunbound))
+       declare_block (block_name);
+    }
+
+  gcc_jit_block_add_assignment (retrive_block (Qentry),
+                               NULL,
+                               comp.func_relocs_local,
+                               gcc_jit_lvalue_as_rvalue (comp.func_relocs));
+
+
+  for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (ht); i++)
+    {
+      Lisp_Object block_name = HASH_KEY (ht, i);
+      if (!EQ (block_name, Qunbound))
+       {
+         Lisp_Object block = HASH_VALUE (ht, i);
+         Lisp_Object insns = CALL1I (comp-block-insns, block);
+         if (NILP (block) || NILP (insns))
+           xsignal1 (Qnative_ice,
+                     build_string ("basic block is missing or empty"));
+
+         comp.block = retrive_block (block_name);
+         while (CONSP (insns))
+           {
+             Lisp_Object insn = XCAR (insns);
+             emit_limple_insn (insn);
+             insns = XCDR (insns);
+           }
+       }
+    }
+  const char *err =  gcc_jit_context_get_first_error (comp.ctxt);
+  if (err)
+    xsignal3 (Qnative_ice,
+             build_string ("failing to compile function"),
+             CALL1I (comp-func-name, func),
+             build_string (err));
+  SAFE_FREE ();
+}
+
+
+/**********************************/
+/* Entry points exposed to lisp.  */
+/**********************************/
+
+/* In use by Fcomp_el_to_eln_filename.  */
+static Lisp_Object loadsearch_re_list;
+
+static Lisp_Object
+make_directory_wrapper (Lisp_Object directory)
+{
+  CALL2I (make-directory, directory, Qt);
+  return Qnil;
+}
+
+static Lisp_Object
+make_directory_wrapper_1 (Lisp_Object ignore)
+{
+  return Qt;
+}
+
+DEFUN ("comp-el-to-eln-rel-filename", Fcomp_el_to_eln_rel_filename,
+       Scomp_el_to_eln_rel_filename, 1, 1, 0,
+       doc: /* Return the corresponding .eln relative filename.  */)
+  (Lisp_Object filename)
+{
+  CHECK_STRING (filename);
+
+  /* Use `file-truename' or fall back to `expand-file-name' when the
+     first is not available (bug#44701).
+
+     `file-truename' is not available only for a short phases of the
+     bootstrap before file.el is loaded, given we do not symlink
+     inside the build directory this should work.  */
+  filename = NILP (Ffboundp (intern_c_string ("file-truename")))
+    ? Fexpand_file_name (filename, Qnil)
+    : CALL1I (file-truename, filename);
+
+  if (NILP (Ffile_exists_p (filename)))
+    xsignal1 (Qfile_missing, filename);
+
+#ifdef WINDOWSNT
+  filename = Fw32_long_file_name (filename);
+#endif
+
+  Lisp_Object content_hash = comp_hash_source_file (filename);
+
+  if (suffix_p (filename, ".gz"))
+    filename = Fsubstring (filename, Qnil, make_fixnum (-3));
+
+  /* We create eln filenames with an hash in order to look-up these
+     starting from the source filename, IOW have a relation
+
+     /absolute/path/filename.el + content ->
+     eln-cache/filename-path_hash-content_hash.eln.
+
+     'dlopen' can return the same handle if two shared with the same
+     filename are loaded in two different times (even if the first was
+     deleted!).  To prevent this scenario the source file content is
+     included in the hashing algorithm.
+
+     As at any point in time no more then one file can exist with the
+     same filename, should be possible to clean up all
+     filename-path_hash-* except the most recent one (or the new one
+     being recompiled).
+
+     As installing .eln files compiled during the build changes their
+     absolute path we need an hashing mechanism that is not sensitive
+     to that.  For this we replace if match PATH_DUMPLOADSEARCH or
+     *PATH_REL_LOADSEARCH with '//' before computing the hash.  */
+
+  if (NILP (loadsearch_re_list))
+    {
+      Lisp_Object sys_re =
+       concat2 (build_string ("\\`[[:ascii:]]+"),
+                Fregexp_quote (build_string ("/" PATH_REL_LOADSEARCH "/")));
+      Lisp_Object dump_load_search = build_string (PATH_DUMPLOADSEARCH "/");
+#ifdef WINDOWSNT
+      dump_load_search = Fw32_long_file_name (dump_load_search);
+#endif
+      loadsearch_re_list = list2 (sys_re, Fregexp_quote (dump_load_search));
+    }
+
+  Lisp_Object lds_re_tail = loadsearch_re_list;
+  FOR_EACH_TAIL (lds_re_tail)
+    {
+      Lisp_Object match_idx =
+       Fstring_match (XCAR (lds_re_tail), filename, Qnil);
+      if (EQ (match_idx, make_fixnum (0)))
+       {
+         filename =
+           Freplace_match (build_string ("//"), Qt, Qt, filename, Qnil);
+         break;
+       }
+    }
+  Lisp_Object separator = build_string ("-");
+  Lisp_Object path_hash = comp_hash_string (filename);
+  filename = concat2 (Ffile_name_nondirectory (Fsubstring (filename, Qnil,
+                                                          make_fixnum (-3))),
+                     separator);
+  Lisp_Object hash = concat3 (path_hash, separator, content_hash);
+  return concat3 (filename, hash, build_string (NATIVE_ELISP_SUFFIX));
+}
+
+DEFUN ("comp-el-to-eln-filename", Fcomp_el_to_eln_filename,
+       Scomp_el_to_eln_filename, 1, 2, 0,
+       doc: /* Return the .eln filename for source FILENAME to used
+for new compilations.
+If BASE-DIR is non-nil use it as a base directory, look for a suitable
+directory in `comp-eln-load-path' otherwise.  */)
+  (Lisp_Object filename, Lisp_Object base_dir)
+{
+  Lisp_Object source_filename = filename;
+  filename = Fcomp_el_to_eln_rel_filename (filename);
+
+  /* If base_dir was not specified search inside Vcomp_eln_load_path
+     for the first directory where we have write access.  */
+  if (NILP (base_dir))
+    {
+      Lisp_Object eln_load_paths = Vcomp_eln_load_path;
+      FOR_EACH_TAIL (eln_load_paths)
+       {
+         Lisp_Object dir = XCAR (eln_load_paths);
+         if (!NILP (Ffile_exists_p (dir)))
+           {
+             if (!NILP (Ffile_writable_p (dir)))
+               {
+                 base_dir = dir;
+                 break;
+               }
+           }
+         else
+           {
+             /* Try to create the directory and if succeeds use it.  */
+             if (NILP (internal_condition_case_1 (make_directory_wrapper,
+                                                  dir, Qt,
+                                                  make_directory_wrapper_1)))
+               {
+                 base_dir = dir;
+                 break;
+               }
+           }
+       }
+      if (NILP (base_dir))
+       error ("Cannot find suitable directory for output in "
+              "`comp-native-load-path'.");
+    }
+
+  if (!file_name_absolute_p (SSDATA (base_dir)))
+    base_dir = Fexpand_file_name (base_dir, Vinvocation_directory);
+
+  /* In case the file being compiled is found in 'LISP_PRELOADED' or
+     `comp-file-preloaded-p' is non-nil target for output the
+     'preloaded' subfolder.  */
+  Lisp_Object lisp_preloaded =
+    Fgetenv_internal (build_string ("LISP_PRELOADED"), Qnil);
+  base_dir = Fexpand_file_name (Vcomp_native_version_dir, base_dir);
+  if (comp_file_preloaded_p
+      || (!NILP (lisp_preloaded)
+         && !NILP (Fmember (CALL1I (file-name-base, source_filename),
+                            Fmapcar (intern_c_string ("file-name-base"),
+                                     CALL1I (split-string, lisp_preloaded))))))
+    base_dir = Fexpand_file_name (build_string ("preloaded"), base_dir);
+
+  return Fexpand_file_name (filename, base_dir);
+}
+
+DEFUN ("comp--install-trampoline", Fcomp__install_trampoline,
+       Scomp__install_trampoline, 2, 2, 0,
+       doc: /* Install a TRAMPOLINE for primitive SUBR-NAME.  */)
+  (Lisp_Object subr_name, Lisp_Object trampoline)
+{
+  CHECK_SYMBOL (subr_name);
+  CHECK_SUBR (trampoline);
+  Lisp_Object orig_subr = Fsymbol_function (subr_name);
+  CHECK_SUBR (orig_subr);
+
+  /* FIXME: add a post dump load trampoline machinery to remove this
+     check.  */
+  if (will_dump_p ())
+    signal_error ("Trying to advice unexpected primitive before dumping",
+                 subr_name);
+
+  Lisp_Object subr_l = Vcomp_subr_list;
+  ptrdiff_t i = ARRAYELTS (helper_link_table);
+  FOR_EACH_TAIL (subr_l)
+    {
+      Lisp_Object subr = XCAR (subr_l);
+      if (EQ (subr, orig_subr))
+       {
+         freloc.link_table[i] = XSUBR (trampoline)->function.a0;
+         Fputhash (subr_name, trampoline, Vcomp_installed_trampolines_h);
+         return Qt;
+       }
+      i++;
+    }
+    signal_error ("Trying to install trampoline for non existent subr",
+                 subr_name);
+    return Qnil;
+}
+
+DEFUN ("comp--init-ctxt", Fcomp__init_ctxt, Scomp__init_ctxt,
+       0, 0, 0,
+       doc: /* Initialize the native compiler context.
+Return t on success.  */)
+  (void)
+{
+  load_gccjit_if_necessary (true);
+
+  if (comp.ctxt)
+    {
+      xsignal1 (Qnative_ice,
+               build_string ("compiler context already taken"));
+      return Qnil;
+    }
+
+  if (NILP (comp.emitter_dispatcher))
+    {
+      /* Move this into syms_of_comp the day will be dumpable.  */
+      comp.emitter_dispatcher = CALLN (Fmake_hash_table);
+      register_emitter (Qset_internal, emit_set_internal);
+      register_emitter (Qhelper_unbind_n, emit_simple_limple_call_lisp_ret);
+      register_emitter (Qhelper_unwind_protect,
+                       emit_simple_limple_call_void_ret);
+      register_emitter (Qrecord_unwind_current_buffer,
+                       emit_simple_limple_call_lisp_ret);
+      register_emitter (Qrecord_unwind_protect_excursion,
+                       emit_simple_limple_call_void_ret);
+      register_emitter (Qhelper_save_restriction,
+                       emit_simple_limple_call_void_ret);
+      /* Inliners.  */
+      register_emitter (Qadd1, emit_add1);
+      register_emitter (Qsub1, emit_sub1);
+      register_emitter (Qconsp, emit_consp);
+      register_emitter (Qcar, emit_car);
+      register_emitter (Qcdr, emit_cdr);
+      register_emitter (Qsetcar, emit_setcar);
+      register_emitter (Qsetcdr, emit_setcdr);
+      register_emitter (Qnegate, emit_negate);
+      register_emitter (Qnumberp, emit_numperp);
+      register_emitter (Qintegerp, emit_integerp);
+      register_emitter (Qcomp_maybe_gc_or_quit, emit_maybe_gc_or_quit);
+    }
+
+  comp.ctxt = gcc_jit_context_acquire ();
+
+  comp.void_type = gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_VOID);
+  comp.void_ptr_type =
+    gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_VOID_PTR);
+  comp.bool_type = gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_BOOL);
+  comp.char_type = gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_CHAR);
+  comp.int_type = gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_INT);
+  comp.unsigned_type = gcc_jit_context_get_type (comp.ctxt,
+                                                GCC_JIT_TYPE_UNSIGNED_INT);
+  comp.long_type = gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_LONG);
+  comp.unsigned_long_type =
+    gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_UNSIGNED_LONG);
+  comp.long_long_type =
+    gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_LONG_LONG);
+  comp.unsigned_long_long_type =
+    gcc_jit_context_get_type (comp.ctxt, GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
+  comp.char_ptr_type = gcc_jit_type_get_pointer (comp.char_type);
+  comp.emacs_int_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                     sizeof (EMACS_INT),
+                                                     true);
+  comp.emacs_uint_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                      sizeof (EMACS_UINT),
+                                                      false);
+#if LISP_WORDS_ARE_POINTERS
+  comp.lisp_word_type =
+    gcc_jit_type_get_pointer (
+      gcc_jit_struct_as_type (
+       gcc_jit_context_new_opaque_struct (comp.ctxt,
+                                          NULL,
+                                          "Lisp_X")));
+#else
+  comp.lisp_word_type = comp.emacs_int_type;
+#endif
+  comp.lisp_word_tag_type
+    = gcc_jit_context_get_int_type (comp.ctxt, sizeof (Lisp_Word_tag), false);
+#ifdef LISP_OBJECT_IS_STRUCT
+  comp.lisp_obj_i = gcc_jit_context_new_field (comp.ctxt,
+                                               NULL,
+                                               comp.lisp_word_type,
+                                               "i");
+  comp.lisp_obj_s = gcc_jit_context_new_struct_type (comp.ctxt,
+                                                     NULL,
+                                                     "Lisp_Object",
+                                                     1,
+                                                     &comp.lisp_obj_i);
+  comp.lisp_obj_type = gcc_jit_struct_as_type (comp.lisp_obj_s);
+#else
+  comp.lisp_obj_type = comp.lisp_word_type;
+#endif
+  comp.lisp_obj_ptr_type = gcc_jit_type_get_pointer (comp.lisp_obj_type);
+  comp.zero =
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                        comp.emacs_int_type,
+                                        0);
+  comp.one =
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                        comp.emacs_int_type,
+                                        1);
+  comp.inttypebits =
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                        comp.emacs_uint_type,
+                                        INTTYPEBITS);
+  comp.lisp_int0 =
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                        comp.emacs_int_type,
+                                        Lisp_Int0);
+  comp.ptrdiff_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                   sizeof (void *),
+                                                   true);
+  comp.uintptr_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                   sizeof (void *),
+                                                   false);
+  comp.size_t_type = gcc_jit_context_get_int_type (comp.ctxt,
+                                                  sizeof (size_t),
+                                                  false);
+
+  comp.exported_funcs_h = CALLN (Fmake_hash_table, QCtest, Qequal);
+  /*
+    Always reinitialize this cause old function definitions are garbage
+    collected by libgccjit when the ctxt is released.
+  */
+  comp.imported_funcs_h = CALLN (Fmake_hash_table);
+
+  define_memcpy ();
+
+  /* Define data structures.  */
+
+  define_lisp_cons ();
+  define_jmp_buf ();
+  define_handler_struct ();
+  define_thread_state_struct ();
+  define_cast_functions ();
+
+  return Qt;
+}
+
+DEFUN ("comp--release-ctxt", Fcomp__release_ctxt, Scomp__release_ctxt,
+       0, 0, 0,
+       doc: /* Release the native compiler context.  */)
+  (void)
+{
+  load_gccjit_if_necessary (true);
+
+  if (comp.ctxt)
+    gcc_jit_context_release (comp.ctxt);
+
+  if (logfile)
+    fclose (logfile);
+  comp.ctxt = NULL;
+
+  return Qt;
+}
+
+#pragma GCC diagnostic ignored "-Waddress"
+DEFUN ("comp-native-driver-options-effective-p",
+       Fcomp_native_driver_options_effective_p,
+       Scomp_native_driver_options_effective_p,
+       0, 0, 0,
+       doc: /* Return t if `comp-native-driver-options' is effective.  */)
+  (void)
+{
+#if defined (LIBGCCJIT_HAVE_gcc_jit_context_add_driver_option)  \
+  || defined (WINDOWSNT)
+  if (gcc_jit_context_add_driver_option)
+    return Qt;
+#endif
+  return Qnil;
+}
+#pragma GCC diagnostic pop
+
+static void
+add_driver_options (void)
+{
+  Lisp_Object options = Fsymbol_value (Qcomp_native_driver_options);
+
+#if defined (LIBGCCJIT_HAVE_gcc_jit_context_add_driver_option) \
+  || defined (WINDOWSNT)
+  load_gccjit_if_necessary (true);
+  if (!NILP (Fcomp_native_driver_options_effective_p ()))
+    FOR_EACH_TAIL (options)
+      gcc_jit_context_add_driver_option (comp.ctxt,
+                                        /* FIXME: Need to encode
+                                           this, but how? either
+                                           ENCODE_FILE or
+                                           ENCODE_SYSTEM.  */
+                                        SSDATA (XCAR (options)));
+#endif
+  if (CONSP (options))
+    xsignal1 (Qnative_compiler_error,
+             build_string ("Customizing native compiler options"
+                           " via `comp-native-driver-options' is"
+                           " only available on libgccjit version 9"
+                           " and above."));
+
+  /* Captured `comp-native-driver-options' because file-local.  */
+#if defined (LIBGCCJIT_HAVE_gcc_jit_context_add_driver_option) \
+  || defined (WINDOWSNT)
+  options = comp.driver_options;
+  if (!NILP (Fcomp_native_driver_options_effective_p ()))
+    FOR_EACH_TAIL (options)
+      gcc_jit_context_add_driver_option (comp.ctxt,
+                                        /* FIXME: Need to encode
+                                           this, but how? either
+                                           ENCODE_FILE or
+                                           ENCODE_SYSTEM.  */
+                                        SSDATA (XCAR (options)));
+#endif
+}
+
+DEFUN ("comp--compile-ctxt-to-file", Fcomp__compile_ctxt_to_file,
+       Scomp__compile_ctxt_to_file,
+       1, 1, 0,
+       doc: /* Compile the current context as native code to file FILENAME.  
*/)
+  (Lisp_Object filename)
+{
+  load_gccjit_if_necessary (true);
+
+  CHECK_STRING (filename);
+  Lisp_Object base_name = Fsubstring (filename, Qnil, make_fixnum (-4));
+  Lisp_Object ebase_name = ENCODE_FILE (base_name);
+
+  comp.func_relocs_local = NULL;
+
+#ifdef WINDOWSNT
+  ebase_name = ansi_encode_filename (ebase_name);
+  /* Tell libgccjit the actual file name of the loaded DLL, otherwise
+     it will use 'libgccjit.so', which is not useful.  */
+  Lisp_Object libgccjit_loaded_from = Fget (Qgccjit, QCloaded_from);
+  Lisp_Object libgccjit_fname;
+
+  if (CONSP (libgccjit_loaded_from))
+    {
+      /* Use the absolute file name if available, otherwise the name
+        we looked for in w32_delayed_load.  */
+      libgccjit_fname = XCDR (libgccjit_loaded_from);
+      if (NILP (libgccjit_fname))
+       libgccjit_fname = XCAR (libgccjit_loaded_from);
+      /* Must encode to ANSI, as libgccjit will not be able to handle
+        UTF-8 encoded file names.  */
+      libgccjit_fname = ENCODE_FILE (libgccjit_fname);
+      libgccjit_fname = ansi_encode_filename (libgccjit_fname);
+      gcc_jit_context_set_str_option (comp.ctxt, GCC_JIT_STR_OPTION_PROGNAME,
+                                     SSDATA (libgccjit_fname));
+    }
+  else /* this should never happen */
+    gcc_jit_context_set_str_option (comp.ctxt, GCC_JIT_STR_OPTION_PROGNAME,
+                                   "libgccjit-0.dll");
+#endif
+
+  comp.speed = XFIXNUM (CALL1I (comp-ctxt-speed, Vcomp_ctxt));
+  eassert (comp.speed < INT_MAX);
+  comp.debug = XFIXNUM (CALL1I (comp-ctxt-debug, Vcomp_ctxt));
+  eassert (comp.debug < INT_MAX);
+  comp.driver_options = CALL1I (comp-ctxt-driver-options, Vcomp_ctxt);
+
+  if (comp.debug)
+      gcc_jit_context_set_bool_option (comp.ctxt,
+                                      GCC_JIT_BOOL_OPTION_DEBUGINFO,
+                                      1);
+  if (comp.debug >= 3)
+    {
+      logfile = emacs_fopen ("libgccjit.log", "w");
+      gcc_jit_context_set_logfile (comp.ctxt,
+                                  logfile,
+                                  0, 0);
+      gcc_jit_context_set_bool_option (comp.ctxt,
+                                      GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES,
+                                      1);
+      gcc_jit_context_set_bool_option (comp.ctxt,
+                                      GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
+                                      1);
+    }
+
+  gcc_jit_context_set_int_option (comp.ctxt,
+                                 GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
+                                 comp.speed < 0 ? 0
+                                 : (comp.speed > 3 ? 3 : comp.speed));
+  comp.d_default_idx =
+    CALL1I (comp-data-container-idx, CALL1I (comp-ctxt-d-default, Vcomp_ctxt));
+  comp.d_impure_idx =
+    CALL1I (comp-data-container-idx, CALL1I (comp-ctxt-d-impure, Vcomp_ctxt));
+  comp.d_ephemeral_idx =
+    CALL1I (comp-data-container-idx, CALL1I (comp-ctxt-d-ephemeral, 
Vcomp_ctxt));
+
+  emit_ctxt_code ();
+
+  /* Define inline functions.  */
+  define_CAR_CDR ();
+  define_PSEUDOVECTORP ();
+  define_CHECK_TYPE ();
+  define_CHECK_IMPURE ();
+  define_bool_to_lisp_obj ();
+  define_setcar_setcdr ();
+  define_add1_sub1 ();
+  define_negate ();
+  define_maybe_gc_or_quit ();
+
+  struct Lisp_Hash_Table *func_h =
+    XHASH_TABLE (CALL1I (comp-ctxt-funcs-h, Vcomp_ctxt));
+  for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (func_h); i++)
+    if (!EQ (HASH_VALUE (func_h, i), Qunbound))
+      declare_function (HASH_VALUE (func_h, i));
+  /* Compile all functions. Can't be done before because the
+     relocation structs has to be already defined.  */
+  for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (func_h); i++)
+    if (!EQ (HASH_VALUE (func_h, i), Qunbound))
+      compile_function (HASH_VALUE (func_h, i));
+
+  /* Work around bug#46495 (GCC PR99126). */
+#if defined (WIDE_EMACS_INT)                                           \
+  && (defined (LIBGCCJIT_HAVE_gcc_jit_context_add_command_line_option) \
+      || defined (WINDOWSNT))
+  Lisp_Object version = Fcomp_libgccjit_version ();
+  if (NILP (version)
+      || XFIXNUM (XCAR (version)) < 11)
+    gcc_jit_context_add_command_line_option (comp.ctxt,
+                                            "-fdisable-tree-isolate-paths");
+#endif
+
+  add_driver_options ();
+
+  if (comp.debug > 1)
+      gcc_jit_context_dump_to_file (comp.ctxt,
+                                   format_string ("%s.c", SSDATA (ebase_name)),
+                                   1);
+  if (!NILP (Fsymbol_value (Qcomp_libgccjit_reproducer)))
+    gcc_jit_context_dump_reproducer_to_file (
+      comp.ctxt,
+      format_string ("%s_libgccjit_repro.c", SSDATA (ebase_name)));
+
+  Lisp_Object tmp_file =
+    Fmake_temp_file_internal (base_name, Qnil, build_string (".eln.tmp"), 
Qnil);
+  Lisp_Object encoded_tmp_file = ENCODE_FILE (tmp_file);
+#ifdef WINDOWSNT
+  encoded_tmp_file = ansi_encode_filename (encoded_tmp_file);
+#endif
+  gcc_jit_context_compile_to_file (comp.ctxt,
+                                  GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY,
+                                  SSDATA (encoded_tmp_file));
+
+  const char *err =  gcc_jit_context_get_first_error (comp.ctxt);
+  if (err)
+    xsignal3 (Qnative_ice,
+             build_string ("failed to compile"),
+             filename,
+             build_string (err));
+
+  CALL1I (comp-clean-up-stale-eln, filename);
+  CALL2I (comp-delete-or-replace-file, filename, tmp_file);
+
+  return filename;
+}
+
+#pragma GCC diagnostic ignored "-Waddress"
+DEFUN ("comp-libgccjit-version", Fcomp_libgccjit_version,
+       Scomp_libgccjit_version, 0, 0, 0,
+       doc: /* Return libgccjit version in use.
+
+The return value has the form (MAJOR MINOR PATCHLEVEL) or nil if
+unknown (before GCC version 10).  */)
+  (void)
+{
+#if defined (LIBGCCJIT_HAVE_gcc_jit_version) || defined (WINDOWSNT)
+  load_gccjit_if_necessary (true);
+
+  return gcc_jit_version_major
+    ? list3 (make_fixnum (gcc_jit_version_major ()),
+            make_fixnum (gcc_jit_version_minor ()),
+            make_fixnum (gcc_jit_version_patchlevel ()))
+    : Qnil;
+#else
+  return Qnil;
+#endif
+}
+#pragma GCC diagnostic pop
+
+
+/******************************************************************************/
+/* Helper functions called from the run-time.                                */
+/* These can't be statics till shared mechanism is used to solve relocations. 
*/
+/* Note: this are all potentially definable directly to gcc and are here just 
*/
+/* for laziness. Change this if a performance impact is measured.             
*/
+/******************************************************************************/
+
+void
+helper_unwind_protect (Lisp_Object handler)
+{
+  /* Support for a function here is new in 24.4.  */
+  record_unwind_protect (FUNCTIONP (handler) ? bcall0 : prog_ignore,
+                        handler);
+}
+
+Lisp_Object
+helper_temp_output_buffer_setup (Lisp_Object x)
+{
+  CHECK_STRING (x);
+  temp_output_buffer_setup (SSDATA (x));
+  return Vstandard_output;
+}
+
+Lisp_Object
+helper_unbind_n (Lisp_Object n)
+{
+  return unbind_to (SPECPDL_INDEX () - XFIXNUM (n), Qnil);
+}
+
+void
+helper_save_restriction (void)
+{
+  record_unwind_protect (save_restriction_restore,
+                        save_restriction_save ());
+}
+
+bool
+helper_PSEUDOVECTOR_TYPEP_XUNTAG (Lisp_Object a, enum pvec_type code)
+{
+  return PSEUDOVECTOR_TYPEP (XUNTAG (a, Lisp_Vectorlike,
+                                    union vectorlike_header),
+                            code);
+}
+
+
+/* `comp-eln-load-path' clean-up support code.  */
+
+static Lisp_Object all_loaded_comp_units_h;
+
+#ifdef WINDOWSNT
+static Lisp_Object
+return_nil (Lisp_Object arg)
+{
+  return Qnil;
+}
+#endif
+
+/* Windows does not let us delete a .eln file that is currently loaded
+   by a process.  The strategy is to rename .eln files into .old.eln
+   instead of removing them when this is not possible and clean-up
+   `comp-eln-load-path' when exiting.
+
+   Any error is ignored because it may be due to the file being loaded
+   in another Emacs instance.  */
+void
+eln_load_path_final_clean_up (void)
+{
+#ifdef WINDOWSNT
+  Lisp_Object dir_tail = Vcomp_eln_load_path;
+  FOR_EACH_TAIL (dir_tail)
+    {
+      Lisp_Object files_in_dir =
+       internal_condition_case_5 (Fdirectory_files,
+                                  Fexpand_file_name (Vcomp_native_version_dir,
+                                                     XCAR (dir_tail)),
+                                  Qt, build_string ("\\.eln\\.old\\'"), Qnil,
+                                  Qnil, Qt, return_nil);
+      FOR_EACH_TAIL (files_in_dir)
+       internal_delete_file (XCAR (files_in_dir));
+    }
+#endif
+}
+
+/* This function puts the compilation unit in the
+  `all_loaded_comp_units_h` hashmap.  */
+static void
+register_native_comp_unit (Lisp_Object comp_u)
+{
+  Fputhash (XNATIVE_COMP_UNIT (comp_u)->file, comp_u, all_loaded_comp_units_h);
+}
+
+
+/***********************************/
+/* Deferred compilation mechanism. */
+/***********************************/
+
+/* List of sources we'll compile and load after having conventionally
+   loaded the compiler and its dependencies.  */
+static Lisp_Object delayed_sources;
+
+/* Queue an asynchronous compilation for the source file defining
+   FUNCTION_NAME and perform a late load.
+
+   NOTE: ideally would be nice to move its call simply into Fload but
+   we need DEFINITION to guard against function redefinition while
+   async compilation happen.  */
+
+void
+maybe_defer_native_compilation (Lisp_Object function_name,
+                               Lisp_Object definition)
+{
+#if 0
+#include <sys/types.h>
+#include <unistd.h>
+  if (!NILP (function_name) &&
+      STRINGP (Vload_true_file_name))
+    {
+      static FILE *f;
+      if (!f)
+       {
+         char str[128];
+         sprintf (str, "log_%d", getpid ());
+         f = fopen (str, "w");
+       }
+      if (!f)
+       exit (1);
+      fprintf (f, "function %s file %s\n",
+              SSDATA (Fsymbol_name (function_name)),
+              SSDATA (Vload_true_file_name));
+      fflush (f);
+    }
+#endif
+  if (!load_gccjit_if_necessary (false))
+    return;
+
+  if (!comp_deferred_compilation
+      || noninteractive
+      || !NILP (Vpurify_flag)
+      || !COMPILEDP (definition)
+      || !STRINGP (Vload_true_file_name)
+      || !suffix_p (Vload_true_file_name, ".elc")
+      || !NILP (Fgethash (Vload_true_file_name, V_comp_no_native_file_h, 
Qnil)))
+    return;
+
+  Lisp_Object src =
+    concat2 (CALL1I (file-name-sans-extension, Vload_true_file_name),
+            build_pure_c_string (".el"));
+  if (NILP (Ffile_exists_p (src)))
+    {
+      src = concat2 (src, build_pure_c_string (".gz"));
+      if (NILP (Ffile_exists_p (src)))
+       return;
+    }
+
+  /* This is to have deferred compilaiton able to compile comp
+     dependencies breaking circularity.  */
+  if (!NILP (Ffeaturep (Qcomp, Qnil)))
+    {
+      /* Comp already loaded.  */
+      if (!NILP (delayed_sources))
+       {
+         CALLN (Ffuncall, intern_c_string ("native--compile-async"),
+                delayed_sources, Qnil, Qlate);
+         delayed_sources = Qnil;
+       }
+      Fputhash (function_name, definition, Vcomp_deferred_pending_h);
+      CALLN (Ffuncall, intern_c_string ("native--compile-async"),
+            src, Qnil, Qlate);
+    }
+  else
+    {
+      delayed_sources = Fcons (src, delayed_sources);
+      /* Require comp only once.  */
+      static bool comp_required = false;
+      if (!comp_required)
+       {
+         comp_required = true;
+         Frequire (Qcomp, Qnil, Qnil);
+       }
+    }
+}
+
+
+/**************************************/
+/* Functions used to load eln files.  */
+/**************************************/
+
+/* Fixup the system eln-cache directory, which is the last entry in
+   `comp-eln-load-path'.  Argument is a .eln file in that directory.  */
+void
+fixup_eln_load_path (Lisp_Object eln_filename)
+{
+  Lisp_Object last_cell = Qnil;
+  Lisp_Object tem = Vcomp_eln_load_path;
+  FOR_EACH_TAIL (tem)
+    if (CONSP (tem))
+      last_cell = tem;
+
+  const char preloaded[] = "/preloaded/";
+  Lisp_Object eln_cache_sys = Ffile_name_directory (eln_filename);
+  const char *p_preloaded =
+    SSDATA (eln_cache_sys) + SBYTES (eln_cache_sys) - sizeof (preloaded) + 1;
+  bool preloaded_p = strcmp (p_preloaded, preloaded) == 0;
+
+  /* One or two directories up...  */
+  for (int i = 0; i < (preloaded_p ? 2 : 1); i++)
+    eln_cache_sys =
+      Ffile_name_directory (Fsubstring_no_properties (eln_cache_sys, Qnil,
+                                                     make_fixnum (-1)));
+  Fsetcar (last_cell, eln_cache_sys);
+}
+
+typedef char *(*comp_lit_str_func) (void);
+
+/* Deserialize read and return static object.  */
+static Lisp_Object
+load_static_obj (struct Lisp_Native_Comp_Unit *comp_u, const char *name)
+{
+  static_obj_t *blob =
+    dynlib_sym (comp_u->handle, format_string ("%s_blob", name));
+  if (blob)
+    /* New blob format.  */
+    return Fread (make_string (blob->data, blob->len));
+
+  static_obj_t *(*f)(void) = dynlib_sym (comp_u->handle, name);
+  if (!f)
+    xsignal1 (Qnative_lisp_file_inconsistent, comp_u->file);
+
+  blob = f ();
+  return Fread (make_string (blob->data, blob->len));
+
+}
+
+/* Return false when something is wrong or true otherwise.  */
+
+static bool
+check_comp_unit_relocs (struct Lisp_Native_Comp_Unit *comp_u)
+{
+  dynlib_handle_ptr handle = comp_u->handle;
+  Lisp_Object *data_relocs = dynlib_sym (handle, DATA_RELOC_SYM);
+  Lisp_Object *data_imp_relocs = dynlib_sym (handle, DATA_RELOC_IMPURE_SYM);
+
+  EMACS_INT d_vec_len = XFIXNUM (Flength (comp_u->data_vec));
+  for (ptrdiff_t i = 0; i < d_vec_len; i++)
+    if (!EQ (data_relocs[i],  AREF (comp_u->data_vec, i)))
+      return false;
+
+  d_vec_len = XFIXNUM (Flength (comp_u->data_impure_vec));
+  for (ptrdiff_t i = 0; i < d_vec_len; i++)
+    {
+      Lisp_Object x = data_imp_relocs[i];
+      if (EQ (x, Qlambda_fixup))
+       return false;
+      else if (SUBR_NATIVE_COMPILEDP (x))
+       {
+         if (NILP (Fgethash (x, comp_u->lambda_gc_guard_h, Qnil)))
+           return false;
+       }
+      else if (!EQ (data_imp_relocs[i], AREF (comp_u->data_impure_vec, i)))
+       return false;
+    }
+  return true;
+}
+
+static void
+unset_cu_load_ongoing (Lisp_Object comp_u)
+{
+  XNATIVE_COMP_UNIT (comp_u)->load_ongoing = false;
+}
+
+Lisp_Object
+load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u, bool loading_dump,
+               bool late_load)
+{
+  Lisp_Object res = Qnil;
+  dynlib_handle_ptr handle = comp_u->handle;
+  Lisp_Object comp_u_lisp_obj;
+  XSETNATIVE_COMP_UNIT (comp_u_lisp_obj, comp_u);
+
+  Lisp_Object *saved_cu = dynlib_sym (handle, COMP_UNIT_SYM);
+  if (!saved_cu)
+    xsignal1 (Qnative_lisp_file_inconsistent, comp_u->file);
+  comp_u->loaded_once = !NILP (*saved_cu);
+  Lisp_Object *data_eph_relocs =
+    dynlib_sym (handle, DATA_RELOC_EPHEMERAL_SYM);
+
+  /* While resurrecting from an image dump loading more than once the
+     same compilation unit does not make any sense.  */
+  eassert (!(loading_dump && comp_u->loaded_once));
+
+  if (comp_u->loaded_once)
+    /* 'dlopen' returns the same handle when trying to load two times
+       the same shared.  In this case touching 'd_reloc' etc leads to
+       fails in case a frame with a reference to it in a live reg is
+       active (comp-speed > 0).
+
+       We must *never* mess with static pointers in an already loaded
+       eln.  */
+    {
+      comp_u_lisp_obj = *saved_cu;
+      comp_u = XNATIVE_COMP_UNIT (comp_u_lisp_obj);
+      comp_u->loaded_once = true;
+    }
+  else
+    *saved_cu = comp_u_lisp_obj;
+
+  /* Once we are sure to have the right compilation unit we want to
+     identify is we have at least another load active on it.  */
+  bool recursive_load = comp_u->load_ongoing;
+  comp_u->load_ongoing = true;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  if (!recursive_load)
+    record_unwind_protect (unset_cu_load_ongoing, comp_u_lisp_obj);
+
+  freloc_check_fill ();
+
+  Lisp_Object (*top_level_run)(Lisp_Object)
+    = dynlib_sym (handle,
+                 late_load ? "late_top_level_run" : "top_level_run");
+
+  /* Always set data_imp_relocs pointer in the compilation unit (in can be
+     used in 'dump_do_dump_relocation').  */
+  comp_u->data_imp_relocs = dynlib_sym (handle, DATA_RELOC_IMPURE_SYM);
+
+  if (!comp_u->loaded_once)
+    {
+      struct thread_state ***current_thread_reloc =
+       dynlib_sym (handle, CURRENT_THREAD_RELOC_SYM);
+      void **pure_reloc = dynlib_sym (handle, PURE_RELOC_SYM);
+      Lisp_Object *data_relocs = dynlib_sym (handle, DATA_RELOC_SYM);
+      Lisp_Object *data_imp_relocs = comp_u->data_imp_relocs;
+      void **freloc_link_table = dynlib_sym (handle, FUNC_LINK_TABLE_SYM);
+
+      if (!(current_thread_reloc
+           && pure_reloc
+           && data_relocs
+           && data_imp_relocs
+           && data_eph_relocs
+           && freloc_link_table
+           && top_level_run)
+         || NILP (Fstring_equal (load_static_obj (comp_u, LINK_TABLE_HASH_SYM),
+                                 Vcomp_abi_hash)))
+       xsignal1 (Qnative_lisp_file_inconsistent, comp_u->file);
+
+      *current_thread_reloc = &current_thread;
+      *pure_reloc = pure;
+
+      /* Imported functions.  */
+      *freloc_link_table = freloc.link_table;
+
+      /* Imported data.  */
+      if (!loading_dump)
+       {
+         comp_u->optimize_qualities =
+           load_static_obj (comp_u, TEXT_OPTIM_QLY_SYM);
+         comp_u->data_vec = load_static_obj (comp_u, TEXT_DATA_RELOC_SYM);
+         comp_u->data_impure_vec =
+           load_static_obj (comp_u, TEXT_DATA_RELOC_IMPURE_SYM);
+
+         if (!NILP (Vpurify_flag))
+           /* Non impure can be copied into pure space.  */
+           comp_u->data_vec = Fpurecopy (comp_u->data_vec);
+       }
+
+      EMACS_INT d_vec_len = XFIXNUM (Flength (comp_u->data_vec));
+      for (EMACS_INT i = 0; i < d_vec_len; i++)
+       data_relocs[i] = AREF (comp_u->data_vec, i);
+
+      d_vec_len = XFIXNUM (Flength (comp_u->data_impure_vec));
+      for (EMACS_INT i = 0; i < d_vec_len; i++)
+       data_imp_relocs[i] = AREF (comp_u->data_impure_vec, i);
+    }
+
+  if (!loading_dump)
+    {
+      /* Note: data_ephemeral_vec is not GC protected except than by
+        this function frame.  After this functions will be
+        deactivated GC will be free to collect it, but it MUST
+        survive till 'top_level_run' has finished his job.  We store
+        into the ephemeral allocation class only objects that we know
+        are necessary exclusively during the first load.  Once these
+        are collected we don't have to maintain them in the heap
+        forever.  */
+      Lisp_Object volatile data_ephemeral_vec;
+      /* In case another load of the same CU is active on the stack
+        all ephemeral data is hold by that frame.  Re-writing
+        'data_ephemeral_vec' would be not only a waste of cycles but
+        more importanly would lead to crashed if the contained data
+        is not cons hashed.  */
+      if (!recursive_load)
+       {
+         data_ephemeral_vec =
+           load_static_obj (comp_u, TEXT_DATA_RELOC_EPHEMERAL_SYM);
+
+         EMACS_INT d_vec_len = XFIXNUM (Flength (data_ephemeral_vec));
+         for (EMACS_INT i = 0; i < d_vec_len; i++)
+           data_eph_relocs[i] = AREF (data_ephemeral_vec, i);
+       }
+      /* Executing this will perform all the expected environment
+        modifications.  */
+      res = top_level_run (comp_u_lisp_obj);
+      /* Make sure data_ephemeral_vec still exists after top_level_run has run.
+        Guard against sibling call optimization (or any other).  */
+      data_ephemeral_vec = data_ephemeral_vec;
+      eassert (check_comp_unit_relocs (comp_u));
+    }
+
+  if (!recursive_load)
+    /* Clean-up the load ongoing flag in case.  */
+    unbind_to (count, Qnil);
+
+  register_native_comp_unit (comp_u_lisp_obj);
+
+  return res;
+}
+
+void
+unload_comp_unit (struct Lisp_Native_Comp_Unit *cu)
+{
+  if (cu->handle == NULL)
+    return;
+
+  Lisp_Object *saved_cu = dynlib_sym (cu->handle, COMP_UNIT_SYM);
+  Lisp_Object this_cu;
+  XSETNATIVE_COMP_UNIT (this_cu, cu);
+  if (EQ (this_cu, *saved_cu))
+    *saved_cu = Qnil;
+  dynlib_close (cu->handle);
+}
+
+Lisp_Object
+native_function_doc (Lisp_Object function)
+{
+  struct Lisp_Native_Comp_Unit *cu =
+    XNATIVE_COMP_UNIT (Fsubr_native_comp_unit (function));
+
+  if (NILP (cu->data_fdoc_v))
+    cu->data_fdoc_v = load_static_obj (cu, TEXT_FDOC_SYM);
+  if (!VECTORP (cu->data_fdoc_v))
+    xsignal2 (Qnative_lisp_file_inconsistent, cu->file,
+             build_string ("missing documentation vector"));
+  return AREF (cu->data_fdoc_v, XSUBR (function)->doc);
+}
+
+static Lisp_Object
+make_subr (Lisp_Object symbol_name, Lisp_Object minarg, Lisp_Object maxarg,
+          Lisp_Object c_name, Lisp_Object type, Lisp_Object doc_idx,
+          Lisp_Object intspec, Lisp_Object comp_u)
+{
+  struct Lisp_Native_Comp_Unit *cu = XNATIVE_COMP_UNIT (comp_u);
+  dynlib_handle_ptr handle = cu->handle;
+  if (!handle)
+    xsignal0 (Qwrong_register_subr_call);
+
+  void *func = dynlib_sym (handle, SSDATA (c_name));
+  eassert (func);
+  union Aligned_Lisp_Subr *x =
+    (union Aligned_Lisp_Subr *) allocate_pseudovector (
+                                 VECSIZE (union Aligned_Lisp_Subr),
+                                 0, VECSIZE (union Aligned_Lisp_Subr),
+                                 PVEC_SUBR);
+  if (CONSP (minarg))
+    {
+      /* Dynamic code.  */
+      x->s.lambda_list[0] = maxarg;
+      maxarg = XCDR (minarg);
+      minarg = XCAR (minarg);
+    }
+  else
+    x->s.lambda_list[0] = Qnil;
+  x->s.function.a0 = func;
+  x->s.min_args = XFIXNUM (minarg);
+  x->s.max_args = FIXNUMP (maxarg) ? XFIXNUM (maxarg) : MANY;
+  x->s.symbol_name = xstrdup (SSDATA (symbol_name));
+  x->s.native_intspec = intspec;
+  x->s.doc = XFIXNUM (doc_idx);
+  x->s.native_comp_u[0] = comp_u;
+  x->s.native_c_name[0] = xstrdup (SSDATA (c_name));
+  x->s.type[0] = type;
+  Lisp_Object tem;
+  XSETSUBR (tem, &x->s);
+
+  return tem;
+}
+
+DEFUN ("comp--register-lambda", Fcomp__register_lambda, Scomp__register_lambda,
+       7, 7, 0,
+       doc: /* Register anonymous lambda.
+This gets called by top_level_run during the load phase.  */)
+  (Lisp_Object reloc_idx, Lisp_Object c_name, Lisp_Object minarg,
+   Lisp_Object maxarg, Lisp_Object type, Lisp_Object rest,
+   Lisp_Object comp_u)
+{
+  Lisp_Object doc_idx = FIRST (rest);
+  Lisp_Object intspec = SECOND (rest);
+  struct Lisp_Native_Comp_Unit *cu = XNATIVE_COMP_UNIT (comp_u);
+  if (cu->loaded_once)
+    return Qnil;
+
+  Lisp_Object tem =
+    make_subr (c_name, minarg, maxarg, c_name, type, doc_idx, intspec, comp_u);
+
+  /* We must protect it against GC because the function is not
+     reachable through symbols.  */
+  Fputhash (tem, Qt, cu->lambda_gc_guard_h);
+  /* This is for fixing up the value in d_reloc while resurrecting
+     from dump.  See 'dump_do_dump_relocation'.  */
+  eassert (NILP (Fgethash (c_name, cu->lambda_c_name_idx_h, Qnil)));
+  Fputhash (c_name, reloc_idx, cu->lambda_c_name_idx_h);
+  /* Do the real relocation fixup.  */
+  cu->data_imp_relocs[XFIXNUM (reloc_idx)] = tem;
+
+  return tem;
+}
+
+DEFUN ("comp--register-subr", Fcomp__register_subr, Scomp__register_subr,
+       7, 7, 0,
+       doc: /* Register exported subr.
+This gets called by top_level_run during the load phase.  */)
+  (Lisp_Object name, Lisp_Object c_name, Lisp_Object minarg,
+   Lisp_Object maxarg, Lisp_Object type, Lisp_Object rest,
+   Lisp_Object comp_u)
+{
+  Lisp_Object doc_idx = FIRST (rest);
+  Lisp_Object intspec = SECOND (rest);
+  Lisp_Object tem =
+    make_subr (SYMBOL_NAME (name), minarg, maxarg, c_name, type, doc_idx,
+              intspec, comp_u);
+
+  if (AUTOLOADP (XSYMBOL (name)->u.s.function))
+    /* Remember that the function was already an autoload.  */
+    LOADHIST_ATTACH (Fcons (Qt, name));
+  LOADHIST_ATTACH (Fcons (Qdefun, name));
+
+  { /* Handle automatic advice activation (bug#42038).
+       See `defalias'.  */
+    Lisp_Object hook = Fget (name, Qdefalias_fset_function);
+    if (!NILP (hook))
+      call2 (hook, name, tem);
+    else
+      Ffset (name, tem);
+  }
+
+  return tem;
+}
+
+DEFUN ("comp--late-register-subr", Fcomp__late_register_subr,
+       Scomp__late_register_subr, 7, 7, 0,
+       doc: /* Register exported subr.
+This gets called by late_top_level_run during the load phase.  */)
+  (Lisp_Object name, Lisp_Object c_name, Lisp_Object minarg,
+   Lisp_Object maxarg, Lisp_Object type, Lisp_Object rest,
+   Lisp_Object comp_u)
+{
+  if (!NILP (Fequal (Fsymbol_function (name),
+                    Fgethash (name, Vcomp_deferred_pending_h, Qnil))))
+    Fcomp__register_subr (name, c_name, minarg, maxarg, type, rest, comp_u);
+  Fremhash (name, Vcomp_deferred_pending_h);
+  return Qnil;
+}
+
+static bool
+file_in_eln_sys_dir (Lisp_Object filename)
+{
+  Lisp_Object eln_sys_dir = Qnil;
+  Lisp_Object tmp = Vcomp_eln_load_path;
+  FOR_EACH_TAIL (tmp)
+    eln_sys_dir = XCAR (tmp);
+  return !NILP (Fstring_match (Fregexp_quote (Fexpand_file_name (eln_sys_dir,
+                                                                Qnil)),
+                              Fexpand_file_name (filename, Qnil), Qnil));
+}
+
+/* Load related routines.  */
+DEFUN ("native-elisp-load", Fnative_elisp_load, Snative_elisp_load, 1, 2, 0,
+       doc: /* Load native elisp code FILENAME.
+LATE_LOAD has to be non-nil when loading for deferred compilation.  */)
+  (Lisp_Object filename, Lisp_Object late_load)
+{
+  CHECK_STRING (filename);
+  if (NILP (Ffile_exists_p (filename)))
+    xsignal2 (Qnative_lisp_load_failed, build_string ("file does not exists"),
+             filename);
+  struct Lisp_Native_Comp_Unit *comp_u = allocate_native_comp_unit ();
+  Lisp_Object encoded_filename = ENCODE_FILE (filename);
+
+  if (!NILP (Fgethash (filename, all_loaded_comp_units_h, Qnil))
+      && !file_in_eln_sys_dir (filename)
+      && !NILP (Ffile_writable_p (filename)))
+    {
+      /* If in this session there was ever a file loaded with this
+        name, rename it before loading, to make sure we always get a
+        new handle!  */
+      Lisp_Object tmp_filename =
+       Fmake_temp_file_internal (filename, Qnil, build_string (".eln.tmp"),
+                                 Qnil);
+      if (NILP (Ffile_writable_p (tmp_filename)))
+       comp_u->handle = dynlib_open (SSDATA (encoded_filename));
+      else
+       {
+         Frename_file (filename, tmp_filename, Qt);
+         comp_u->handle = dynlib_open (SSDATA (ENCODE_FILE (tmp_filename)));
+         Frename_file (tmp_filename, filename, Qnil);
+       }
+    }
+  else
+    comp_u->handle = dynlib_open (SSDATA (encoded_filename));
+
+  if (!comp_u->handle)
+    xsignal2 (Qnative_lisp_load_failed, filename,
+             build_string (dynlib_error ()));
+  comp_u->file = filename;
+  comp_u->data_vec = Qnil;
+  comp_u->lambda_gc_guard_h = CALLN (Fmake_hash_table, QCtest, Qeq);
+  comp_u->lambda_c_name_idx_h = CALLN (Fmake_hash_table, QCtest, Qequal);
+  return load_comp_unit (comp_u, false, !NILP (late_load));
+}
+
+#endif /* HAVE_NATIVE_COMP */
+
+DEFUN ("native-comp-available-p", Fnative_comp_available_p,
+       Snative_comp_available_p, 0, 0, 0,
+       doc: /* Return non-nil if native compilation support is built-in.  */)
+  (void)
+{
+#ifdef HAVE_NATIVE_COMP
+  return load_gccjit_if_necessary (false) ? Qt : Qnil;
+#else
+  return Qnil;
+#endif
+}
+
+
+void
+syms_of_comp (void)
+{
+#ifdef HAVE_NATIVE_COMP
+  /* Compiler control customizes.  */
+  DEFVAR_BOOL ("comp-deferred-compilation", comp_deferred_compilation,
+              doc: /* If non-nil compile loaded .elc files asynchronously.
+
+After compilation, each function definition is updated to the native
+compiled one.  */);
+  comp_deferred_compilation = true;
+
+  DEFSYM (Qcomp_speed, "comp-speed");
+  DEFSYM (Qcomp_debug, "comp-debug");
+  DEFSYM (Qcomp_native_driver_options, "comp-native-driver-options");
+  DEFSYM (Qcomp_libgccjit_reproducer, "comp-libgccjit-reproducer");
+
+  /* Limple instruction set.  */
+  DEFSYM (Qcomment, "comment");
+  DEFSYM (Qjump, "jump");
+  DEFSYM (Qcall, "call");
+  DEFSYM (Qcallref, "callref");
+  DEFSYM (Qdirect_call, "direct-call");
+  DEFSYM (Qdirect_callref, "direct-callref");
+  DEFSYM (Qassume, "assume");
+  DEFSYM (Qsetimm, "setimm");
+  DEFSYM (Qreturn, "return");
+  DEFSYM (Qunreachable, "unreachable");
+  DEFSYM (Qcomp_mvar, "comp-mvar");
+  DEFSYM (Qcond_jump, "cond-jump");
+  DEFSYM (Qphi, "phi");
+  /* Ops in use for prologue emission.  */
+  DEFSYM (Qset_par_to_local, "set-par-to-local");
+  DEFSYM (Qset_args_to_local, "set-args-to-local");
+  DEFSYM (Qset_rest_args_to_local, "set-rest-args-to-local");
+  DEFSYM (Qinc_args, "inc-args");
+  DEFSYM (Qcond_jump_narg_leq, "cond-jump-narg-leq");
+  /* Others.  */
+  DEFSYM (Qpush_handler, "push-handler");
+  DEFSYM (Qpop_handler, "pop-handler");
+  DEFSYM (Qfetch_handler, "fetch-handler");
+  DEFSYM (Qcondition_case, "condition-case");
+  /* call operands.  */
+  DEFSYM (Qcatcher, "catcher");
+  DEFSYM (Qentry, "entry");
+  DEFSYM (Qset_internal, "set_internal");
+  DEFSYM (Qrecord_unwind_current_buffer, "record_unwind_current_buffer");
+  DEFSYM (Qrecord_unwind_protect_excursion, "record_unwind_protect_excursion");
+  DEFSYM (Qhelper_unbind_n, "helper_unbind_n");
+  DEFSYM (Qhelper_unwind_protect, "helper_unwind_protect");
+  DEFSYM (Qhelper_save_restriction, "helper_save_restriction");
+  /* Inliners.  */
+  DEFSYM (Qadd1, "1+");
+  DEFSYM (Qsub1, "1-");
+  DEFSYM (Qconsp, "consp");
+  DEFSYM (Qcar, "car");
+  DEFSYM (Qcdr, "cdr");
+  DEFSYM (Qsetcar, "setcar");
+  DEFSYM (Qsetcdr, "setcdr");
+  DEFSYM (Qnegate, "negate");
+  DEFSYM (Qnumberp, "numberp");
+  DEFSYM (Qintegerp, "integerp");
+  DEFSYM (Qcomp_maybe_gc_or_quit, "comp-maybe-gc-or-quit");
+
+  /* Allocation classes. */
+  DEFSYM (Qd_default, "d-default");
+  DEFSYM (Qd_impure, "d-impure");
+  DEFSYM (Qd_ephemeral, "d-ephemeral");
+
+  /* Others.  */
+  DEFSYM (Qcomp, "comp");
+  DEFSYM (Qfixnum, "fixnum");
+  DEFSYM (Qscratch, "scratch");
+  DEFSYM (Qlate, "late");
+  DEFSYM (Qlambda_fixup, "lambda-fixup");
+  DEFSYM (Qgccjit, "gccjit");
+  DEFSYM (Qcomp_subr_trampoline_install, "comp-subr-trampoline-install");
+  DEFSYM (Qcomp_warning_on_missing_source, "comp-warning-on-missing-source");
+
+  /* To be signaled by the compiler.  */
+  DEFSYM (Qnative_compiler_error, "native-compiler-error");
+  Fput (Qnative_compiler_error, Qerror_conditions,
+       pure_list (Qnative_compiler_error, Qerror));
+  Fput (Qnative_compiler_error, Qerror_message,
+        build_pure_c_string ("Native compiler error"));
+
+  DEFSYM (Qnative_ice, "native-ice");
+  Fput (Qnative_ice, Qerror_conditions,
+       pure_list (Qnative_ice, Qnative_compiler_error, Qerror));
+  Fput (Qnative_ice, Qerror_message,
+        build_pure_c_string ("Internal native compiler error"));
+
+  /* By the load machinery.  */
+  DEFSYM (Qnative_lisp_load_failed, "native-lisp-load-failed");
+  Fput (Qnative_lisp_load_failed, Qerror_conditions,
+       pure_list (Qnative_lisp_load_failed, Qerror));
+  Fput (Qnative_lisp_load_failed, Qerror_message,
+        build_pure_c_string ("Native elisp load failed"));
+
+  DEFSYM (Qnative_lisp_wrong_reloc, "native-lisp-wrong-reloc");
+  Fput (Qnative_lisp_wrong_reloc, Qerror_conditions,
+       pure_list (Qnative_lisp_wrong_reloc, Qnative_lisp_load_failed, Qerror));
+  Fput (Qnative_lisp_wrong_reloc, Qerror_message,
+        build_pure_c_string ("Primitive redefined or wrong relocation"));
+
+  DEFSYM (Qwrong_register_subr_call, "wrong-register-subr-call");
+  Fput (Qwrong_register_subr_call, Qerror_conditions,
+       pure_list (Qwrong_register_subr_call, Qnative_lisp_load_failed, 
Qerror));
+  Fput (Qwrong_register_subr_call, Qerror_message,
+        build_pure_c_string ("comp--register-subr can only be called during "
+                           "native lisp load phase."));
+
+  DEFSYM (Qnative_lisp_file_inconsistent, "native-lisp-file-inconsistent");
+  Fput (Qnative_lisp_file_inconsistent, Qerror_conditions,
+       pure_list (Qnative_lisp_file_inconsistent, Qnative_lisp_load_failed, 
Qerror));
+  Fput (Qnative_lisp_file_inconsistent, Qerror_message,
+        build_pure_c_string ("eln file inconsistent with current runtime "
+                            "configuration, please recompile"));
+
+  defsubr (&Scomp__subr_signature);
+  defsubr (&Scomp_el_to_eln_rel_filename);
+  defsubr (&Scomp_el_to_eln_filename);
+  defsubr (&Scomp_native_driver_options_effective_p);
+  defsubr (&Scomp__install_trampoline);
+  defsubr (&Scomp__init_ctxt);
+  defsubr (&Scomp__release_ctxt);
+  defsubr (&Scomp__compile_ctxt_to_file);
+  defsubr (&Scomp_libgccjit_version);
+  defsubr (&Scomp__register_lambda);
+  defsubr (&Scomp__register_subr);
+  defsubr (&Scomp__late_register_subr);
+  defsubr (&Snative_elisp_load);
+
+  staticpro (&comp.exported_funcs_h);
+  comp.exported_funcs_h = Qnil;
+  staticpro (&comp.imported_funcs_h);
+  comp.imported_funcs_h = Qnil;
+  staticpro (&comp.func_blocks_h);
+  staticpro (&comp.emitter_dispatcher);
+  comp.emitter_dispatcher = Qnil;
+  staticpro (&delayed_sources);
+  delayed_sources = Qnil;
+  staticpro (&loadsearch_re_list);
+  loadsearch_re_list = Qnil;
+
+  staticpro (&all_loaded_comp_units_h);
+  all_loaded_comp_units_h =
+    CALLN (Fmake_hash_table, QCweakness, Qkey_and_value, QCtest, Qequal);
+
+  DEFVAR_LISP ("comp-ctxt", Vcomp_ctxt,
+              doc: /* The compiler context.  */);
+  Vcomp_ctxt = Qnil;
+
+  /* FIXME should be initialized but not here...  Plus this don't have
+     to be necessarily exposed to lisp but can easy debug for now.  */
+  DEFVAR_LISP ("comp-subr-list", Vcomp_subr_list,
+              doc: /* List of all defined subrs.  */);
+  DEFVAR_LISP ("comp-abi-hash", Vcomp_abi_hash,
+              doc: /* String signing the .eln files ABI.  */);
+  Vcomp_abi_hash = Qnil;
+  DEFVAR_LISP ("comp-native-version-dir", Vcomp_native_version_dir,
+              doc: /* Directory in use to disambiguate eln compatibility.  */);
+  Vcomp_native_version_dir = Qnil;
+
+  DEFVAR_LISP ("comp-deferred-pending-h", Vcomp_deferred_pending_h,
+              doc: /* Hash table symbol-name -> function-value.
+For internal use.  */);
+  Vcomp_deferred_pending_h = CALLN (Fmake_hash_table, QCtest, Qeq);
+
+  DEFVAR_LISP ("comp-eln-to-el-h", Vcomp_eln_to_el_h,
+              doc: /* Hash table eln-filename -> el-filename.  */);
+  Vcomp_eln_to_el_h = CALLN (Fmake_hash_table, QCtest, Qequal);
+
+  DEFVAR_LISP ("comp-eln-load-path", Vcomp_eln_load_path,
+              doc: /* List of eln cache directories.
+
+If a directory is non absolute is assumed to be relative to
+`invocation-directory'.
+`comp-native-version-dir' value is used as a sub-folder name inside
+each eln cache directory.
+The last directory of this list is assumed to be the system one.  */);
+
+  /* Temporary value in use for bootstrap.  We can't do better as
+     `invocation-directory' is still unset, will be fixed up during
+     dump reload.  */
+  Vcomp_eln_load_path = Fcons (build_string ("../native-lisp/"), Qnil);
+
+  DEFVAR_BOOL ("comp-enable-subr-trampolines", comp_enable_subr_trampolines,
+              doc: /* If non-nil enable primitive trampoline synthesis.
+This makes primitive functions redefinable or advisable effectively.  */);
+
+  DEFVAR_LISP ("comp-installed-trampolines-h", Vcomp_installed_trampolines_h,
+              doc: /* Hash table subr-name -> installed trampoline.
+This is used to prevent double trampoline instantiation but also to
+protect the trampolines against GC.  */);
+  Vcomp_installed_trampolines_h = CALLN (Fmake_hash_table);
+
+  DEFVAR_LISP ("comp-no-native-file-h", V_comp_no_native_file_h,
+              doc: /* Files for which no deferred compilation has to
+be performed because the bytecode version was explicitly requested by
+the user during load.
+For internal use.  */);
+  V_comp_no_native_file_h = CALLN (Fmake_hash_table, QCtest, Qequal);
+
+  DEFVAR_BOOL ("comp-file-preloaded-p", comp_file_preloaded_p,
+              doc: /* When non-nil assume the file being compiled to
+be preloaded.  */);
+
+  Fprovide (intern_c_string ("nativecomp"), Qnil);
+#endif /* #ifdef HAVE_NATIVE_COMP */
+
+  defsubr (&Snative_comp_available_p);
+}
diff --git a/src/comp.h b/src/comp.h
new file mode 100644
index 0000000..c4af419
--- /dev/null
+++ b/src/comp.h
@@ -0,0 +1,114 @@
+/* Elisp native compiler definitions
+
+Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef COMP_H
+#define COMP_H
+
+/* To keep ifdefs under control.  */
+enum {
+  NATIVE_COMP_FLAG =
+#ifdef HAVE_NATIVE_COMP
+  1
+#else
+  0
+#endif
+};
+
+#include <dynlib.h>
+
+struct Lisp_Native_Comp_Unit
+{
+  union vectorlike_header header;
+  /* The original eln file loaded.  In the pdumper file this is stored
+     as a cons cell of 2 alternative file names: the car is the
+     filename relative to the directory of an installed binary, the
+     cdr is the filename relative to the directory of an uninstalled
+     binary.  This is arranged in loadup.el.  */
+  Lisp_Object file;
+  Lisp_Object optimize_qualities;
+  /* Guard anonymous lambdas against Garbage Collection and serve
+     sanity checks.  */
+  Lisp_Object lambda_gc_guard_h;
+  /* Hash c_name -> d_reloc_imp index.  */
+  Lisp_Object lambda_c_name_idx_h;
+  /* Hash doc-idx -> function documentation.  */
+  Lisp_Object data_fdoc_v;
+  /* Analogous to the constant vector but per compilation unit.  */
+  Lisp_Object data_vec;
+  /* 'data_impure_vec' must be last (see allocate_native_comp_unit).
+     Same as data_vec but for data that cannot be moved to pure space.  */
+  Lisp_Object data_impure_vec;
+  /* STUFFS WE DO NOT DUMP!!  */
+  Lisp_Object *data_imp_relocs;
+  bool loaded_once;
+  bool load_ongoing;
+  dynlib_handle_ptr handle;
+} GCALIGNED_STRUCT;
+
+#ifdef HAVE_NATIVE_COMP
+
+INLINE bool
+NATIVE_COMP_UNITP (Lisp_Object a)
+{
+  return PSEUDOVECTORP (a, PVEC_NATIVE_COMP_UNIT);
+}
+
+INLINE struct Lisp_Native_Comp_Unit *
+XNATIVE_COMP_UNIT (Lisp_Object a)
+{
+  eassert (NATIVE_COMP_UNITP (a));
+  return XUNTAG (a, Lisp_Vectorlike, struct Lisp_Native_Comp_Unit);
+}
+
+/* Defined in comp.c.  */
+
+extern void hash_native_abi (void);
+
+extern Lisp_Object load_comp_unit (struct Lisp_Native_Comp_Unit *comp_u,
+                                  bool loading_dump, bool late_load);
+
+extern void unload_comp_unit (struct Lisp_Native_Comp_Unit *);
+
+extern Lisp_Object native_function_doc (Lisp_Object function);
+
+extern void syms_of_comp (void);
+
+extern void maybe_defer_native_compilation (Lisp_Object function_name,
+                                           Lisp_Object definition);
+
+extern void eln_load_path_final_clean_up (void);
+
+extern void fixup_eln_load_path (Lisp_Object directory);
+
+#else /* #ifdef HAVE_NATIVE_COMP */
+
+static inline void
+maybe_defer_native_compilation (Lisp_Object function_name,
+                               Lisp_Object definition)
+{}
+
+static inline
+void unload_comp_unit (struct Lisp_Native_Comp_Unit *cu)
+{}
+
+extern void syms_of_comp (void);
+
+#endif /* #ifdef HAVE_NATIVE_COMP */
+
+#endif /* #ifndef COMP_H */
diff --git a/src/data.c b/src/data.c
index 3667b03..d547f5d 100644
--- a/src/data.c
+++ b/src/data.c
@@ -88,12 +88,6 @@ XOBJFWD (lispfwd a)
 }
 
 static void
-CHECK_SUBR (Lisp_Object x)
-{
-  CHECK_TYPE (SUBRP (x), Qsubrp, x);
-}
-
-static void
 set_blv_found (struct Lisp_Buffer_Local_Value *blv, int found)
 {
   eassert (found == !EQ (blv->defcell, blv->valcell));
@@ -259,6 +253,8 @@ for example, (type-of 1) returns `integer'.  */)
           }
         case PVEC_MODULE_FUNCTION:
           return Qmodule_function;
+       case PVEC_NATIVE_COMP_UNIT:
+          return Qnative_comp_unit;
         case PVEC_XWIDGET:
           return Qxwidget;
         case PVEC_XWIDGET_VIEW:
@@ -779,6 +775,13 @@ DEFUN ("fset", Ffset, Sfset, 2, 2, 0,
 
   eassert (valid_lisp_object_p (definition));
 
+#ifdef HAVE_NATIVE_COMP
+  if (comp_enable_subr_trampolines
+      && SUBRP (function)
+      && !SUBR_NATIVE_COMPILEDP (function))
+    CALLN (Ffuncall, Qcomp_subr_trampoline_install, symbol);
+#endif
+
   set_symbol_function (symbol, definition);
 
   return definition;
@@ -824,6 +827,8 @@ The return value is undefined.  */)
       Ffset (symbol, definition);
   }
 
+  maybe_defer_native_compilation (symbol, definition);
+
   if (!NILP (docstring))
     Fput (symbol, Qfunction_documentation, docstring);
   /* We used to return `definition', but now that `defun' and `defmacro' expand
@@ -870,6 +875,72 @@ SUBR must be a built-in function.  */)
   return build_string (name);
 }
 
+DEFUN ("subr-native-elisp-p", Fsubr_native_elisp_p, Ssubr_native_elisp_p, 1, 1,
+       0, doc: /* Return t if the object is native compiled lisp
+function, nil otherwise.  */)
+  (Lisp_Object object)
+{
+  return SUBR_NATIVE_COMPILEDP (object) ? Qt : Qnil;
+}
+
+DEFUN ("subr-native-lambda-list", Fsubr_native_lambda_list,
+       Ssubr_native_lambda_list, 1, 1, 0,
+       doc: /* Return the lambda list for a native compiled lisp/d
+function or t otherwise.  */)
+  (Lisp_Object subr)
+{
+  CHECK_SUBR (subr);
+
+  return SUBR_NATIVE_COMPILED_DYNP (subr)
+    ? XSUBR (subr)->lambda_list[0]
+    : Qt;
+}
+
+DEFUN ("subr-type", Fsubr_type,
+       Ssubr_type, 1, 1, 0,
+       doc: /* Return the type of SUBR.  */)
+  (Lisp_Object subr)
+{
+  CHECK_SUBR (subr);
+#ifdef HAVE_NATIVE_COMP
+  return SUBR_TYPE (subr);
+#else
+  return Qnil;
+#endif
+}
+
+#ifdef HAVE_NATIVE_COMP
+
+DEFUN ("subr-native-comp-unit", Fsubr_native_comp_unit,
+       Ssubr_native_comp_unit, 1, 1, 0,
+       doc: /* Return the native compilation unit.  */)
+  (Lisp_Object subr)
+{
+  CHECK_SUBR (subr);
+  return XSUBR (subr)->native_comp_u[0];
+}
+
+DEFUN ("native-comp-unit-file", Fnative_comp_unit_file,
+       Snative_comp_unit_file, 1, 1, 0,
+       doc: /* Return the file of the native compilation unit.  */)
+  (Lisp_Object comp_unit)
+{
+  CHECK_TYPE (NATIVE_COMP_UNITP (comp_unit), Qnative_comp_unit, comp_unit);
+  return XNATIVE_COMP_UNIT (comp_unit)->file;
+}
+
+DEFUN ("native-comp-unit-set-file", Fnative_comp_unit_set_file,
+       Snative_comp_unit_set_file, 2, 2, 0,
+       doc: /* Return the file of the native compilation unit.  */)
+  (Lisp_Object comp_unit, Lisp_Object new_file)
+{
+  CHECK_TYPE (NATIVE_COMP_UNITP (comp_unit), Qnative_comp_unit, comp_unit);
+  XNATIVE_COMP_UNIT (comp_unit)->file = new_file;
+  return comp_unit;
+}
+
+#endif
+
 DEFUN ("interactive-form", Finteractive_form, Sinteractive_form, 1, 1, 0,
        doc: /* Return the interactive form of CMD or nil if none.
 If CMD is not a command, the return value is nil.
@@ -895,6 +966,9 @@ Value, if non-nil, is a list (interactive SPEC).  */)
 
   if (SUBRP (fun))
     {
+      if (SUBR_NATIVE_COMPILEDP (fun) && !NILP (XSUBR (fun)->native_intspec))
+       return XSUBR (fun)->native_intspec;
+
       const char *spec = XSUBR (fun)->intspec;
       if (spec)
        return list2 (Qinteractive,
@@ -3961,6 +4035,7 @@ syms_of_data (void)
   DEFSYM (Qoverlay, "overlay");
   DEFSYM (Qfinalizer, "finalizer");
   DEFSYM (Qmodule_function, "module-function");
+  DEFSYM (Qnative_comp_unit, "native-comp-unit");
   DEFSYM (Quser_ptr, "user-ptr");
   DEFSYM (Qfloat, "float");
   DEFSYM (Qwindow_configuration, "window-configuration");
@@ -4085,6 +4160,14 @@ syms_of_data (void)
   defsubr (&Sbyteorder);
   defsubr (&Ssubr_arity);
   defsubr (&Ssubr_name);
+  defsubr (&Ssubr_native_elisp_p);
+  defsubr (&Ssubr_native_lambda_list);
+  defsubr (&Ssubr_type);
+#ifdef HAVE_NATIVE_COMP
+  defsubr (&Ssubr_native_comp_unit);
+  defsubr (&Snative_comp_unit_file);
+  defsubr (&Snative_comp_unit_set_file);
+#endif
 #ifdef HAVE_MODULES
   defsubr (&Suser_ptrp);
 #endif
diff --git a/src/decompress.c b/src/decompress.c
index 4839249..17224f6 100644
--- a/src/decompress.c
+++ b/src/decompress.c
@@ -25,6 +25,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "buffer.h"
 #include "composite.h"
+#include "md5.h"
 
 #include <verify.h>
 
@@ -66,6 +67,107 @@ init_zlib_functions (void)
 #endif /* WINDOWSNT */
 
 
+
+#define MD5_BLOCKSIZE 32768 /* From md5.c  */
+
+static char acc_buff[2 * MD5_BLOCKSIZE];
+static size_t acc_size;
+
+static void
+accumulate_and_process_md5 (void *data, size_t len, struct md5_ctx *ctxt)
+{
+  eassert (len <= MD5_BLOCKSIZE);
+  /* We may optimize this saving some of these memcpy/move using
+     directly the outer buffers but so far don't bother.  */
+  memcpy (acc_buff + acc_size, data, len);
+  acc_size += len;
+  if (acc_size >= MD5_BLOCKSIZE)
+    {
+      acc_size -= MD5_BLOCKSIZE;
+      md5_process_block (acc_buff, MD5_BLOCKSIZE, ctxt);
+      memmove (acc_buff, acc_buff + MD5_BLOCKSIZE, acc_size);
+    }
+}
+
+static void
+final_process_md5 (struct md5_ctx *ctxt)
+{
+  if (acc_size)
+    {
+      md5_process_bytes (acc_buff, acc_size, ctxt);
+      acc_size = 0;
+    }
+}
+
+int
+md5_gz_stream (FILE *source, void *resblock)
+{
+  z_stream stream;
+  unsigned char in[MD5_BLOCKSIZE];
+  unsigned char out[MD5_BLOCKSIZE];
+
+#ifdef WINDOWSNT
+  if (!zlib_initialized)
+    zlib_initialized = init_zlib_functions ();
+  if (!zlib_initialized)
+    {
+      message1 ("zlib library not found");
+      return -1;
+    }
+#endif
+
+  eassert (!acc_size);
+
+  struct md5_ctx ctx;
+  md5_init_ctx (&ctx);
+
+  /* allocate inflate state */
+  stream.zalloc = Z_NULL;
+  stream.zfree = Z_NULL;
+  stream.opaque = Z_NULL;
+  stream.avail_in = 0;
+  stream.next_in = Z_NULL;
+  int res = inflateInit2 (&stream, MAX_WBITS + 32);
+  if (res != Z_OK)
+    return -1;
+
+  do {
+    stream.avail_in = fread (in, 1, MD5_BLOCKSIZE, source);
+    if (ferror (source)) {
+      inflateEnd (&stream);
+      return -1;
+    }
+    if (stream.avail_in == 0)
+      break;
+    stream.next_in = in;
+
+    do {
+      stream.avail_out = MD5_BLOCKSIZE;
+      stream.next_out = out;
+      res = inflate (&stream, Z_NO_FLUSH);
+
+      if (res != Z_OK && res != Z_STREAM_END)
+       return -1;
+
+      accumulate_and_process_md5 (out, MD5_BLOCKSIZE - stream.avail_out, &ctx);
+    } while (!stream.avail_out);
+
+  } while (res != Z_STREAM_END);
+
+  final_process_md5 (&ctx);
+  inflateEnd (&stream);
+
+  if (res != Z_STREAM_END)
+    return -1;
+
+  md5_finish_ctx (&ctx, resblock);
+
+  return 0;
+}
+#undef MD5_BLOCKSIZE
+
+
+
 struct decompress_unwind_data
 {
   ptrdiff_t old_point, orig, start, nbytes;
diff --git a/src/dispextern.h b/src/dispextern.h
index 8f1d62b..baf9212 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -1270,7 +1270,7 @@ extern struct glyph space_glyph;
 /* True means last display completed.  False means it was preempted.  */
 
 extern bool display_completed;
-
+extern bool delayed_size_change;
 
 
 /************************************************************************
@@ -3652,7 +3652,7 @@ extern void gui_update_window_begin (struct window *);
 extern void gui_update_window_end (struct window *, bool, bool);
 #endif
 void do_pending_window_change (bool);
-void change_frame_size (struct frame *, int, int, bool, bool, bool, bool);
+void change_frame_size (struct frame *, int, int, bool, bool, bool);
 void init_display (void);
 void syms_of_display (void);
 extern void spec_glyph_lookup_face (struct window *, GLYPH *);
diff --git a/src/dispnew.c b/src/dispnew.c
index f594b6d..2760366 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -102,7 +102,7 @@ bool display_completed;
 
 /* True means SIGWINCH happened when not safe.  */
 
-static bool delayed_size_change;
+bool delayed_size_change;
 
 /* A glyph for a space.  */
 
@@ -5770,32 +5770,34 @@ handle_window_change_signal (int sig)
      termcap-controlled terminal, but we can't decide which.
      Therefore, we resize the frames corresponding to each tty.
   */
-  for (tty = tty_list; tty; tty = tty->next) {
+  for (tty = tty_list; tty; tty = tty->next)
+    {
+      if (! tty->term_initted)
+       continue;
 
-    if (! tty->term_initted)
-      continue;
+      /* Suspended tty frames have tty->input == NULL avoid trying to
+        use it.  */
+      if (!tty->input)
+       continue;
 
-    /* Suspended tty frames have tty->input == NULL avoid trying to
-       use it.  */
-    if (!tty->input)
-      continue;
+      get_tty_size (fileno (tty->input), &width, &height);
 
-    get_tty_size (fileno (tty->input), &width, &height);
+      if (width > 5 && height > 2)
+       {
+         Lisp_Object tail, frame;
 
-    if (width > 5 && height > 2) {
-      Lisp_Object tail, frame;
+         FOR_EACH_FRAME (tail, frame)
+           {
+             struct frame *f = XFRAME (frame);
 
-      FOR_EACH_FRAME (tail, frame)
-        if (FRAME_TERMCAP_P (XFRAME (frame)) && FRAME_TTY (XFRAME (frame)) == 
tty)
-          /* Record the new sizes, but don't reallocate the data
-             structures now.  Let that be done later outside of the
-             signal handler.  */
-          change_frame_size (XFRAME (frame), width,
-                            height - FRAME_MENU_BAR_LINES (XFRAME (frame))
-                            - FRAME_TAB_BAR_LINES (XFRAME (frame)),
-                            0, 1, 0, 0);
+             if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty)
+               /* Record the new sizes, but don't reallocate the data
+                  structures now.  Let that be done later outside of the
+                  signal handler.  */
+               change_frame_size (f, width, height, false, true, false);
+           }
+       }
     }
-  }
 }
 
 static void
@@ -5821,15 +5823,17 @@ do_pending_window_change (bool safe)
     {
       Lisp_Object tail, frame;
 
-      delayed_size_change = 0;
+      delayed_size_change = false;
 
       FOR_EACH_FRAME (tail, frame)
        {
          struct frame *f = XFRAME (frame);
 
-         if (f->new_height != 0 || f->new_width != 0)
+         /* Negative new_width or new_height values mean no change is
+            required (a native size can never drop below zero).  */
+         if (f->new_height >= 0 || f->new_width >= 0)
            change_frame_size (f, f->new_width, f->new_height,
-                              0, 0, safe, f->new_pixelwise);
+                              false, false, safe);
        }
     }
 }
@@ -5837,47 +5841,43 @@ do_pending_window_change (bool safe)
 
 static void
 change_frame_size_1 (struct frame *f, int new_width, int new_height,
-                    bool pretend, bool delay, bool safe, bool pixelwise)
+                    bool pretend, bool delay, bool safe)
 {
-  /* If we can't deal with the change now, queue it for later.  */
   if (delay || (redisplaying_p && !safe))
     {
+      if (CONSP (frame_size_history)
+         && ((new_width != f->new_width
+              || new_height != f->new_height
+              || new_width != FRAME_PIXEL_WIDTH (f)
+              || new_height != FRAME_PIXEL_HEIGHT (f))))
+       frame_size_history_extra
+         (f, build_string ("change_frame_size_1, delayed"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+          new_width, new_height, f->new_width, f->new_height);
+
+      /* We can't deal with the change now, queue it for later.  */
       f->new_width = new_width;
       f->new_height = new_height;
-      f->new_pixelwise = pixelwise;
-      delayed_size_change = 1;
+      delayed_size_change = true;
     }
   else
     {
-      /* This size-change overrides any pending one for this frame.  */
-      f->new_height = 0;
-      f->new_width = 0;
-      f->new_pixelwise = 0;
-
-      /* If an argument is zero, set it to the current value.  */
-      if (pixelwise)
-       {
-         new_width = (new_width <= 0) ? FRAME_TEXT_WIDTH (f) : new_width;
-         new_height = (new_height <= 0) ? FRAME_TEXT_HEIGHT (f) : new_height;
-       }
-      else
-       {
-         new_width = (((new_width <= 0) ? FRAME_COLS (f) : new_width)
-                      * FRAME_COLUMN_WIDTH (f));
-         new_height = (((new_height <= 0) ? FRAME_LINES (f) : new_height)
-                       * FRAME_LINE_HEIGHT (f));
-       }
-
-      /* Adjust frame size but make sure set_window_size_hook does not
-        get called.  */
-      adjust_frame_size (f, new_width, new_height, 5, pretend,
-                        Qchange_frame_size);
+      /* Storing -1 in the new_width/new_height slots means that no size
+        change is pending.  Native sizes are always non-negative.  */
+      f->new_height = -1;
+      f->new_width = -1;
+      /* adjust_frame_size wants its arguments in terms of text_width
+        and text_height, so convert them here.  For pathologically
+        small frames, the resulting values may be negative though.  */
+      adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, new_width),
+                        FRAME_PIXEL_TO_TEXT_HEIGHT (f, new_height), 5,
+                        pretend, Qchange_frame_size);
     }
 }
 
 
-/* Change text height/width of frame F.  Values may be given as zero to
-   indicate that no change is needed.
+/* Change native height/width of frame F to NEW_WIDTH/NEW_HEIGHT pixels.
+   Values may be given as -1 to indicate that no change is needed.
 
    If DELAY, assume we're being called from a signal handler, and queue
    the change for later - perhaps the next redisplay.  Since this tries
@@ -5887,7 +5887,7 @@ change_frame_size_1 (struct frame *f, int new_width, int 
new_height,
    change frame sizes while a redisplay is in progress.  */
 void
 change_frame_size (struct frame *f, int new_width, int new_height,
-                  bool pretend, bool delay, bool safe, bool pixelwise)
+                  bool pretend, bool delay, bool safe)
 {
   Lisp_Object tail, frame;
 
@@ -5897,13 +5897,12 @@ change_frame_size (struct frame *f, int new_width, int 
new_height,
          size affects all frames.  Termcap now supports multiple
          ttys. */
       FOR_EACH_FRAME (tail, frame)
-       if (! FRAME_WINDOW_P (XFRAME (frame)))
+       if (!FRAME_WINDOW_P (XFRAME (frame)))
          change_frame_size_1 (XFRAME (frame), new_width, new_height,
-                              pretend, delay, safe, pixelwise);
+                              pretend, delay, safe);
     }
   else
-    change_frame_size_1 (f, new_width, new_height, pretend, delay, safe,
-                        pixelwise);
+    change_frame_size_1 (f, new_width, new_height, pretend, delay, safe);
 }
 
 /***********************************************************************
@@ -6505,9 +6504,8 @@ init_display_interactive (void)
     t->display_info.tty->top_frame = selected_frame;
     change_frame_size (XFRAME (selected_frame),
                        FrameCols (t->display_info.tty),
-                       FrameRows (t->display_info.tty)
-                      - FRAME_MENU_BAR_LINES (f)
-                      - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0);
+                       FrameRows (t->display_info.tty),
+                      false, false, true);
 
     /* Delete the initial terminal. */
     if (--initial_terminal->reference_count == 0
diff --git a/src/doc.c b/src/doc.c
index 1307aa5..01f4368 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -327,6 +327,11 @@ string is passed through `substitute-command-keys'.  */)
     xsignal1 (Qvoid_function, function);
   if (CONSP (fun) && EQ (XCAR (fun), Qmacro))
     fun = XCDR (fun);
+#ifdef HAVE_NATIVE_COMP
+  if (!NILP (Fsubr_native_elisp_p (fun)))
+    doc = native_function_doc (fun);
+  else
+#endif
   if (SUBRP (fun))
     doc = make_fixnum (XSUBR (fun)->doc);
 #ifdef HAVE_MODULES
@@ -495,10 +500,11 @@ store_function_docstring (Lisp_Object obj, EMACS_INT 
offset)
            XSETCAR (tem, make_fixnum (offset));
        }
     }
-
   /* Lisp_Subrs have a slot for it.  */
-  else if (SUBRP (fun))
-    XSUBR (fun)->doc = offset;
+  else if (SUBRP (fun) && !SUBR_NATIVE_COMPILEDP (fun))
+    {
+      XSUBR (fun)->doc = offset;
+    }
 
   /* Bytecode objects sometimes have slots for it.  */
   else if (COMPILEDP (fun))
diff --git a/src/dynlib.c b/src/dynlib.c
index 86f8b7e..1338e91 100644
--- a/src/dynlib.c
+++ b/src/dynlib.c
@@ -301,15 +301,11 @@ dynlib_error (void)
   return dlerror ();
 }
 
-/* FIXME: Currently there is no way to unload a module, so this
-   function is never used.  */
-#if false
 int
 dynlib_close (dynlib_handle_ptr h)
 {
   return dlclose (h) == 0;
 }
-#endif
 
 #else
 
diff --git a/src/editfns.c b/src/editfns.c
index bc73c1e..04b8e85 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2941,6 +2941,8 @@ DEFUN ("propertize", Fpropertize, Spropertize, 1, MANY, 0,
 First argument is the string to copy.
 Remaining arguments form a sequence of PROPERTY VALUE pairs for text
 properties to add to the result.
+
+See Info node `(elisp) Text Properties' for more information.
 usage: (propertize STRING &rest PROPERTIES)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
diff --git a/src/emacs.c b/src/emacs.c
index 9a83a08..645215b 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -37,6 +37,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <mbstring.h>
+#include <filename.h>  /* for IS_ABSOLUTE_FILE_NAME */
 #include "w32.h"
 #include "w32heap.h"
 #endif
@@ -438,9 +439,9 @@ terminate_due_to_signal (int sig, int backtrace_limit)
   /* This shouldn't be executed, but it prevents a warning.  */
   exit (1);
 }
+
 
 /* Code for dealing with Lisp access to the Unix command line.  */
-
 static void
 init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
 {
@@ -482,8 +483,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char 
const *original_pwd)
   if (NILP (Vinvocation_directory))
     {
       Lisp_Object found;
-      int yes = openp (Vexec_path, Vinvocation_name,
-                      Vexec_suffixes, &found, make_fixnum (X_OK), false);
+      int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
+                      &found, make_fixnum (X_OK), false, false);
       if (yes == 1)
        {
          /* Add /: to the front of the name
@@ -738,15 +739,29 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t 
*candidate_size)
      implementation of malloc, since the caller calls our free.  */
 #ifdef WINDOWSNT
   char *prog_fname = w32_my_exename ();
+  if (prog_fname)
+    *candidate_size = strlen (prog_fname) + 1;
   return prog_fname ? xstrdup (prog_fname) : NULL;
 #else  /* !WINDOWSNT */
   char *candidate = NULL;
 
   /* If the executable name contains a slash, we have some kind of
-     path already, so just copy it.  */
+     path already, so just resolve symlinks and return the result.  */
   eassert (argv0);
   if (strchr (argv0, DIRECTORY_SEP))
-    return xstrdup (argv0);
+    {
+      char *real_name = realpath (argv0, NULL);
+
+      if (real_name)
+       {
+         *candidate_size = strlen (real_name) + 1;
+         return real_name;
+       }
+
+      char *val = xstrdup (argv0);
+      *candidate_size = strlen (val) + 1;
+      return val;
+    }
   ptrdiff_t argv0_length = strlen (argv0);
 
   const char *path = getenv ("PATH");
@@ -783,7 +798,22 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t 
*candidate_size)
       struct stat st;
       if (file_access_p (candidate, X_OK)
          && stat (candidate, &st) == 0 && S_ISREG (st.st_mode))
-       return candidate;
+       {
+         /* People put on PATH a symlink to the real Emacs
+            executable, with all the auxiliary files where the real
+            executable lives.  Support that.  */
+         if (lstat (candidate, &st) == 0 && S_ISLNK (st.st_mode))
+           {
+             char *real_name = realpath (candidate, NULL);
+
+             if (real_name)
+               {
+                 *candidate_size = strlen (real_name) + 1;
+                 return real_name;
+               }
+           }
+         return candidate;
+       }
       *candidate = '\0';
     }
   while (*path++ != '\0');
@@ -797,6 +827,7 @@ load_pdump (int argc, char **argv)
 {
   const char *const suffix = ".pdmp";
   int result;
+  char *emacs_executable = argv[0];
   const char *strip_suffix =
 #if defined DOS_NT || defined CYGWIN
     ".exe"
@@ -804,6 +835,7 @@ load_pdump (int argc, char **argv)
     NULL
 #endif
     ;
+  const char *argv0_base = "emacs";
 
   /* TODO: maybe more thoroughly scrub process environment in order to
      make this use case (loading a dump file in an unexeced emacs)
@@ -826,9 +858,19 @@ load_pdump (int argc, char **argv)
       skip_args++;
     }
 
+  /* Where's our executable?  */
+  ptrdiff_t bufsize, exec_bufsize;
+  emacs_executable = load_pdump_find_executable (argv[0], &bufsize);
+  exec_bufsize = bufsize;
+
+  /* If we couldn't find our executable, go straight to looking for
+     the dump in the hardcoded location.  */
+  if (!(emacs_executable && *emacs_executable))
+    goto hardcoded;
+
   if (dump_file)
     {
-      result = pdumper_load (dump_file);
+      result = pdumper_load (dump_file, emacs_executable);
 
       if (result != PDUMPER_LOAD_SUCCESS)
         fatal ("could not load dump file \"%s\": %s",
@@ -842,49 +884,29 @@ load_pdump (int argc, char **argv)
      so we can't use decode_env_path.  We're working in whatever
      encoding the system natively uses for filesystem access, so
      there's no need for character set conversion.  */
-  ptrdiff_t bufsize;
-  dump_file = load_pdump_find_executable (argv[0], &bufsize);
-
-  /* If we couldn't find our executable, go straight to looking for
-     the dump in the hardcoded location.  */
-  if (dump_file && *dump_file)
-    {
-#ifdef WINDOWSNT
-      /* w32_my_exename resolves symlinks internally, so no need to
-        call realpath.  */
-#else
-      char *real_exename = realpath (dump_file, NULL);
-      if (!real_exename)
-        fatal ("could not resolve realpath of \"%s\": %s",
-               dump_file, strerror (errno));
-      xfree (dump_file);
-      dump_file = real_exename;
-#endif
-      ptrdiff_t exenamelen = strlen (dump_file);
-#ifndef WINDOWSNT
-      bufsize = exenamelen + 1;
-#endif
-      if (strip_suffix)
-        {
-         ptrdiff_t strip_suffix_length = strlen (strip_suffix);
-         ptrdiff_t prefix_length = exenamelen - strip_suffix_length;
-         if (0 <= prefix_length
-             && !memcmp (&dump_file[prefix_length], strip_suffix,
-                         strip_suffix_length))
-           exenamelen = prefix_length;
-        }
-      ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
-      if (bufsize < needed)
-       dump_file = xpalloc (dump_file, &bufsize, needed - bufsize, -1, 1);
-      strcpy (dump_file + exenamelen, suffix);
-      result = pdumper_load (dump_file);
-      if (result == PDUMPER_LOAD_SUCCESS)
-        goto out;
-
-      if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
-        fatal ("could not load dump file \"%s\": %s",
-               dump_file, dump_error_to_string (result));
-    }
+  ptrdiff_t exenamelen = strlen (emacs_executable);
+  if (strip_suffix)
+    {
+      ptrdiff_t strip_suffix_length = strlen (strip_suffix);
+      ptrdiff_t prefix_length = exenamelen - strip_suffix_length;
+      if (0 <= prefix_length
+         && !memcmp (&emacs_executable[prefix_length], strip_suffix,
+                     strip_suffix_length))
+       exenamelen = prefix_length;
+    }
+  ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
+  dump_file = xpalloc (NULL, &bufsize, needed - bufsize, -1, 1);
+  memcpy (dump_file, emacs_executable, exenamelen);
+  strcpy (dump_file + exenamelen, suffix);
+  result = pdumper_load (dump_file, emacs_executable);
+  if (result == PDUMPER_LOAD_SUCCESS)
+    goto out;
+
+  if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
+    fatal ("could not load dump file \"%s\": %s",
+          dump_file, dump_error_to_string (result));
+
+ hardcoded:
 
 #ifdef WINDOWSNT
   /* On MS-Windows, PATH_EXEC normally starts with a literal
@@ -895,12 +917,11 @@ load_pdump (int argc, char **argv)
   /* Look for "emacs.pdmp" in PATH_EXEC.  We hardcode "emacs" in
      "emacs.pdmp" so that the Emacs binary still works if the user
      copies and renames it.  */
-  const char *argv0_base = "emacs";
-  ptrdiff_t needed = (strlen (path_exec)
-                      + 1
-                      + strlen (argv0_base)
-                      + strlen (suffix)
-                      + 1);
+  needed = (strlen (path_exec)
+           + 1
+           + strlen (argv0_base)
+           + strlen (suffix)
+           + 1);
   if (bufsize < needed)
     {
       xfree (dump_file);
@@ -908,7 +929,21 @@ load_pdump (int argc, char **argv)
     }
   sprintf (dump_file, "%s%c%s%s",
            path_exec, DIRECTORY_SEP, argv0_base, suffix);
-  result = pdumper_load (dump_file);
+  /* Assume the Emacs binary lives in a sibling directory as set up by
+     the default installation configuration.  */
+  const char *go_up = "../../../../bin/";
+  needed += (strip_suffix ? strlen (strip_suffix) : 0)
+    - strlen (suffix) + strlen (go_up);
+  if (exec_bufsize < needed)
+    {
+      xfree (emacs_executable);
+      emacs_executable = xpalloc (NULL, &exec_bufsize, needed - exec_bufsize,
+                                 -1, 1);
+    }
+  sprintf (emacs_executable, "%s%c%s%s%s",
+          path_exec, DIRECTORY_SEP, go_up, argv0_base,
+          strip_suffix ? strip_suffix : "");
+  result = pdumper_load (dump_file, emacs_executable);
 
   if (result == PDUMPER_LOAD_FILE_NOT_FOUND)
     {
@@ -943,7 +978,7 @@ load_pdump (int argc, char **argv)
 #endif
       sprintf (dump_file, "%s%c%s%s",
               path_exec, DIRECTORY_SEP, argv0_base, suffix);
-      result = pdumper_load (dump_file);
+      result = pdumper_load (dump_file, emacs_executable);
     }
 
   if (result != PDUMPER_LOAD_SUCCESS)
@@ -955,6 +990,7 @@ load_pdump (int argc, char **argv)
 
  out:
   xfree (dump_file);
+  xfree (emacs_executable);
 }
 #endif /* HAVE_PDUMPER */
 
@@ -1812,6 +1848,9 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   init_json ();
 #endif
 
+  if (!initialized)
+    syms_of_comp ();
+
   no_loadup
     = argmatch (argv, argc, "-nl", "--no-loadup", 6, NULL, &skip_args);
 
@@ -1983,7 +2022,8 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   /* Init buffer storage and default directory of main buffer.  */
   init_buffer ();
 
-  init_callproc_1 ();  /* Must precede init_cmdargs and init_sys_modes.  */
+  /* Must precede init_cmdargs and init_sys_modes.  */
+  init_callproc_1 ();
 
   /* Must precede init_lread.  */
   init_cmdargs (argc, argv, skip_args, original_pwd);
@@ -2173,6 +2213,11 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #endif
 
       keys_of_keyboard ();
+
+#ifdef HAVE_NATIVE_COMP
+      /* Must be after the last defsubr has run.  */
+      hash_native_abi ();
+#endif
     }
   else
     {
@@ -2613,6 +2658,10 @@ all of which are called before Emacs is actually killed. 
 */
       unlink (SSDATA (listfile));
     }
 
+#ifdef HAVE_NATIVE_COMP
+  eln_load_path_final_clean_up ();
+#endif
+
   if (FIXNUMP (arg))
     exit_code = (XFIXNUM (arg) < 0
                 ? XFIXNUM (arg) | INT_MIN
@@ -3263,7 +3312,18 @@ because they do not depend on external libraries and are 
always available.
 
 Also note that this is not a generic facility for accessing external
 libraries; only those already known by Emacs will be loaded.  */);
+#ifdef WINDOWSNT
+  /* FIXME: We may need to load libgccjit when dumping before
+     term/w32-win.el defines `dynamic-library-alist`. This will fail
+     if that variable is empty, so add libgccjit-0.dll to it.  */
+  if (will_dump_p ())
+    Vdynamic_library_alist = list1 (list2 (Qgccjit,
+                                           build_string ("libgccjit-0.dll")));
+  else
+    Vdynamic_library_alist = Qnil;
+#else
   Vdynamic_library_alist = Qnil;
+#endif
   Fput (intern_c_string ("dynamic-library-alist"), Qrisky_local_variable, Qt);
 
 #ifdef WINDOWSNT
diff --git a/src/epaths.in b/src/epaths.in
index 1de1e05..0c72610 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -27,6 +27,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 */
 #define PATH_LOADSEARCH "/usr/local/share/emacs/lisp"
 
+/* Like PATH_LOADSEARCH, but contains the relative path from the
+   installation directory.
+*/
+#define PATH_REL_LOADSEARCH ""
 
 /* Like PATH_LOADSEARCH, but contains the non-standard pieces.
    These are the site-lisp directories.  Configure sets this to
diff --git a/src/eval.c b/src/eval.c
index fd93f5b..aeedcc5 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -219,8 +219,17 @@ void
 init_eval_once (void)
 {
   /* Don't forget to update docs (lispref node "Local Variables").  */
-  max_specpdl_size = 1800; /* See bug#46818.  */
-  max_lisp_eval_depth = 800;
+  if (!NATIVE_COMP_FLAG)
+    {
+      max_specpdl_size = 1800; /* See bug#46818.  */
+      max_lisp_eval_depth = 800;
+    }
+  else
+    {
+      /* Original values increased for comp.el.  */
+      max_specpdl_size = 2500;
+      max_lisp_eval_depth = 1600;
+    }
   Vrun_hooks = Qnil;
   pdumper_do_now_and_after_load (init_eval_once_for_pdumper);
 }
@@ -1521,6 +1530,90 @@ internal_condition_case_2 (Lisp_Object (*bfun) 
(Lisp_Object, Lisp_Object),
     }
 }
 
+/* Like internal_condition_case_1 but call BFUN with ARG1, ARG2, ARG3 as
+   its arguments.  */
+
+Lisp_Object
+internal_condition_case_3 (Lisp_Object (*bfun) (Lisp_Object, Lisp_Object,
+                                                Lisp_Object),
+                           Lisp_Object arg1, Lisp_Object arg2, Lisp_Object 
arg3,
+                           Lisp_Object handlers,
+                           Lisp_Object (*hfun) (Lisp_Object))
+{
+  struct handler *c = push_handler (handlers, CONDITION_CASE);
+  if (sys_setjmp (c->jmp))
+    {
+      Lisp_Object val = handlerlist->val;
+      clobbered_eassert (handlerlist == c);
+      handlerlist = handlerlist->next;
+      return hfun (val);
+    }
+  else
+    {
+      Lisp_Object val = bfun (arg1, arg2, arg3);
+      eassert (handlerlist == c);
+      handlerlist = c->next;
+      return val;
+    }
+}
+
+/* Like internal_condition_case_1 but call BFUN with ARG1, ARG2, ARG3, ARG4 as
+   its arguments.  */
+
+Lisp_Object
+internal_condition_case_4 (Lisp_Object (*bfun) (Lisp_Object, Lisp_Object,
+                                                Lisp_Object, Lisp_Object),
+                           Lisp_Object arg1, Lisp_Object arg2,
+                           Lisp_Object arg3, Lisp_Object arg4,
+                           Lisp_Object handlers,
+                           Lisp_Object (*hfun) (Lisp_Object))
+{
+  struct handler *c = push_handler (handlers, CONDITION_CASE);
+  if (sys_setjmp (c->jmp))
+    {
+      Lisp_Object val = handlerlist->val;
+      clobbered_eassert (handlerlist == c);
+      handlerlist = handlerlist->next;
+      return hfun (val);
+    }
+  else
+    {
+      Lisp_Object val = bfun (arg1, arg2, arg3, arg4);
+      eassert (handlerlist == c);
+      handlerlist = c->next;
+      return val;
+    }
+}
+
+/* Like internal_condition_case_1 but call BFUN with ARG1, ARG2, ARG3,
+   ARG4, ARG5 as its arguments.  */
+
+Lisp_Object
+internal_condition_case_5 (Lisp_Object (*bfun) (Lisp_Object, Lisp_Object,
+                                                Lisp_Object, Lisp_Object,
+                                               Lisp_Object),
+                           Lisp_Object arg1, Lisp_Object arg2,
+                           Lisp_Object arg3, Lisp_Object arg4,
+                          Lisp_Object arg5, Lisp_Object handlers,
+                           Lisp_Object (*hfun) (Lisp_Object))
+{
+  struct handler *c = push_handler (handlers, CONDITION_CASE);
+  if (sys_setjmp (c->jmp))
+    {
+      Lisp_Object val = handlerlist->val;
+      clobbered_eassert (handlerlist == c);
+      handlerlist = handlerlist->next;
+      return hfun (val);
+    }
+  else
+    {
+      Lisp_Object val = bfun (arg1, arg2, arg3, arg4, arg5);
+      eassert (handlerlist == c);
+      handlerlist = c->next;
+      return val;
+    }
+}
+
 /* Like internal_condition_case but call BFUN with NARGS as first,
    and ARGS as second argument.  */
 
@@ -2356,7 +2449,7 @@ eval_sub (Lisp_Object form)
   else if (!NILP (fun) && (fun = XSYMBOL (fun)->u.s.function, SYMBOLP (fun)))
     fun = indirect_function (fun);
 
-  if (SUBRP (fun))
+  if (SUBRP (fun) && !SUBR_NATIVE_COMPILED_DYNP (fun))
     {
       Lisp_Object args_left = original_args;
       ptrdiff_t numargs = list_length (args_left);
@@ -2459,7 +2552,9 @@ eval_sub (Lisp_Object form)
            }
        }
     }
-  else if (COMPILEDP (fun) || MODULE_FUNCTIONP (fun))
+  else if (COMPILEDP (fun)
+          || SUBR_NATIVE_COMPILED_DYNP (fun)
+          || MODULE_FUNCTIONP (fun))
     return apply_lambda (fun, original_args, count);
   else
     {
@@ -2937,9 +3032,11 @@ usage: (funcall FUNCTION &rest ARGUMENTS)  */)
       && (fun = XSYMBOL (fun)->u.s.function, SYMBOLP (fun)))
     fun = indirect_function (fun);
 
-  if (SUBRP (fun))
+  if (SUBRP (fun) && !SUBR_NATIVE_COMPILED_DYNP (fun))
     val = funcall_subr (XSUBR (fun), numargs, args + 1);
-  else if (COMPILEDP (fun) || MODULE_FUNCTIONP (fun))
+  else if (COMPILEDP (fun)
+          || SUBR_NATIVE_COMPILED_DYNP (fun)
+          || MODULE_FUNCTIONP (fun))
     val = funcall_lambda (fun, numargs, args + 1);
   else
     {
@@ -3149,6 +3246,11 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
   else if (MODULE_FUNCTIONP (fun))
     return funcall_module (fun, nargs, arg_vector);
 #endif
+  else if (SUBR_NATIVE_COMPILED_DYNP (fun))
+    {
+      syms_left = XSUBR (fun)->lambda_list[0];
+      lexenv = Qnil;
+    }
   else
     emacs_abort ();
 
@@ -3209,6 +3311,13 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
 
   if (CONSP (fun))
     val = Fprogn (XCDR (XCDR (fun)));
+  else if (SUBR_NATIVE_COMPILEDP (fun))
+    {
+      eassert (SUBR_NATIVE_COMPILED_DYNP (fun));
+      /* No need to use funcall_subr as we have zero arguments by
+        construction.  */
+      val = XSUBR (fun)->function.a0 ();
+    }
   else
     val = fetch_and_exec_byte_code (fun, Qnil, 0, NULL);
 
diff --git a/src/fns.c b/src/fns.c
index 1758148..41429c8 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4492,6 +4492,15 @@ check_mutable_hash_table (Lisp_Object obj, struct 
Lisp_Hash_Table *h)
   eassert (!PURE_P (h));
 }
 
+static void
+collect_interval (INTERVAL interval, Lisp_Object collector)
+{
+  nconc2 (collector,
+         list1(list3 (make_fixnum (interval->position),
+                      make_fixnum (interval->position + LENGTH (interval)),
+                      interval->plist)));
+}
+
 /* Put an entry into hash table H that associates KEY with VALUE.
    HASH is a previously computed hash code of KEY.
    Value is the index of the entry in H matching KEY.  */
@@ -4949,6 +4958,30 @@ Hash codes are not guaranteed to be preserved across 
Emacs sessions.  */)
   return hashfn_equal (obj, NULL);
 }
 
+DEFUN ("sxhash-equal-including-properties", Fsxhash_equal_including_properties,
+       Ssxhash_equal_including_properties, 1, 1, 0,
+       doc: /* Return an integer hash code for OBJ suitable for
+`equal-including-properties'.
+If (sxhash-equal-including-properties A B), then
+(= (sxhash-equal-including-properties A) (sxhash-equal-including-properties 
B)).
+
+Hash codes are not guaranteed to be preserved across Emacs sessions.  */)
+  (Lisp_Object obj)
+{
+  if (STRINGP (obj))
+    {
+      Lisp_Object collector = Fcons (Qnil, Qnil);
+      traverse_intervals (string_intervals (obj), 0, collect_interval,
+                         collector);
+      return
+       make_ufixnum (
+         SXHASH_REDUCE (sxhash_combine (sxhash (obj),
+                                        sxhash (CDR (collector)))));
+    }
+
+  return hashfn_equal (obj, NULL);
+}
+
 DEFUN ("make-hash-table", Fmake_hash_table, Smake_hash_table, 0, MANY, 0,
        doc: /* Create and return a new hash table.
 
@@ -5832,15 +5865,6 @@ Case is always significant and text properties are 
ignored. */)
   return make_int (string_byte_to_char (haystack, res - SSDATA (haystack)));
 }
 
-static void
-collect_interval (INTERVAL interval, Lisp_Object collector)
-{
-  nconc2 (collector,
-         list1(list3 (make_fixnum (interval->position),
-                      make_fixnum (interval->position + LENGTH (interval)),
-                      interval->plist)));
-}
-
 DEFUN ("object-intervals", Fobject_intervals, Sobject_intervals, 1, 1, 0,
        doc: /* Return a copy of the text properties of OBJECT.
 OBJECT must be a buffer or a string.
@@ -5922,6 +5946,7 @@ syms_of_fns (void)
   defsubr (&Ssxhash_eq);
   defsubr (&Ssxhash_eql);
   defsubr (&Ssxhash_equal);
+  defsubr (&Ssxhash_equal_including_properties);
   defsubr (&Smake_hash_table);
   defsubr (&Scopy_hash_table);
   defsubr (&Shash_table_count);
diff --git a/src/frame.c b/src/frame.c
index 648cc65..baa0e47 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -150,29 +150,6 @@ get_frame_param (struct frame *frame, Lisp_Object prop)
 }
 
 
-void
-frame_size_history_add (struct frame *f, Lisp_Object fun_symbol,
-                       int width, int height, Lisp_Object rest)
-{
-  Lisp_Object frame;
-
-  XSETFRAME (frame, f);
-  if (CONSP (frame_size_history)
-      && FIXNUMP (XCAR (frame_size_history))
-      && 0 < XFIXNUM (XCAR (frame_size_history)))
-    frame_size_history =
-      Fcons (make_fixnum (XFIXNUM (XCAR (frame_size_history)) - 1),
-            Fcons (list4
-                   (frame, fun_symbol,
-                    ((width > 0)
-                     ? list4i (FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
-                               width, height)
-                     : Qnil),
-                    rest),
-                   XCDR (frame_size_history)));
-}
-
-
 /* Return 1 if `frame-inhibit-implied-resize' is non-nil or fullscreen
    state of frame F would be affected by a vertical (horizontal if
    HORIZONTAL is true) resize.  PARAMETER is the symbol of the frame
@@ -193,78 +170,54 @@ frame_inhibit_resize (struct frame *f, bool horizontal, 
Lisp_Object parameter)
          || FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))
        : ((horizontal && f->inhibit_horizontal_resize)
          || (!horizontal && f->inhibit_vertical_resize)));
-  if (inhibit && !FRAME_TERMCAP_P (f) && !FRAME_MSDOS_P (f))
-    frame_size_history_add
-      (f, Qframe_inhibit_resize, 0, 0,
-       list5 (horizontal ? Qt : Qnil, parameter,
-             f->after_make_frame ? Qt : Qnil,
-             frame_inhibit_implied_resize,
-             fullscreen));
 
   return inhibit;
 }
 
+
+/** Set menu bar lines for a TTY frame.  */
 static void
 set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 {
-  int nlines;
   int olines = FRAME_MENU_BAR_LINES (f);
+  int nlines = TYPE_RANGED_FIXNUMP (int, value) ? XFIXNUM (value) : 0;
 
   /* Right now, menu bars don't work properly in minibuf-only frames;
      most of the commands try to apply themselves to the minibuffer
      frame itself, and get an error because you can't switch buffers
      in or split the minibuffer window.  */
-  if (FRAME_MINIBUF_ONLY_P (f))
-    return;
-
-  if (TYPE_RANGED_FIXNUMP (int, value))
-    nlines = XFIXNUM (value);
-  else
-    nlines = 0;
-
-  if (nlines != olines)
+  if (!FRAME_MINIBUF_ONLY_P (f) && nlines != olines)
     {
       windows_or_buffers_changed = 14;
-      FRAME_MENU_BAR_LINES (f) = nlines;
-      FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
-      change_frame_size (f, FRAME_COLS (f),
-                        FRAME_LINES (f) + olines - nlines,
-                        0, 1, 0, 0);
+      FRAME_MENU_BAR_LINES (f) = FRAME_MENU_BAR_HEIGHT (f) = nlines;
+      change_frame_size (f, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+                        false, true, false);
     }
 }
 
+
+/** Set tab bar lines for a TTY frame.  */
 static void
 set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 {
-  int nlines;
   int olines = FRAME_TAB_BAR_LINES (f);
+  int nlines = TYPE_RANGED_FIXNUMP (int, value) ? XFIXNUM (value) : 0;
 
   /* Right now, tab bars don't work properly in minibuf-only frames;
      most of the commands try to apply themselves to the minibuffer
      frame itself, and get an error because you can't switch buffers
      in or split the minibuffer window.  */
-  if (FRAME_MINIBUF_ONLY_P (f))
-    return;
-
-  if (TYPE_RANGED_FIXNUMP (int, value))
-    nlines = XFIXNUM (value);
-  else
-    nlines = 0;
-
-  if (nlines != olines)
+  if (!FRAME_MINIBUF_ONLY_P (f) && nlines != olines)
     {
       windows_or_buffers_changed = 14;
-      FRAME_TAB_BAR_LINES (f) = nlines;
-      FRAME_TAB_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
-      change_frame_size (f, FRAME_COLS (f),
-                        FRAME_LINES (f) + olines - nlines,
-                        0, 1, 0, 0);
+      FRAME_TAB_BAR_LINES (f) = FRAME_TAB_BAR_HEIGHT (f) = nlines;
+      change_frame_size (f, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+                        false, true, false);
     }
 }
 
 Lisp_Object Vframe_list;
 
-
 DEFUN ("framep", Fframep, Sframep, 1, 1, 0,
        doc: /* Return non-nil if OBJECT is a frame.
 Value is:
@@ -368,14 +321,15 @@ DEFUN ("frame-windows-min-size", Fframe_windows_min_size,
  *
  * If `frame-windows-min-size' is called, it will make sure that the
  * return value accommodates all windows of FRAME respecting the values
- * of `window-min-height' (`window-min-width' if HORIZONTAL is non-nil).
- * With IGNORE non-nil the values of these variables are ignored.
+ * of `window-min-height' (`window-min-width' if HORIZONTAL is
+ * non-nil) and `window-safe-min-height' (`window-safe-min-width')
+ * according to IGNORE (see `window-min-size').
  *
  * In either case, never return a value less than 1.  For TTY frames,
  * additionally limit the minimum frame height to a value large enough
- * to support the menu bar, the mode line, and the echo area.
+ * to support menu bar, tab bar, mode line and echo area.
  */
-static int
+int
 frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal,
                        Lisp_Object ignore, Lisp_Object pixelwise)
 {
@@ -407,6 +361,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object 
horizontal,
   else
     retval = XFIXNUM (call4 (Qframe_windows_min_size, frame, horizontal,
                          ignore, pixelwise));
+
   /* Don't allow too small height of text-mode frames, or else cm.c
      might abort in cmcheckmagic.  */
   if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal))
@@ -415,6 +370,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object 
horizontal,
                        + FRAME_TAB_BAR_LINES (f)
                        + FRAME_WANTS_MODELINE_P (f)
                        + 2);   /* one text line and one echo-area line */
+
       if (retval < min_height)
        retval = min_height;
     }
@@ -476,9 +432,10 @@ keep_ratio (struct frame *f, struct frame *p, int 
old_width, int old_height,
              if (CONSP (keep_ratio)
                  && (NILP (Fcar (keep_ratio))
                      || EQ (Fcar (keep_ratio), Qheight_only))
-                 && p->pixel_width - f->pixel_width < pos_x)
+                 && FRAME_PIXEL_WIDTH (p) - FRAME_PIXEL_WIDTH (f) < pos_x)
                {
-                 int p_f_width = p->pixel_width - f->pixel_width;
+                 int p_f_width
+                   = FRAME_PIXEL_WIDTH (p) - FRAME_PIXEL_WIDTH (f);
 
                  if (p_f_width <= 0)
                    pos_x = 0;
@@ -498,14 +455,15 @@ keep_ratio (struct frame *f, struct frame *p, int 
old_width, int old_height,
              if (CONSP (keep_ratio)
                  && (NILP (Fcar (keep_ratio))
                      || EQ (Fcar (keep_ratio), Qwidth_only))
-                 && p->pixel_height - f->pixel_height < pos_y)
+                 && FRAME_PIXEL_HEIGHT (p) - FRAME_PIXEL_HEIGHT (f) < pos_y)
                /* When positional adjustment was requested and the
                   width of F should remain unaltered, try to constrain
                   F to its parent.  This means that when the parent
                   frame is enlarged later the child's original position
                   won't get restored.  */
                {
-                 int p_f_height = p->pixel_height - f->pixel_height;
+                 int p_f_height
+                   = FRAME_PIXEL_HEIGHT (p) - FRAME_PIXEL_HEIGHT (f);
 
                  if (p_f_height <= 0)
                    pos_y = 0;
@@ -525,60 +483,143 @@ keep_ratio (struct frame *f, struct frame *p, int 
old_width, int old_height,
          if (CONSP (keep_ratio) && EQ (Fcar (keep_ratio), Qheight_only))
            pixel_width = -1;
          else
-           {
-             pixel_width = (int)(f->pixel_width * width_factor + 0.5);
-             pixel_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixel_width);
-           }
+           pixel_width
+             = (int)(FRAME_PIXEL_WIDTH (f) * width_factor + 0.5);
 
          if (CONSP (keep_ratio) && EQ (Fcar (keep_ratio), Qwidth_only))
            pixel_height = -1;
          else
-           {
-             pixel_height = (int)(f->pixel_height * height_factor + 0.5);
-             pixel_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixel_height);
-           }
+           pixel_height
+             = (int)(FRAME_PIXEL_HEIGHT (f) * height_factor + 0.5);
 
-         adjust_frame_size (f, pixel_width, pixel_height, 1, 0,
-                            Qkeep_ratio);
+         adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, pixel_width),
+                            FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixel_height), 1,
+                            false, Qkeep_ratio);
        }
     }
 }
 #endif
 
 
+static void
+frame_size_history_adjust (struct frame *f, int inhibit, Lisp_Object parameter,
+                          int old_text_width, int old_text_height,
+                          int new_text_width, int new_text_height,
+                          int old_text_cols, int old_text_lines,
+                          int new_text_cols, int new_text_lines,
+                          int old_native_width, int old_native_height,
+                          int new_native_width, int new_native_height,
+                          int old_inner_width, int old_inner_height,
+                          int new_inner_width, int new_inner_height,
+                          int min_inner_width, int min_inner_height,
+                          bool inhibit_horizontal, bool inhibit_vertical)
+{
+  Lisp_Object frame;
+
+  XSETFRAME (frame, f);
+  if (CONSP (frame_size_history)
+      && FIXNUMP (XCAR (frame_size_history))
+      && 0 < XFIXNUM (XCAR (frame_size_history)))
+    frame_size_history =
+      Fcons (make_fixnum (XFIXNUM (XCAR (frame_size_history)) - 1),
+            Fcons (Fcons (list4 (frame, make_fixnum (5),
+                                 make_fixnum (inhibit), parameter),
+                          list5 (list4i (old_text_width, old_text_height,
+                                         new_text_width, new_text_height),
+                                 list4i (old_text_cols, old_text_lines,
+                                         new_text_cols, new_text_lines),
+                                 list4i (old_native_width, old_native_height,
+                                         new_native_width, new_native_height),
+                                 list4i (old_inner_width, old_inner_height,
+                                         new_inner_width,  new_inner_height),
+                                 list4 (make_fixnum (min_inner_width),
+                                        make_fixnum (min_inner_height),
+                                        inhibit_horizontal ? Qt : Qnil,
+                                        inhibit_vertical ? Qt : Qnil))),
+                   XCDR (frame_size_history)));
+}
+
+
+void
+frame_size_history_plain (struct frame *f, Lisp_Object parameter)
+{
+  Lisp_Object frame;
+
+  XSETFRAME (frame, f);
+  if (CONSP (frame_size_history)
+      && FIXNUMP (XCAR (frame_size_history))
+      && 0 < XFIXNUM (XCAR (frame_size_history)))
+    frame_size_history =
+      Fcons (make_fixnum (XFIXNUM (XCAR (frame_size_history)) - 1),
+            Fcons (Fcons (list3 (frame, make_fixnum (1), parameter), Qt),
+                   XCDR (frame_size_history)));
+}
+
+
+void
+frame_size_history_extra (struct frame *f, Lisp_Object parameter,
+                         int pixel_width, int pixel_height,
+                         int extra_width, int extra_height,
+                         int delayed_width, int delayed_height)
+{
+  Lisp_Object frame;
+
+  XSETFRAME (frame, f);
+  if (CONSP (frame_size_history)
+      && FIXNUMP (XCAR (frame_size_history))
+      && 0 < XFIXNUM (XCAR (frame_size_history)))
+    frame_size_history =
+      Fcons (make_fixnum (XFIXNUM (XCAR (frame_size_history)) - 1),
+            Fcons (Fcons (list3 (frame, make_fixnum (2), parameter),
+                          list2 (list4i (pixel_width, pixel_height,
+                                         extra_width, extra_height),
+                                 list2i (delayed_width, delayed_height))),
+                   XCDR (frame_size_history)));
+}
+
+
 /**
  * adjust_frame_size:
  *
- * Adjust size of frame F.  NEW_WIDTH and NEW_HEIGHT specify the new
- * text size of F in pixels.  A value of -1 means no change is requested
- * for that direction (but the frame may still have to be resized to
- * accommodate windows with their minimum sizes).  This can either issue
- * a request to resize the frame externally (via set_window_size_hook), to
- * resize the frame internally (via resize_frame_windows) or do nothing
- * at all.
+ * Adjust size of frame F.  NEW_TEXT_WIDTH and NEW_TEXT_HEIGHT specify
+ * the new text size of F in pixels.  When INHIBIT equals 2, 3 or 4, a
+ * value of -1 means to leave the text size of F unchanged and adjust,
+ * if necessary and possible, F's native size accordingly.  When INHIBIT
+ * equals 0, 1 or 5, a negative value means that the frame has been (or
+ * should be) made pathologically small which usually means that parts
+ * of the frame's windows may not be entirely visible.
  *
- * The argument INHIBIT can assume the following values:
+ * The effect of calling this function can be to either issue a request
+ * to resize the frame externally (via set_window_size_hook), to resize
+ * the frame internally (via resize_frame_windows) or to do nothing.
+ *
+ * The argument INHIBIT controls whether set_window_size_hook may be
+ * called and can assume the following values:
  *
  * 0 means to unconditionally call set_window_size_hook even if sizes
  *   apparently do not change.  Fx_create_frame uses this to pass the
  *   initial size to the window manager.
  *
- * 1 means to call set_window_size_hook if the native frame size really
- *   changes.  Fset_frame_size, Fset_frame_height, ... use this.
+ * 1 means to call set_window_size_hook if the native frame size should
+ *   change.  Fset_frame_size and friends and width and height parameter
+ *   changes use this.
  *
  * 2 means to call set_window_size_hook provided frame_inhibit_resize
- *   allows it.  The menu and tool bar code use this ("3" won't work
- *   here in general because menu and tool bar are often not counted in
- *   the frame's text height).
+ *   allows it.  The code updating external menu and tool bars uses this
+ *   to keep the height of the native frame unaltered when one of these
+ *   bars is added or removed.  This means that Emacs has to work
+ *   against the window manager which usually tries to keep the combined
+ *   height (native frame plus bar) unaltered.
  *
- * 3 means call set_window_size_hook if window minimum sizes must be
- *   preserved or frame_inhibit_resize allows it.
- *   gui_set_left_fringe, gui_set_scroll_bar_width, gui_new_font
- *   ... use (or should use) this.
+ * 3 means to call set_window_size_hook if window minimum sizes must be
+ *   preserved or frame_inhibit_resize allows it.  This is the default
+ *   for parameters accounted for in a frame's text size like fringes,
+ *   scroll bars, internal border, tab bar, internal tool and menu bars.
+ *   It's also used when the frame's default font changes.
  *
- * 4 means call set_window_size_hook only if window minimum sizes must
- *   be preserved.  x_set_right_divider_width, x_set_border_width and
- *   the code responsible for wrapping the tool bar use this.
+ * 4 means to call set_window_size_hook only if window minimum sizes
+ *   must be preserved.  The code for setting up window dividers and
+ *   that responsible for wrapping the (internal) tool bar use this.
  *
  * 5 means to never call set_window_size_hook.  change_frame_size uses
  *   this.
@@ -590,146 +631,172 @@ keep_ratio (struct frame *f, struct frame *p, int 
old_width, int old_height,
  * PRETEND is as for change_frame_size.  PARAMETER, if non-nil, is the
  * symbol of the parameter changed (like `menu-bar-lines', `font', ...).
  * This is passed on to frame_inhibit_resize to let the latter decide on
- * a case-by-case basis whether the frame may be resized externally.
+ * a case-by-case basis whether set_window_size_hook should be called.
  */
 void
-adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit,
-                  bool pretend, Lisp_Object parameter)
+adjust_frame_size (struct frame *f, int new_text_width, int new_text_height,
+                  int inhibit, bool pretend, Lisp_Object parameter)
 {
   int unit_width = FRAME_COLUMN_WIDTH (f);
   int unit_height = FRAME_LINE_HEIGHT (f);
-  int old_pixel_width = FRAME_PIXEL_WIDTH (f);
-  int old_pixel_height = FRAME_PIXEL_HEIGHT (f);
-  int old_cols = FRAME_COLS (f);
-  int old_lines = FRAME_LINES (f);
-  int new_pixel_width, new_pixel_height;
-  /* The following two values are calculated from the old frame pixel
-     sizes and any "new" settings for tool bar, menu bar and internal
-     borders.  We do it this way to detect whether we have to call
-     set_window_size_hook as consequence of the new settings.  */
-  int windows_width = FRAME_WINDOWS_WIDTH (f);
-  int windows_height = FRAME_WINDOWS_HEIGHT (f);
-  int min_windows_width, min_windows_height;
-  /* These are a bit tedious, maybe we should use a macro.  */
+  int old_native_width = FRAME_PIXEL_WIDTH (f);
+  int old_native_height = FRAME_PIXEL_HEIGHT (f);
+  int new_native_width, new_native_height;
+  /* The desired minimum inner width and height of the frame calculated
+     via 'frame-windows-min-size'.  */
+  int min_inner_width, min_inner_height;
+  /* Get the "old" inner width, height and position of F via its root
+     window and the minibuffer window.  We cannot use FRAME_INNER_WIDTH
+     and FRAME_INNER_HEIGHT here since the internal border and the top
+     margin may have been already set to new values.  */
   struct window *r = XWINDOW (FRAME_ROOT_WINDOW (f));
-  int old_windows_width = WINDOW_PIXEL_WIDTH (r);
-  int old_windows_height
+  int old_inner_width = WINDOW_PIXEL_WIDTH (r);
+  int old_inner_height
     = (WINDOW_PIXEL_HEIGHT (r)
        + ((FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
          ? WINDOW_PIXEL_HEIGHT (XWINDOW (FRAME_MINIBUF_WINDOW (f)))
          : 0));
-  int new_windows_width, new_windows_height;
+  int new_inner_width, new_inner_height;
+  int old_text_cols = FRAME_COLS (f);
+  int old_text_lines = FRAME_LINES (f);
+  int new_text_cols, new_text_lines;
   int old_text_width = FRAME_TEXT_WIDTH (f);
   int old_text_height = FRAME_TEXT_HEIGHT (f);
-  /* If a size is < 0 use the old value.  */
-  int new_text_width = (new_width >= 0) ? new_width : old_text_width;
-  int new_text_height = (new_height >= 0) ? new_height : old_text_height;
-  int new_cols, new_lines;
   bool inhibit_horizontal, inhibit_vertical;
   Lisp_Object frame;
 
   XSETFRAME (frame, f);
 
-  frame_size_history_add
-    (f, Qadjust_frame_size_1, new_text_width, new_text_height,
-     list2 (parameter, make_fixnum (inhibit)));
-
-  /* The following two values are calculated from the old window body
-     sizes and any "new" settings for scroll bars, dividers, fringes and
-     margins (though the latter should have been processed already).  */
-  min_windows_width
-    = frame_windows_min_size (frame, Qt, (inhibit == 5) ? Qt : Qnil, Qt);
-  min_windows_height
-    = frame_windows_min_size (frame, Qnil, (inhibit == 5) ? Qt : Qnil, Qt);
+  min_inner_width
+    = frame_windows_min_size (frame, Qt, (inhibit == 5) ? Qsafe : Qnil, Qt);
+  min_inner_height
+    = frame_windows_min_size (frame, Qnil, (inhibit == 5) ? Qsafe : Qnil, Qt);
 
   if (inhibit >= 2 && inhibit <= 4)
     /* When INHIBIT is in [2..4] inhibit if the "old" window sizes stay
        within the limits and either resizing is inhibited or INHIBIT
        equals 4.  */
     {
-      inhibit_horizontal = (windows_width >= min_windows_width
+      if (new_text_width == -1)
+       new_text_width = FRAME_TEXT_WIDTH (f);
+      if (new_text_height == -1)
+       new_text_height = FRAME_TEXT_HEIGHT (f);
+
+      inhibit_horizontal = (FRAME_INNER_WIDTH (f) >= min_inner_width
                             && (inhibit == 4
                                 || frame_inhibit_resize (f, true, parameter)));
-      inhibit_vertical = (windows_height >= min_windows_height
+      inhibit_vertical = (FRAME_INNER_HEIGHT (f) >= min_inner_height
                           && (inhibit == 4
                               || frame_inhibit_resize (f, false, parameter)));
     }
   else
-    /* Otherwise inhibit if INHIBIT equals 5.  */
+    /* Otherwise inhibit if INHIBIT equals 5.  If we wanted to overrule
+       the WM do that here (could lead to some sort of eternal fight
+       with the WM).  */
     inhibit_horizontal = inhibit_vertical = inhibit == 5;
 
-  new_pixel_width = ((inhibit_horizontal && (inhibit < 5))
-                    ? old_pixel_width
-                    : max (FRAME_TEXT_TO_PIXEL_WIDTH (f, new_text_width),
-                           min_windows_width
-                           + 2 * FRAME_INTERNAL_BORDER_WIDTH (f)));
-  new_windows_width = new_pixel_width - 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
-  new_text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, new_pixel_width);
-  new_cols = new_text_width / unit_width;
-
-  new_pixel_height = ((inhibit_vertical && (inhibit < 5))
-                     ? old_pixel_height
-                     : max (FRAME_TEXT_TO_PIXEL_HEIGHT (f, new_text_height),
-                            min_windows_height
-                            + FRAME_TOP_MARGIN_HEIGHT (f)
+  new_native_width = ((inhibit_horizontal && inhibit < 5)
+                     ? old_native_width
+                     : max (FRAME_TEXT_TO_PIXEL_WIDTH (f, new_text_width),
+                            min_inner_width
                             + 2 * FRAME_INTERNAL_BORDER_WIDTH (f)));
-  new_windows_height = (new_pixel_height
-                       - FRAME_TOP_MARGIN_HEIGHT (f)
-                       - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
-  new_text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, new_pixel_height);
-  new_lines = new_text_height / unit_height;
+  new_inner_width = new_native_width - 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
+  new_text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, new_native_width);
+  new_text_cols = new_text_width / unit_width;
+
+  new_native_height = ((inhibit_vertical && inhibit < 5)
+                      ? old_native_height
+                      : max (FRAME_TEXT_TO_PIXEL_HEIGHT (f, new_text_height),
+                             min_inner_height
+                             + FRAME_TOP_MARGIN_HEIGHT (f)
+                             + 2 * FRAME_INTERNAL_BORDER_WIDTH (f)));
+  new_inner_height = (new_native_height
+                     - FRAME_TOP_MARGIN_HEIGHT (f)
+                     - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
+  new_text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, new_native_height);
+  new_text_lines = new_text_height / unit_height;
 
-#ifdef HAVE_WINDOW_SYSTEM
   if (FRAME_WINDOW_P (f)
       && f->can_set_window_size
       && ((!inhibit_horizontal
-          && (new_pixel_width != old_pixel_width
+          && (new_native_width != old_native_width
               || inhibit == 0 || inhibit == 2))
          || (!inhibit_vertical
-             && (new_pixel_height != old_pixel_height
+             && (new_native_height != old_native_height
                  || inhibit == 0 || inhibit == 2))))
-    /* We are either allowed to change the frame size or the minimum
-       sizes request such a change.  Do not care for fixing minimum
-       sizes here, we do that eventually when we're called from
-       change_frame_size.  */
     {
       /* Make sure we respect fullheight and fullwidth.  */
       if (inhibit_horizontal)
-       new_text_width = old_text_width;
+       new_native_width = old_native_width;
       else if (inhibit_vertical)
-       new_text_height = old_text_height;
+       new_native_height = old_native_height;
+
+      if (inhibit == 2 && f->new_width > 0 && f->new_height > 0)
+       /* For implied resizes with inhibit 2 (external menu and tool
+          bar) pick up any new sizes the display engine has not
+          processed yet.  Otherwsie, we would request the old sizes
+          which will make this request appear as a request to set new
+          sizes and have the WM react accordingly which is not TRT.  */
+       {
+         /* But don't that for the external menu bar on Motif.
+            Otherwise, switching off the menu bar will shrink the frame
+            and switching it on will not enlarge it.  */
+#ifdef USE_MOTIF
+         if (!EQ (parameter, Qmenu_bar_lines))
+#endif
+           {
+             new_native_width = f->new_width;
+             new_native_height = f->new_height;
+           }
+       }
 
-      frame_size_history_add
-       (f, Qadjust_frame_size_2, new_text_width, new_text_height,
-        list2 (inhibit_horizontal ? Qt : Qnil,
-               inhibit_vertical ? Qt : Qnil));
+      if (CONSP (frame_size_history))
+       frame_size_history_adjust (f, inhibit, parameter,
+                                  old_text_width, old_text_height,
+                                  new_text_width, new_text_height,
+                                  old_text_cols, old_text_lines,
+                                  new_text_cols, new_text_lines,
+                                  old_native_width, old_native_height,
+                                  new_native_width, new_native_height,
+                                  old_inner_width, old_inner_height,
+                                  new_inner_width, new_inner_height,
+                                  min_inner_width, min_inner_height,
+                                  inhibit_horizontal, inhibit_vertical);
 
       if (FRAME_TERMINAL (f)->set_window_size_hook)
         FRAME_TERMINAL (f)->set_window_size_hook
-         (f, 0, new_text_width, new_text_height, 1);
+         (f, 0, new_native_width, new_native_height);
       f->resized_p = true;
 
       return;
     }
-#endif
+
+  if (CONSP (frame_size_history))
+    frame_size_history_adjust (f, inhibit, parameter,
+                              old_text_width, old_text_height,
+                              new_text_width, new_text_height,
+                              old_text_cols, old_text_lines,
+                              new_text_cols, new_text_lines,
+                              old_native_width, old_native_height,
+                              new_native_width, new_native_height,
+                              old_inner_width, old_inner_height,
+                              new_inner_width, new_inner_height,
+                              min_inner_width, min_inner_height,
+                              inhibit_horizontal, inhibit_vertical);
 
   if ((XWINDOW (FRAME_ROOT_WINDOW (f))->pixel_top
        == FRAME_TOP_MARGIN_HEIGHT (f))
       && new_text_width == old_text_width
       && new_text_height == old_text_height
-      && new_windows_width == old_windows_width
-      && new_windows_height == old_windows_height
-      && new_pixel_width == old_pixel_width
-      && new_pixel_height == old_pixel_height
-      && new_cols == old_cols
-      && new_lines == old_lines)
-    /* No change.  Sanitize window sizes and return.  */
-    {
-      sanitize_window_sizes (Qt);
-      sanitize_window_sizes (Qnil);
-
-      return;
-    }
+      && new_inner_width == old_inner_width
+      && new_inner_height == old_inner_height
+      /* We might be able to drop these but some doubts remain.  */
+      && new_native_width == old_native_width
+      && new_native_height == old_native_height
+      && new_text_cols == old_text_cols
+      && new_text_lines == old_text_lines)
+    /* No change.  */
+    return;
 
   block_input ();
 
@@ -738,69 +805,67 @@ adjust_frame_size (struct frame *f, int new_width, int 
new_height, int inhibit,
      our video hardware.  Try to find the smallest size greater or
      equal to the requested dimensions, while accounting for the fact
      that the menu-bar lines are not counted in the frame height.  */
-  int dos_new_lines = new_lines + FRAME_TOP_MARGIN (f);
-  dos_set_window_size (&dos_new_lines, &new_cols);
-  new_lines = dos_new_lines - FRAME_TOP_MARGIN (f);
+  int dos_new_text_lines = new_text_lines + FRAME_TOP_MARGIN (f);
+
+  dos_set_window_size (&dos_new_text_lines, &new_text_cols);
+  new_text_lines = dos_new_text_lines - FRAME_TOP_MARGIN (f);
 #endif
 
-  if (new_windows_width != old_windows_width)
+  if (new_inner_width != old_inner_width)
     {
-      resize_frame_windows (f, new_windows_width, true);
+      resize_frame_windows (f, new_inner_width, true);
 
       /* MSDOS frames cannot PRETEND, as they change frame size by
         manipulating video hardware.  */
       if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
-       FrameCols (FRAME_TTY (f)) = new_cols;
+       FrameCols (FRAME_TTY (f)) = new_text_cols;
 
 #if defined (HAVE_WINDOW_SYSTEM)
       if (WINDOWP (f->tab_bar_window))
        {
-         XWINDOW (f->tab_bar_window)->pixel_width = new_windows_width;
+         XWINDOW (f->tab_bar_window)->pixel_width = new_inner_width;
          XWINDOW (f->tab_bar_window)->total_cols
-           = new_windows_width / unit_width;
+           = new_inner_width / unit_width;
        }
 #endif
 
 #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
       if (WINDOWP (f->tool_bar_window))
        {
-         XWINDOW (f->tool_bar_window)->pixel_width = new_windows_width;
+         XWINDOW (f->tool_bar_window)->pixel_width = new_inner_width;
          XWINDOW (f->tool_bar_window)->total_cols
-           = new_windows_width / unit_width;
+           = new_inner_width / unit_width;
        }
 #endif
     }
-  else if (new_cols != old_cols)
+  else if (new_text_cols != old_text_cols)
     call2 (Qwindow__pixel_to_total, frame, Qt);
 
-  if (new_windows_height != old_windows_height
+  if (new_inner_height != old_inner_height
       /* When the top margin has changed we have to recalculate the top
         edges of all windows.  No such calculation is necessary for the
         left edges.  */
       || WINDOW_TOP_PIXEL_EDGE (r) != FRAME_TOP_MARGIN_HEIGHT (f))
     {
-      resize_frame_windows (f, new_windows_height, false);
+      resize_frame_windows (f, new_inner_height, false);
 
       /* MSDOS frames cannot PRETEND, as they change frame size by
         manipulating video hardware.  */
       if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f))
-       FrameRows (FRAME_TTY (f)) = new_lines + FRAME_TOP_MARGIN (f);
+       FrameRows (FRAME_TTY (f)) = new_text_lines + FRAME_TOP_MARGIN (f);
     }
-  else if (new_lines != old_lines)
+  else if (new_text_lines != old_text_lines)
     call2 (Qwindow__pixel_to_total, frame, Qnil);
 
-  frame_size_history_add
-    (f, Qadjust_frame_size_3, new_text_width, new_text_height,
-     list4i (old_pixel_width, old_pixel_height,
-            new_pixel_width, new_pixel_height));
-
   /* Assign new sizes.  */
+  FRAME_COLS (f) = new_text_cols;
+  FRAME_LINES (f) = new_text_lines;
   FRAME_TEXT_WIDTH (f) = new_text_width;
   FRAME_TEXT_HEIGHT (f) = new_text_height;
-  FRAME_PIXEL_WIDTH (f) = new_pixel_width;
-  FRAME_PIXEL_HEIGHT (f) = new_pixel_height;
-  SET_FRAME_COLS (f, new_cols);
-  SET_FRAME_LINES (f, new_lines);
+  FRAME_PIXEL_WIDTH (f) = new_native_width;
+  FRAME_PIXEL_HEIGHT (f) = new_native_height;
+  FRAME_TOTAL_COLS (f) = FRAME_PIXEL_WIDTH (f) / FRAME_COLUMN_WIDTH (f);
+  FRAME_TOTAL_LINES (f) = FRAME_PIXEL_HEIGHT (f) / FRAME_LINE_HEIGHT (f);
 
   {
     struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
@@ -814,18 +879,18 @@ adjust_frame_size (struct frame *f, int new_width, int 
new_height, int inhibit,
       w->cursor.vpos = w->cursor.y = 0;
   }
 
-  /* Sanitize window sizes.  */
-  sanitize_window_sizes (Qt);
-  sanitize_window_sizes (Qnil);
-
   adjust_frame_glyphs (f);
   calculate_costs (f);
   SET_FRAME_GARBAGED (f);
+  /* We now say here that F was resized instead of using the old
+     condition below.  Some resizing must have taken place and if it was
+     only shifting the root window's position (paranoia?).  */
+  f->resized_p = true;
 
-  /* A frame was "resized" if one of its pixelsizes changed, even if its
-     X window wasn't resized at all.  */
-  f->resized_p = (new_pixel_width != old_pixel_width
-                 || new_pixel_height != old_pixel_height);
+/**   /\* A frame was "resized" if its native size changed, even if its X **/
+/**      window wasn't resized at all.  *\/ **/
+/**   f->resized_p = (new_native_width != old_native_width **/
+/**              || new_native_height != old_native_height); **/
 
   unblock_input ();
 
@@ -836,8 +901,8 @@ adjust_frame_size (struct frame *f, int new_width, int 
new_height, int inhibit,
 
     FOR_EACH_FRAME (frames, frame1)
       if (FRAME_PARENT_FRAME (XFRAME (frame1)) == f)
-       keep_ratio (XFRAME (frame1), f, old_pixel_width, old_pixel_height,
-                   new_pixel_width, new_pixel_height);
+       keep_ratio (XFRAME (frame1), f, old_native_width, old_native_height,
+                   new_native_width, new_native_height);
   }
 #endif
 }
@@ -886,6 +951,8 @@ make_frame (bool mini_p)
   f->tool_bar_resized = false;
   f->column_width = 1;  /* !FRAME_WINDOW_P value.  */
   f->line_height = 1;  /* !FRAME_WINDOW_P value.  */
+  f->new_width = -1;
+  f->new_height = -1;
 #ifdef HAVE_WINDOW_SYSTEM
   f->vertical_scroll_bar_type = vertical_scroll_bar_none;
   f->horizontal_scroll_bars = false;
@@ -934,17 +1001,14 @@ make_frame (bool mini_p)
 
   wset_frame (rw, frame);
 
-  /* 80/25 is arbitrary,
-     just so that there is "something there."
+  /* 80/25 is arbitrary, just so that there is "something there."
      Correct size will be set up later with adjust_frame_size.  */
+  FRAME_COLS (f) = FRAME_TOTAL_COLS (f) = rw->total_cols = 80;
+  FRAME_TEXT_WIDTH (f) = FRAME_PIXEL_WIDTH (f) = rw->pixel_width
+    = 80 * FRAME_COLUMN_WIDTH (f);
+  FRAME_LINES (f) = FRAME_TOTAL_LINES (f) = 25;
+  FRAME_TEXT_HEIGHT (f) = FRAME_PIXEL_HEIGHT (f) = 25 * FRAME_LINE_HEIGHT (f);
 
-  SET_FRAME_COLS (f, 80);
-  SET_FRAME_LINES (f, 25);
-  SET_FRAME_WIDTH (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f));
-  SET_FRAME_HEIGHT (f, FRAME_LINES (f) * FRAME_LINE_HEIGHT (f));
-
-  rw->total_cols = FRAME_COLS (f);
-  rw->pixel_width = rw->total_cols * FRAME_COLUMN_WIDTH (f);
   rw->total_lines = FRAME_LINES (f) - (mini_p ? 1 : 0);
   rw->pixel_height = rw->total_lines * FRAME_LINE_HEIGHT (f);
 
@@ -1318,8 +1382,8 @@ affects all frames on the same terminal device.  */)
   {
     int width, height;
     get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
-    adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
-                      - FRAME_TAB_BAR_LINES (f),
+    /* With INHIBIT 5 pass correct text height to adjust_frame_size.  */
+    adjust_frame_size (f, width, height - FRAME_TOP_MARGIN (f),
                       5, 0, Qterminal_frame);
   }
 
@@ -3223,21 +3287,23 @@ If FRAME is omitted or nil, return information on the 
currently selected frame.
                                    : FRAME_W32_P (f) ? "w32term"
                                    :"tty"));
     }
+
   store_in_alist (&alist, Qname, f->name);
-  height = (f->new_height
-           ? (f->new_pixelwise
-              ? (f->new_height / FRAME_LINE_HEIGHT (f))
-              : f->new_height)
+  /* It's questionable whether here we should report the value of
+     f->new_height (and f->new_width below) but we've done that in the
+     past, so let's keep it.  Note that a value of -1 for either of
+     these means that no new size was requested.  */
+  height = (f->new_height >= 0
+           ? f->new_height / FRAME_LINE_HEIGHT (f)
            : FRAME_LINES (f));
   store_in_alist (&alist, Qheight, make_fixnum (height));
-  width = (f->new_width
-          ? (f->new_pixelwise
-             ? (f->new_width / FRAME_COLUMN_WIDTH (f))
-             : f->new_width)
-          : FRAME_COLS (f));
+  width = (f->new_width >= 0
+          ? f->new_width / FRAME_COLUMN_WIDTH (f)
+          : FRAME_COLS(f));
   store_in_alist (&alist, Qwidth, make_fixnum (width));
-  store_in_alist (&alist, Qmodeline, (FRAME_WANTS_MODELINE_P (f) ? Qt : Qnil));
-  store_in_alist (&alist, Qunsplittable, (FRAME_NO_SPLIT_P (f) ? Qt : Qnil));
+
+  store_in_alist (&alist, Qmodeline, FRAME_WANTS_MODELINE_P (f) ? Qt : Qnil);
+  store_in_alist (&alist, Qunsplittable, FRAME_NO_SPLIT_P (f) ? Qt : Qnil);
   store_in_alist (&alist, Qbuffer_list, f->buffer_list);
   store_in_alist (&alist, Qburied_buffer_list, f->buried_buffer_list);
 
@@ -3250,6 +3316,7 @@ If FRAME is omitted or nil, return information on the 
currently selected frame.
     {
       /* This ought to be correct in f->param_alist for an X frame.  */
       Lisp_Object lines;
+
       XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f));
       store_in_alist (&alist, Qmenu_bar_lines, lines);
       XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f));
@@ -3596,15 +3663,18 @@ DEFUN ("frame-bottom-divider-width", 
Fbottom_divider_width, Sbottom_divider_widt
 static int
 check_frame_pixels (Lisp_Object size, Lisp_Object pixelwise, int item_size)
 {
+  intmax_t sz;
+  int pixel_size; /* size * item_size */
+
   CHECK_INTEGER (size);
   if (!NILP (pixelwise))
     item_size = 1;
-  intmax_t sz;
-  int pixel_size; /* size * item_size */
-  if (! integer_to_intmax (size, &sz)
+
+  if (!integer_to_intmax (size, &sz)
       || INT_MULTIPLY_WRAPV (sz, item_size, &pixel_size))
     args_out_of_range_3 (size, make_int (INT_MIN / item_size),
                         make_int (INT_MAX / item_size));
+
   return pixel_size;
 }
 
@@ -3627,9 +3697,13 @@ If FRAME is nil, it defaults to the selected frame.  */)
   (Lisp_Object frame, Lisp_Object height, Lisp_Object pretend, Lisp_Object 
pixelwise)
 {
   struct frame *f = decode_live_frame (frame);
-  int pixel_height = check_frame_pixels (height, pixelwise,
-                                        FRAME_LINE_HEIGHT (f));
-  adjust_frame_size (f, -1, pixel_height, 1, !NILP (pretend), Qheight);
+  int text_height
+    = check_frame_pixels (height, pixelwise, FRAME_LINE_HEIGHT (f));
+
+  /* With INHIBIT 1 pass correct text width to adjust_frame_size.  */
+  adjust_frame_size
+    (f, FRAME_TEXT_WIDTH (f), text_height, 1, !NILP (pretend), Qheight);
+
   return Qnil;
 }
 
@@ -3652,9 +3726,13 @@ If FRAME is nil, it defaults to the selected frame.  */)
   (Lisp_Object frame, Lisp_Object width, Lisp_Object pretend, Lisp_Object 
pixelwise)
 {
   struct frame *f = decode_live_frame (frame);
-  int pixel_width = check_frame_pixels (width, pixelwise,
-                                       FRAME_COLUMN_WIDTH (f));
-  adjust_frame_size (f, pixel_width, -1, 1, !NILP (pretend), Qwidth);
+  int text_width
+    = check_frame_pixels (width, pixelwise, FRAME_COLUMN_WIDTH (f));
+
+  /* With INHIBIT 1 pass correct text height to adjust_frame_size.  */
+  adjust_frame_size
+    (f, text_width, FRAME_TEXT_HEIGHT (f), 1, !NILP (pretend), Qwidth);
+
   return Qnil;
 }
 
@@ -3670,11 +3748,14 @@ If FRAME is nil, it defaults to the selected frame.  */)
   (Lisp_Object frame, Lisp_Object width, Lisp_Object height, Lisp_Object 
pixelwise)
 {
   struct frame *f = decode_live_frame (frame);
-  int pixel_width = check_frame_pixels (width, pixelwise,
-                                       FRAME_COLUMN_WIDTH (f));
-  int pixel_height = check_frame_pixels (height, pixelwise,
-                                        FRAME_LINE_HEIGHT (f));
-  adjust_frame_size (f, pixel_width, pixel_height, 1, 0, Qsize);
+  int text_width
+    = check_frame_pixels (width, pixelwise, FRAME_COLUMN_WIDTH (f));
+  int text_height
+    = check_frame_pixels (height, pixelwise, FRAME_LINE_HEIGHT (f));
+
+  /* PRETEND is always false here.  */
+  adjust_frame_size (f, text_width, text_height, 1, false, Qsize);
+
   return Qnil;
 }
 
@@ -4038,11 +4119,9 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
 {
   Lisp_Object tail, frame;
 
-
-  /* If both of these parameters are present, it's more efficient to
-     set them both at once.  So we wait until we've looked at the
-     entire list before we set them.  */
-  int width = -1, height = -1;  /* -1 denotes they were not changed. */
+  /* Neither of these values should be used.  */
+  int width = -1, height = -1;
+  bool width_change = false, height_change = false;
 
   /* Same here.  */
   Lisp_Object left, top;
@@ -4120,6 +4199,8 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
 
       if (EQ (prop, Qwidth))
         {
+         width_change = true;
+
          if (RANGED_FIXNUMP (0, val, INT_MAX))
            width = XFIXNAT (val) * FRAME_COLUMN_WIDTH (f) ;
          else if (CONSP (val) && EQ (XCAR (val), Qtext_pixels)
@@ -4128,9 +4209,13 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
          else if (FLOATP (val))
            width = frame_float (f, val, FRAME_FLOAT_WIDTH, &parent_done,
                                 &outer_done, -1);
+         else
+           width_change = false;
         }
       else if (EQ (prop, Qheight))
         {
+         height_change = true;
+
          if (RANGED_FIXNUMP (0, val, INT_MAX))
            height = XFIXNAT (val) * FRAME_LINE_HEIGHT (f);
          else if (CONSP (val) && EQ (XCAR (val), Qtext_pixels)
@@ -4139,6 +4224,8 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
          else if (FLOATP (val))
            height = frame_float (f, val, FRAME_FLOAT_HEIGHT, &parent_done,
                                 &outer_done, -1);
+         else
+           height_change = false;
         }
       else if (EQ (prop, Qtop))
        top = val;
@@ -4207,23 +4294,28 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
        XSETINT (icon_top, 0);
     }
 
-  /* Don't set these parameters unless they've been explicitly
-     specified.  The window might be mapped or resized while we're in
-     this function, and we don't want to override that unless the lisp
-     code has asked for it.
-
-     Don't set these parameters unless they actually differ from the
-     window's current parameters; the window may not actually exist
-     yet.  */
-  if ((width != -1 && width != FRAME_TEXT_WIDTH (f))
-      || (height != -1 && height != FRAME_TEXT_HEIGHT (f)))
-    /* We could consider checking f->after_make_frame here, but I
-       don't have the faintest idea why the following is needed at
-       all.  With the old setting it can get a Heisenbug when
-       EmacsFrameResize intermittently provokes a delayed
-       change_frame_size in the middle of adjust_frame_size.  */
-    /**        || (f->can_set_window_size && (f->new_height || f->new_width))) 
**/
-    adjust_frame_size (f, width, height, 1, 0, Qx_set_frame_parameters);
+  if (width_change || height_change)
+    {
+      Lisp_Object parameter;
+
+      if (width_change)
+       {
+         if (height_change)
+           parameter = Qsize;
+         else
+           {
+             height = FRAME_TEXT_HEIGHT (f);
+             parameter = Qwidth;
+           }
+       }
+      else
+       {
+         width = FRAME_TEXT_WIDTH (f);
+         parameter = Qheight;
+       }
+
+      adjust_frame_size (f, width, height, 1, 0, parameter);
+    }
 
   if ((!NILP (left) || !NILP (top))
       && ! (left_no_change && top_no_change)
@@ -4296,9 +4388,6 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
     {
       Lisp_Object old_value = get_frame_param (f, Qfullscreen);
 
-      frame_size_history_add
-       (f, Qx_set_fullscreen, 0, 0, list2 (old_value, fullscreen));
-
       store_frame_param (f, Qfullscreen, fullscreen);
       if (!EQ (fullscreen, old_value))
        gui_set_fullscreen (f, fullscreen, old_value);
@@ -5514,25 +5603,16 @@ On Nextstep, this just calls `ns-parse-geometry'.  */)
 
    This function does not make the coordinates positive.  */
 
-#define DEFAULT_ROWS 36
-#define DEFAULT_COLS 80
-
 long
 gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p,
-                        bool toolbar_p, int *x_width, int *x_height)
+                        bool toolbar_p)
 {
   Lisp_Object height, width, user_size, top, left, user_position;
   long window_prompting = 0;
   Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
   int parent_done = -1, outer_done = -1;
-
-  /* Default values if we fall through.
-     Actually, if that happens we should get
-     window manager prompting.  */
-  SET_FRAME_WIDTH (f, DEFAULT_COLS * FRAME_COLUMN_WIDTH (f));
-  SET_FRAME_COLS (f, DEFAULT_COLS);
-  SET_FRAME_HEIGHT (f, DEFAULT_ROWS * FRAME_LINE_HEIGHT (f));
-  SET_FRAME_LINES (f, DEFAULT_ROWS);
+  int text_width = 80 * FRAME_COLUMN_WIDTH (f);
+  int text_height = 36 * FRAME_LINE_HEIGHT (f);
 
   /* Window managers expect that if program-specified
      positions are not (0,0), they're intentional, not defaults.  */
@@ -5547,8 +5627,12 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
   if (tabbar_p && FRAME_TAB_BAR_LINES (f))
     {
       if (frame_default_tab_bar_height)
+       /* A default tab bar height was already set by the display code
+          for some other frame, use that.  */
        FRAME_TAB_BAR_HEIGHT (f) = frame_default_tab_bar_height;
       else
+       /* Calculate the height from various other settings.  For some
+          reason, these are usually off by 2 hence of no use.  */
        {
          int margin, relief;
 
@@ -5601,7 +5685,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
 
   /* Ensure that earlier new_width and new_height settings won't
      override what we specify below.  */
-  f->new_width = f->new_height = 0;
+  f->new_width = f->new_height = -1;
 
   height = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, 
RES_TYPE_NUMBER);
   width = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER);
@@ -5615,9 +5699,8 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
              if ((XFIXNUM (XCDR (width)) < 0 || XFIXNUM (XCDR (width)) > 
INT_MAX))
                xsignal1 (Qargs_out_of_range, XCDR (width));
 
-             SET_FRAME_WIDTH (f, XFIXNUM (XCDR (width)));
+             text_width = XFIXNUM (XCDR (width));
              f->inhibit_horizontal_resize = true;
-             *x_width = XFIXNUM (XCDR (width));
            }
          else if (FLOATP (width))
            {
@@ -5631,7 +5714,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
                                               &parent_done, &outer_done, -1);
 
                  if (new_width > -1)
-                   SET_FRAME_WIDTH (f, new_width);
+                   text_width = new_width;
                }
            }
          else
@@ -5640,7 +5723,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
              if ((XFIXNUM (width) < 0 || XFIXNUM (width) > INT_MAX))
                xsignal1 (Qargs_out_of_range, width);
 
-             SET_FRAME_WIDTH (f, XFIXNUM (width) * FRAME_COLUMN_WIDTH (f));
+             text_width = XFIXNUM (width) * FRAME_COLUMN_WIDTH (f);
            }
        }
 
@@ -5652,9 +5735,8 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
              if ((XFIXNUM (XCDR (height)) < 0 || XFIXNUM (XCDR (height)) > 
INT_MAX))
                xsignal1 (Qargs_out_of_range, XCDR (height));
 
-             SET_FRAME_HEIGHT (f, XFIXNUM (XCDR (height)));
+             text_height = XFIXNUM (XCDR (height));
              f->inhibit_vertical_resize = true;
-             *x_height = XFIXNUM (XCDR (height));
            }
          else if (FLOATP (height))
            {
@@ -5668,7 +5750,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
                                                &parent_done, &outer_done, -1);
 
                  if (new_height > -1)
-                   SET_FRAME_HEIGHT (f, new_height);
+                   text_height = new_height;
                }
            }
          else
@@ -5677,7 +5759,7 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
              if ((XFIXNUM (height) < 0) || (XFIXNUM (height) > INT_MAX))
                xsignal1 (Qargs_out_of_range, height);
 
-             SET_FRAME_HEIGHT (f, XFIXNUM (height) * FRAME_LINE_HEIGHT (f));
+             text_height = XFIXNUM (height) * FRAME_LINE_HEIGHT (f);
            }
        }
 
@@ -5689,6 +5771,9 @@ gui_figure_window_size (struct frame *f, Lisp_Object 
parms, bool tabbar_p,
        window_prompting |= PSize;
     }
 
+  adjust_frame_size (f, text_width, text_height, 5, false,
+                    Qgui_figure_window_size);
+
   top = gui_display_get_arg (dpyinfo, parms, Qtop, 0, 0, RES_TYPE_NUMBER);
   left = gui_display_get_arg (dpyinfo, parms, Qleft, 0, 0, RES_TYPE_NUMBER);
   user_position = gui_display_get_arg (dpyinfo, parms, Quser_position, 0, 0,
@@ -6004,39 +6089,17 @@ syms_of_frame (void)
   DEFSYM (Qtab_bar_size, "tab-bar-size");
   DEFSYM (Qtool_bar_external, "tool-bar-external");
   DEFSYM (Qtool_bar_size, "tool-bar-size");
-  /* The following are used for frame_size_history.  */
-  DEFSYM (Qadjust_frame_size_1, "adjust-frame-size-1");
-  DEFSYM (Qadjust_frame_size_2, "adjust-frame-size-2");
-  DEFSYM (Qadjust_frame_size_3, "adjust-frame-size-3");
-  DEFSYM (Qx_set_frame_parameters, "x-set-frame-parameters");
-  DEFSYM (QEmacsFrameResize, "EmacsFrameResize");
-  DEFSYM (Qset_frame_size, "set-frame-size");
-  DEFSYM (Qframe_inhibit_resize, "frame-inhibit-resize");
-  DEFSYM (Qx_set_fullscreen, "x-set-fullscreen");
-  DEFSYM (Qx_check_fullscreen, "x-check-fullscreen");
-  DEFSYM (Qxg_frame_resized, "xg-frame-resized");
-  DEFSYM (Qxg_frame_set_char_size_1, "xg-frame-set-char-size-1");
-  DEFSYM (Qxg_frame_set_char_size_2, "xg-frame-set-char-size-2");
-  DEFSYM (Qxg_frame_set_char_size_3, "xg-frame-set-char-size-3");
-  DEFSYM (Qxg_frame_set_char_size_4, "xg-frame-set-char-size-4");
-  DEFSYM (Qx_set_window_size_1, "x-set-window-size-1");
-  DEFSYM (Qx_set_window_size_2, "x-set-window-size-2");
-  DEFSYM (Qx_set_window_size_3, "x-set-window-size-3");
-  DEFSYM (Qxg_change_toolbar_position, "xg-change-toolbar-position");
-  DEFSYM (Qx_net_wm_state, "x-net-wm-state");
-  DEFSYM (Qx_handle_net_wm_state, "x-handle-net-wm-state");
-  DEFSYM (Qtb_size_cb, "tb-size-cb");
-  DEFSYM (Qupdate_frame_tab_bar, "update-frame-tab-bar");
-  DEFSYM (Qupdate_frame_tool_bar, "update-frame-tool-bar");
-  DEFSYM (Qfree_frame_tab_bar, "free-frame-tab-bar");
-  DEFSYM (Qfree_frame_tool_bar, "free-frame-tool-bar");
-  DEFSYM (Qx_set_menu_bar_lines, "x-set-menu-bar-lines");
-  DEFSYM (Qchange_frame_size, "change-frame-size");
-  DEFSYM (Qxg_frame_set_char_size, "xg-frame-set-char-size");
-  DEFSYM (Qset_window_configuration, "set-window-configuration");
-  DEFSYM (Qx_create_frame_1, "x-create-frame-1");
-  DEFSYM (Qx_create_frame_2, "x-create-frame-2");
-  DEFSYM (Qterminal_frame, "terminal-frame");
+  /* The following are passed to adjust_frame_size.  */
+  DEFSYM (Qx_set_menu_bar_lines, "x_set_menu_bar_lines");
+  DEFSYM (Qchange_frame_size, "change_frame_size");
+  DEFSYM (Qxg_frame_set_char_size, "xg_frame_set_char_size");
+  DEFSYM (Qx_set_window_size_1, "x_set_window_size_1");
+  DEFSYM (Qset_window_configuration, "set_window_configuration");
+  DEFSYM (Qx_create_frame_1, "x_create_frame_1");
+  DEFSYM (Qx_create_frame_2, "x_create_frame_2");
+  DEFSYM (Qgui_figure_window_size, "gui_figure_window_size");
+  DEFSYM (Qtip_frame, "tip_frame");
+  DEFSYM (Qterminal_frame, "terminal_frame");
 
 #ifdef HAVE_NS
   DEFSYM (Qns_parse_geometry, "ns-parse-geometry");
@@ -6065,9 +6128,7 @@ syms_of_frame (void)
   DEFSYM (Qleft_fringe, "left-fringe");
   DEFSYM (Qline_spacing, "line-spacing");
   DEFSYM (Qmenu_bar_lines, "menu-bar-lines");
-  DEFSYM (Qupdate_frame_menubar, "update-frame-menubar");
-  DEFSYM (Qfree_frame_menubar_1, "free-frame-menubar-1");
-  DEFSYM (Qfree_frame_menubar_2, "free-frame-menubar-2");
+  DEFSYM (Qtab_bar_lines, "tab-bar-lines");
   DEFSYM (Qmouse_color, "mouse-color");
   DEFSYM (Qname, "name");
   DEFSYM (Qright_divider_width, "right-divider-width");
@@ -6079,7 +6140,6 @@ syms_of_frame (void)
   DEFSYM (Qscroll_bar_width, "scroll-bar-width");
   DEFSYM (Qsticky, "sticky");
   DEFSYM (Qtitle, "title");
-  DEFSYM (Qtab_bar_lines, "tab-bar-lines");
   DEFSYM (Qtool_bar_lines, "tool-bar-lines");
   DEFSYM (Qtool_bar_position, "tool-bar-position");
   DEFSYM (Qunsplittable, "unsplittable");
diff --git a/src/frame.h b/src/frame.h
index ab7d3c5..f89151c 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -256,8 +256,8 @@ struct frame
      be used for output.  */
   bool_bf glyphs_initialized_p : 1;
 
-  /* Set to true in change_frame_size when size of frame changed
-     Clear the frame in clear_garbaged_frames if set.  */
+  /* Set to true in adjust_frame_size when one of the frame's sizes
+     changed.  Clear the frame in clear_garbaged_frames if set.  */
   bool_bf resized_p : 1;
 
   /* Set to true if the default face for the frame has been
@@ -415,10 +415,6 @@ struct frame
   bool_bf no_special_glyphs : 1;
 #endif /* HAVE_WINDOW_SYSTEM */
 
-  /* Whether new_height and new_width shall be interpreted
-     in pixels.  */
-  bool_bf new_pixelwise : 1;
-
   /* True means set_window_size_hook requests can be processed for
      this frame.  */
   bool_bf can_set_window_size : 1;
@@ -426,11 +422,23 @@ struct frame
   /* Set to true after this frame was made by `make-frame'.  */
   bool_bf after_make_frame : 1;
 
-  /* Whether the tab bar height change should be taken into account.  */
+  /* Two sticky flags, that are both false when a frame is created.
+     'display_tab_bar' sets the former to true the first time it
+     displays the tab bar.  When the former is true, the next call of
+     'x_change_tab_bar_height' and associates sets the latter true and
+     tries to adjust the frame height in a way that the now valid pixel
+     height of the tab bar is taken into account by the frame's native
+     height.  */
   bool_bf tab_bar_redisplayed : 1;
   bool_bf tab_bar_resized : 1;
 
-  /* Whether the tool bar height change should be taken into account.  */
+  /* Two sticky flags, that are both false when a frame is created.
+     'redisplay_tool_bar' sets the former to true the first time it
+     displays the tool bar.  When the former is true, the next call of
+     'x_change_tool_bar_height' and associates sets the latter true and
+     tries to adjust the frame height in a way that the now valid pixel
+     height of the tool bar is taken into account by the frame's native
+     height.  */
   bool_bf tool_bar_redisplayed : 1;
   bool_bf tool_bar_resized : 1;
 
@@ -461,7 +469,7 @@ struct frame
      last time run_window_change_functions was called on it.  */
   ptrdiff_t number_of_windows;
 
-  /* Number of lines (rounded up) of tab bar.  REMOVE THIS  */
+  /* Number of frame lines (rounded up) of tab bar.  */
   int tab_bar_lines;
 
   /* Height of frame internal tab bar in pixels.  */
@@ -470,7 +478,7 @@ struct frame
   int n_tab_bar_rows;
   int n_tab_bar_items;
 
-  /* Number of lines (rounded up) of tool bar.  REMOVE THIS  */
+  /* Number of frame lines (rounded up) of tool bar.  */
   int tool_bar_lines;
 
   /* Height of frame internal tool bar in pixels.  */
@@ -492,39 +500,24 @@ struct frame
   /* Cost of deleting n lines on this frame.  */
   int *delete_n_lines_cost;
 
-  /* Text width of this frame (excluding fringes, vertical scroll bar
-     and internal border widths) and text height (excluding menu bar,
-     tool bar, horizontal scroll bar and internal border widths) in
-     units of canonical characters.  */
+  /* Text width and height of this frame in (and maybe rounded to) frame
+     columns and lines.  */
   int text_cols, text_lines;
-
-  /* Total width of this frame (including fringes, vertical scroll bar
-     and internal border widths) and total height (including menu bar,
-     tool bar, horizontal scroll bar and internal border widths) in
-     units of canonical characters.  */
-  int total_cols, total_lines;
-
-  /* Text width of this frame (excluding fringes, vertical scroll bar
-     and internal border widths) and text height (excluding menu bar,
-     tool bar, horizontal scroll bar and internal border widths) in
-     pixels.  */
+  /* Text width and height of this frame in pixels.  */
   int text_width, text_height;
 
-  /* New text height and width for pending size change.  0 if no change
-     pending.  These values represent pixels or canonical character units
-     according to the value of new_pixelwise and correlate to the
-     text width/height of the frame.  */
+  /* Native width of this frame in (and maybe rounded to) frame columns
+     and lines.  */
+  int total_cols, total_lines;
+  /* Native width and height of this frame in pixels.  */
+  int pixel_width, pixel_height;
+  /* New native width and height of this frame for pending size change,
+     in pixels.  -1 if no change pending.  */
   int new_width, new_height;
 
   /* Pixel position of the frame window (x and y offsets in root window).  */
   int left_pos, top_pos;
 
-  /* Total width of this frame (including fringes, vertical scroll bar
-     and internal border widths) and total height (including internal
-     menu and tool bars, horizontal scroll bar and internal border
-     widths) in pixels.  */
-  int pixel_width, pixel_height;
-
   /* This is the gravity value for the specified window position.  */
   int win_gravity;
 
@@ -854,7 +847,6 @@ default_pixels_per_inch_y (void)
 
 /* FRAME_WINDOW_P tests whether the frame is a graphical window system
    frame.  */
-
 #ifdef HAVE_X_WINDOWS
 #define FRAME_WINDOW_P(f) FRAME_X_P (f)
 #endif
@@ -923,45 +915,36 @@ default_pixels_per_inch_y (void)
 # define FRAME_SCALE_FACTOR(f) 1
 #endif
 
-/* Pixel width of frame F.  */
+/* Native width and height of frame F, in pixels and frame
+   columns/lines.  */
 #define FRAME_PIXEL_WIDTH(f) ((f)->pixel_width)
-
-/* Pixel height of frame F.  */
 #define FRAME_PIXEL_HEIGHT(f) ((f)->pixel_height)
+#define FRAME_TOTAL_COLS(f) ((f)->total_cols)
+#define FRAME_TOTAL_LINES(f) ((f)->total_lines)
 
-/* Width of frame F, measured in canonical character columns,
-   not including scroll bars if any.  */
-#define FRAME_COLS(f) (f)->text_cols
-
-/* Height of frame F, measured in canonical lines, including
-   non-toolkit menu bar and non-toolkit tool bar lines.  */
-#define FRAME_LINES(f) (f)->text_lines
-
-/* Width of frame F, measured in pixels not including the width for
-   fringes, scroll bar, and internal borders.  */
+/* Text width and height of frame F, in pixels and frame
+   columns/lines.  */
 #define FRAME_TEXT_WIDTH(f) (f)->text_width
-
-/* Height of frame F, measured in pixels not including the height
-   for scroll bar and internal borders.  */
 #define FRAME_TEXT_HEIGHT(f) (f)->text_height
+#define FRAME_COLS(f) ((f)->text_cols)
+#define FRAME_LINES(f) ((f)->text_lines)
 
-/* Number of lines of frame F used for menu bar.
-   This is relevant on terminal frames and on
-   X Windows when not using the X toolkit.
-   These lines are counted in FRAME_LINES.  */
-#define FRAME_MENU_BAR_LINES(f) (f)->menu_bar_lines
+/* True if this frame should display an external menu bar.  */
+#ifdef HAVE_EXT_MENU_BAR
+#define FRAME_EXTERNAL_MENU_BAR(f) (f)->external_menu_bar
+#else
+#define FRAME_EXTERNAL_MENU_BAR(f) false
+#endif
 
-/* Pixel height of frame F's menu bar.  */
+/* Size of frame F's internal menu bar in frame lines and pixels.  */
+#define FRAME_MENU_BAR_LINES(f) (f)->menu_bar_lines
 #define FRAME_MENU_BAR_HEIGHT(f) (f)->menu_bar_height
 
-/* Number of lines of frame F used for the tab-bar.  */
+/* Size of frame F's tab bar in frame lines and pixels.  */
 #define FRAME_TAB_BAR_LINES(f) (f)->tab_bar_lines
-
-/* Pixel height of frame F's tab-bar.  */
 #define FRAME_TAB_BAR_HEIGHT(f) (f)->tab_bar_height
 
-/* True if this frame should display a tool bar
-   in a way that does not use any text lines.  */
+/* True if this frame should display an external tool bar.  */
 #ifdef HAVE_EXT_TOOL_BAR
 #define FRAME_EXTERNAL_TOOL_BAR(f) (f)->external_tool_bar
 #else
@@ -975,27 +958,21 @@ default_pixels_per_inch_y (void)
 #define FRAME_TOOL_BAR_POSITION(f) ((void) (f), Qtop)
 #endif
 
-/* Number of lines of frame F used for the tool-bar.  */
+/* Size of frame F's internal tool bar in frame lines and pixels.  */
 #define FRAME_TOOL_BAR_LINES(f) (f)->tool_bar_lines
-
-/* Pixel height of frame F's tool-bar.  */
 #define FRAME_TOOL_BAR_HEIGHT(f) (f)->tool_bar_height
 
-/* Lines above the top-most window in frame F.  */
-#define FRAME_TOP_MARGIN(F) \
-  (FRAME_MENU_BAR_LINES (F) + FRAME_TAB_BAR_LINES (F) + FRAME_TOOL_BAR_LINES 
(F))
+/* Height of frame F's top margin in frame lines.  */
+#define FRAME_TOP_MARGIN(F)                    \
+  (FRAME_MENU_BAR_LINES (F)                    \
+   + FRAME_TAB_BAR_LINES (F)                   \
+   + FRAME_TOOL_BAR_LINES (F))
 
 /* Pixel height of frame F's top margin.  */
-#define FRAME_TOP_MARGIN_HEIGHT(F)                             \
-  (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TAB_BAR_HEIGHT (F) + 
FRAME_TOOL_BAR_HEIGHT (F))
-
-/* True if this frame should display a menu bar
-   in a way that does not use any text lines.  */
-#ifdef HAVE_EXT_MENU_BAR
-#define FRAME_EXTERNAL_MENU_BAR(f) (f)->external_menu_bar
-#else
-#define FRAME_EXTERNAL_MENU_BAR(f) false
-#endif
+#define FRAME_TOP_MARGIN_HEIGHT(F)             \
+  (FRAME_MENU_BAR_HEIGHT (F)                   \
+   + FRAME_TAB_BAR_HEIGHT (F)                  \
+   + FRAME_TOOL_BAR_HEIGHT (F))
 
 /* True if frame F is currently visible.  */
 #define FRAME_VISIBLE_P(f) (f)->visible
@@ -1192,48 +1169,6 @@ default_pixels_per_inch_y (void)
    ? FRAME_CONFIG_SCROLL_BAR_LINES (f) \
    : 0)
 
-/* Total width of frame F, in columns (characters),
-   including the width used by scroll bars if any.  */
-#define FRAME_TOTAL_COLS(f) ((f)->total_cols)
-
-/* Total height of frame F, in lines (characters),
-   including the height used by scroll bars if any.  */
-#define FRAME_TOTAL_LINES(f) ((f)->total_lines)
-
-/* Set the character widths of frame F.  WIDTH specifies a nominal
-   character text width.  */
-#define SET_FRAME_COLS(f, width)                                       \
-  ((f)->text_cols = (width),                                           \
-   (f)->total_cols = ((width)                                          \
-                     + FRAME_SCROLL_BAR_COLS (f)                       \
-                     + FRAME_FRINGE_COLS (f)))
-
-/* Set the character heights of frame F.  HEIGHT specifies a nominal
-   character text height.  */
-#define SET_FRAME_LINES(f, height)                                     \
-  ((f)->text_lines = (height),                                         \
-   (f)->total_lines = ((height)                                                
\
-                      + FRAME_TOP_MARGIN (f)                           \
-                      + FRAME_SCROLL_BAR_LINES (f)))
-
-/* Set the widths of frame F.  WIDTH specifies a nominal pixel text
-   width.  */
-#define SET_FRAME_WIDTH(f, width)                                      \
-  ((f)->text_width = (width),                                          \
-   (f)->pixel_width = ((width)                                         \
-                      + FRAME_SCROLL_BAR_AREA_WIDTH (f)                \
-                      + FRAME_TOTAL_FRINGE_WIDTH (f)                   \
-                      + 2 * FRAME_INTERNAL_BORDER_WIDTH (f)))
-
-/* Set the heights of frame F.  HEIGHT specifies a nominal pixel text
-   height.  */
-#define SET_FRAME_HEIGHT(f, height)                                    \
-  ((f)->text_height = (height),                                                
\
-   (f)->pixel_height = ((height)                                       \
-                       + FRAME_TOP_MARGIN_HEIGHT (f)                   \
-                       + FRAME_SCROLL_BAR_AREA_HEIGHT (f)              \
-                       + 2 * FRAME_INTERNAL_BORDER_WIDTH (f)))
-
 /* Maximum + 1 legitimate value for FRAME_CURSOR_X.  */
 #define FRAME_CURSOR_X_LIMIT(f) \
   (FRAME_COLS (f) + FRAME_LEFT_SCROLL_BAR_COLS (f))
@@ -1254,7 +1189,6 @@ default_pixels_per_inch_y (void)
 #define FRAME_BACKGROUND_PIXEL(f) ((f)->background_pixel)
 
 /* Return a pointer to the face cache of frame F.  */
-
 #define FRAME_FACE_CACHE(F)    (F)->face_cache
 
 /* Return the size of message_buf of the frame F.  We multiply the
@@ -1280,15 +1214,13 @@ default_pixels_per_inch_y (void)
    This macro is a holdover from a time when multiple frames weren't always
    supported.  An alternate definition of the macro would expand to
    something which executes the statement once.  */
-
-#define FOR_EACH_FRAME(list_var, frame_var)    \
-  for ((list_var) = Vframe_list;               \
-       (CONSP (list_var)                       \
+#define FOR_EACH_FRAME(list_var, frame_var)     \
+  for ((list_var) = Vframe_list;                \
+       (CONSP (list_var)                        \
        && (frame_var = XCAR (list_var), true)); \
        list_var = XCDR (list_var))
 
 /* Reflect mouse movement when a complete frame update is performed.  */
-
 #define FRAME_MOUSE_UPDATE(frame)                              \
   do {                                                         \
     Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (frame);              \
@@ -1303,8 +1235,7 @@ default_pixels_per_inch_y (void)
   } while (false)
 
 /* Handy macro to construct an argument to Fmodify_frame_parameters.  */
-
-#define AUTO_FRAME_ARG(name, parameter, value) \
+#define AUTO_FRAME_ARG(name, parameter, value)         \
   AUTO_LIST1 (name, AUTO_CONS_EXPR (parameter, value))
 
 /* False means there are no visible garbaged frames.  */
@@ -1314,7 +1245,6 @@ extern bool frame_garbaged;
    We call redisplay_other_windows to make sure the frame gets redisplayed
    if some changes were applied to it while it wasn't visible (and hence
    wasn't redisplayed).  */
-
 INLINE void
 SET_FRAME_VISIBLE (struct frame *f, int v)
 {
@@ -1329,9 +1259,8 @@ SET_FRAME_VISIBLE (struct frame *f, int v)
   f->visible = v;
 }
 
-/* Set iconify of frame F.  */
-
-#define SET_FRAME_ICONIFIED(f, i)                      \
+/* Set iconified status of frame F.  */
+#define SET_FRAME_ICONIFIED(f, i)                              \
   (f)->iconified = (eassert (0 <= (i) && (i) <= 1), (i))
 
 extern Lisp_Object selected_frame;
@@ -1378,11 +1307,14 @@ extern void frame_make_pointer_invisible (struct frame 
*);
 extern void frame_make_pointer_visible (struct frame *);
 extern Lisp_Object delete_frame (Lisp_Object, Lisp_Object);
 extern bool frame_inhibit_resize (struct frame *, bool, Lisp_Object);
-extern void adjust_frame_size (struct frame *, int, int, int, bool, 
Lisp_Object);
-extern void frame_size_history_add (struct frame *f, Lisp_Object fun_symbol,
-                                   int width, int height, Lisp_Object rest);
+extern void adjust_frame_size (struct frame *, int, int, int, bool,
+                              Lisp_Object);
 extern Lisp_Object mouse_position (bool);
-
+extern int frame_windows_min_size (Lisp_Object, Lisp_Object, Lisp_Object,
+                                  Lisp_Object);
+extern void frame_size_history_plain (struct frame *, Lisp_Object);
+extern void frame_size_history_extra (struct frame *, Lisp_Object,
+                                     int, int, int, int, int, int);
 extern Lisp_Object Vframe_list;
 
 /* Value is a pointer to the selected frame.  If the selected frame
@@ -1661,12 +1593,11 @@ IMAGE_OPT_FROM_ID (struct frame *f, int id)
    - FRAME_SCROLL_BAR_AREA_HEIGHT (f)                  \
    - 2 * FRAME_INTERNAL_BORDER_WIDTH (f))
 
-/* Return the width/height reserved for the windows of frame F.  */
-#define FRAME_WINDOWS_WIDTH(f)                 \
+#define FRAME_INNER_WIDTH(f)                   \
   (FRAME_PIXEL_WIDTH (f)                       \
    - 2 * FRAME_INTERNAL_BORDER_WIDTH (f))
 
-#define FRAME_WINDOWS_HEIGHT(f)                        \
+#define FRAME_INNER_HEIGHT(f)                  \
   (FRAME_PIXEL_HEIGHT (f)                      \
    - FRAME_TOP_MARGIN_HEIGHT (f)               \
    - 2 * FRAME_INTERNAL_BORDER_WIDTH (f))
@@ -1710,7 +1641,7 @@ extern void gui_set_horizontal_scroll_bars (struct frame 
*, Lisp_Object, Lisp_Ob
 extern void gui_set_scroll_bar_width (struct frame *, Lisp_Object, 
Lisp_Object);
 extern void gui_set_scroll_bar_height (struct frame *, Lisp_Object, 
Lisp_Object);
 
-extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, bool, 
int *, int *);
+extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, bool);
 
 extern void gui_set_alpha (struct frame *, Lisp_Object, Lisp_Object);
 extern void gui_set_no_special_glyphs (struct frame *, Lisp_Object, 
Lisp_Object);
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 16487b0..897eade 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -998,68 +998,50 @@ xg_set_geometry (struct frame *f)
     }
 }
 
-/* Function to handle resize of our frame.  As we have a Gtk+ tool bar
-   and a Gtk+ menu bar, we get resize events for the edit part of the
-   frame only.  We let Gtk+ deal with the Gtk+ parts.
-   F is the frame to resize.
-   PIXELWIDTH, PIXELHEIGHT is the new size in pixels.  */
-
+/** Function to handle resize of native frame F to WIDTH and HEIGHT
+    pixels after we got a ConfigureNotify event.  */
 void
-xg_frame_resized (struct frame *f, int pixelwidth, int pixelheight)
+xg_frame_resized (struct frame *f, int width, int height)
 {
-  int width, height;
-
-  if (pixelwidth == -1 && pixelheight == -1)
-    {
-      if (FRAME_GTK_WIDGET (f) && gtk_widget_get_mapped (FRAME_GTK_WIDGET (f)))
-       gdk_window_get_geometry (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
-                                0, 0, &pixelwidth, &pixelheight);
-      else
-       return;
-    }
-
-  width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
-  height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
-  PGTK_TRACE ("xg_frame_resized: pixel: %dx%d, text: %dx%d", pixelwidth, 
pixelheight, width, height);
-
-  frame_size_history_add
-    (f, Qxg_frame_resized, width, height, Qnil);
-
-  PGTK_TRACE ("width: %d -> %d.", FRAME_TEXT_WIDTH (f), width);
-  PGTK_TRACE ("height: %d -> %d.", FRAME_TEXT_HEIGHT (f), height);
-  PGTK_TRACE ("pixelwidth: %d -> %d.", FRAME_PIXEL_WIDTH (f), pixelwidth);
-  PGTK_TRACE ("pixelheight: %d -> %d.", FRAME_PIXEL_HEIGHT (f), pixelheight);
+  /* Ignore case where size of native rectangle didn't change.  */
+  if (width != FRAME_PIXEL_WIDTH (f) || height != FRAME_PIXEL_HEIGHT (f)
+      || (delayed_size_change
+         && (width != f->new_width || height != f->new_height)))
+    {
+      if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("xg_frame_resized, changed"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          delayed_size_change ? f->new_width : -1,
+          delayed_size_change ? f->new_height : -1);
 
-  if (width != FRAME_TEXT_WIDTH (f)
-      || height != FRAME_TEXT_HEIGHT (f)
-      || pixelwidth != FRAME_PIXEL_WIDTH (f)
-      || pixelheight != FRAME_PIXEL_HEIGHT (f))
-    {
       FRAME_RIF (f)->clear_under_internal_border (f);
-      change_frame_size (f, width, height, 0, 1, 0, 1);
+      change_frame_size (f, width, height, false, true, false);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
     }
+  else if (CONSP (frame_size_history))
+    frame_size_history_extra
+      (f, build_string ("xg_frame_resized, unchanged"),
+       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+       delayed_size_change ? f->new_width : -1,
+       delayed_size_change ? f->new_height : -1);
+
 }
 
 /** Resize the outer window of frame F.  WIDTH and HEIGHT are the new
-    pixel sizes of F's text area.  */
+    native pixel sizes of F.  */
 void
 xg_frame_set_char_size (struct frame *f, int width, int height)
 {
-  int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
-  int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
   Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
   gint gwidth, gheight;
-  int totalheight
-    = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
-  int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f);
+  int outer_height
+    = height + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
+  int outer_width = width + FRAME_TOOLBAR_WIDTH (f);
   bool was_visible = false;
   bool hide_child_frame;
 
-  if (FRAME_PIXEL_HEIGHT (f) == 0)
-    return;
-
 #ifndef HAVE_PGTK
   gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                       &gwidth, &gheight);
@@ -1078,8 +1060,8 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
   /* Do this before resize, as we don't know yet if we will be resized.  */
   FRAME_RIF (f)->clear_under_internal_border (f);
 
-  totalheight /= xg_get_scale (f);
-  totalwidth /= xg_get_scale (f);
+  outer_height /= xg_get_scale (f);
+  outer_width /= xg_get_scale (f);
 
   x_wm_set_size_hint (f, 0, 0);
 
@@ -1092,61 +1074,45 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
      manager will abolish it.  At least the respective size should
      remain unchanged but giving the frame back its normal size will
      be broken ... */
-  if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
-    {
-      frame_size_history_add
-       (f, Qxg_frame_set_char_size_1, width, height,
-        list2i (gheight, totalheight));
-
+  if (EQ (fullscreen, Qfullwidth) && width == FRAME_PIXEL_WIDTH (f))
 #ifndef HAVE_PGTK
-      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        gwidth, totalheight);
+    gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      gwidth, outer_height);
 #else
-      if (FRAME_GTK_OUTER_WIDGET (f))
-       {
-         gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            gwidth, totalheight);
-       }
-      else
-       {
-         gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                      gwidth, totalheight);
-       }
+    if (FRAME_GTK_OUTER_WIDGET (f))
+      {
+       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                          gwidth, outer_height);
+      }
+    else
+      {
+       gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+                                    gwidth, outer_height);
+      }
 #endif
-    }
-  else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
-    {
-      frame_size_history_add
-       (f, Qxg_frame_set_char_size_2, width, height,
-        list2i (gwidth, totalwidth));
-
+  else if (EQ (fullscreen, Qfullheight) && height == FRAME_PIXEL_HEIGHT (f))
 #ifndef HAVE_PGTK
-      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        totalwidth, gheight);
+    gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      outer_width, gheight);
 #else
-      if (FRAME_GTK_OUTER_WIDGET (f))
-       {
-         gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            totalwidth, gheight);
-       }
-      else
-       {
-         gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                      totalwidth, gheight);
-       }
+    if (FRAME_GTK_OUTER_WIDGET (f))
+      {
+       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                          outer_width, gheight);
+      }
+    else
+      {
+       gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+                                    outer_width, gheight);
+      }
 #endif
-    }
   else if (FRAME_PARENT_FRAME (f) && FRAME_VISIBLE_P (f))
     {
       was_visible = true;
       hide_child_frame = EQ (x_gtk_resize_child_frames, Qhide);
 
-      if (totalwidth != gwidth || totalheight != gheight)
+      if (outer_width != gwidth || outer_height != gheight)
        {
-         frame_size_history_add
-           (f, Qxg_frame_set_char_size_4, width, height,
-            list2i (totalwidth, totalheight));
-
           if (hide_child_frame)
             {
               block_input ();
@@ -1160,14 +1126,14 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
 
 #ifndef HAVE_PGTK
          gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            totalwidth, totalheight);
+                            outer_width, outer_height);
 #else
          if (FRAME_GTK_OUTER_WIDGET (f)) {
            gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                              totalwidth, totalheight);
+                              outer_width, outer_height);
          } else {
            gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                        totalwidth, totalheight);
+                                        outer_width, outer_height);
          }
 #endif
 
@@ -1187,19 +1153,16 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
     }
   else
     {
-      frame_size_history_add
-       (f, Qxg_frame_set_char_size_3, width, height,
-        list2i (totalwidth, totalheight));
 #ifndef HAVE_PGTK
       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        totalwidth, totalheight);
+                        outer_width, outer_height);
 #else
       if (FRAME_GTK_OUTER_WIDGET (f)) {
        gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                          totalwidth, totalheight);
+                          outer_width, outer_height);
       } else {
        gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                    totalwidth, totalheight);
+                                    outer_width, outer_height);
       }
 #endif
       fullscreen = Qnil;
@@ -1224,6 +1187,12 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
       x_wait_for_event (f, ConfigureNotify);
 #endif
 
+      if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("xg_frame_set_char_size, visible"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          f->new_width, f->new_height);
+
       if (!NILP (fullscreen))
        /* Try to restore fullscreen state.  */
        {
@@ -1232,8 +1201,17 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
        }
     }
   else
-    adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
+    {
+      if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("xg_frame_set_char_size, invisible"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          f->new_width, f->new_height);
 
+      adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+                        FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
+                        5, 0, Qxg_frame_set_char_size);
+    }
 }
 
 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
@@ -1372,7 +1350,8 @@ style_changed_cb (GObject *go,
             {
               FRAME_TERMINAL (f)->set_scroll_bar_default_width_hook (f);
               FRAME_TERMINAL (f)->set_scroll_bar_default_height_hook (f);
-              xg_frame_set_char_size (f, FRAME_TEXT_WIDTH (f), 
FRAME_TEXT_HEIGHT (f));
+              xg_frame_set_char_size (f, FRAME_PIXEL_WIDTH (f),
+                                     FRAME_PIXEL_HEIGHT (f));
             }
         }
     }
@@ -5040,10 +5019,7 @@ tb_size_cb (GtkWidget    *widget,
   struct frame *f = user_data;
 
   if (xg_update_tool_bar_sizes (f))
-    {
-      frame_size_history_add (f, Qtb_size_cb, 0, 0, Qnil);
-      adjust_frame_size (f, -1, -1, 5, 0, Qtool_bar_lines);
-    }
+    adjust_frame_size (f, -1, -1, 2, false, Qtool_bar_lines);
 }
 
 /* Create a tool bar for frame F.  */
@@ -5675,23 +5651,10 @@ update_frame_tool_bar (struct frame *f)
         xg_pack_tool_bar (f, FRAME_TOOL_BAR_POSITION (f));
       gtk_widget_show_all (x->toolbar_widget);
       if (xg_update_tool_bar_sizes (f))
-       {
-         int inhibit
-           = ((f->after_make_frame
-               && !f->tool_bar_resized
-               && (EQ (frame_inhibit_implied_resize, Qt)
-                   || (CONSP (frame_inhibit_implied_resize)
-                       && !NILP (Fmemq (Qtool_bar_lines,
-                                        frame_inhibit_implied_resize))))
-               /* This will probably fail to DTRT in the
-                  fullheight/-width cases.  */
-               && NILP (get_frame_param (f, Qfullscreen)))
-              ? 0
-              : 2);
-
-         frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
-         adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
-       }
+       /* It's not entirely clear whether here we want a treatment
+          similar to that for frames with internal tool bar.  */
+       adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
+
       f->tool_bar_resized = f->tool_bar_redisplayed;
     }
 
@@ -5740,7 +5703,6 @@ free_frame_tool_bar (struct frame *f)
                              NULL);
         }
 
-      frame_size_history_add (f, Qfree_frame_tool_bar, 0, 0, Qnil);
       adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
 
       unblock_input ();
@@ -5772,11 +5734,7 @@ xg_change_toolbar_position (struct frame *f, Lisp_Object 
pos)
   g_object_unref (top_widget);
 
   if (xg_update_tool_bar_sizes (f))
-    {
-      frame_size_history_add (f, Qxg_change_toolbar_position, 0, 0, Qnil);
-      adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
-    }
-
+    adjust_frame_size (f, -1, -1, 2, 0, Qtool_bar_lines);
 
   unblock_input ();
 }
diff --git a/src/image.c b/src/image.c
index 9608406..a78674f 100644
--- a/src/image.c
+++ b/src/image.c
@@ -628,7 +628,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object 
file)
 
   /* Search bitmap-file-path for the file, if appropriate.  */
   if (openp (Vx_bitmap_file_path, file, Qnil, &found,
-            make_fixnum (R_OK), false)
+            make_fixnum (R_OK), false, false)
       < 0)
     return -1;
 
@@ -3275,7 +3275,7 @@ image_find_image_fd (Lisp_Object file, int *pfd)
 
   /* Try to find FILE in data-directory/images, then x-bitmap-file-path.  */
   fd = openp (search_path, file, Qnil, &file_found,
-             pfd ? Qt : make_fixnum (R_OK), false);
+             pfd ? Qt : make_fixnum (R_OK), false, false);
   if (fd >= 0 || fd == -2)
     {
       file_found = ENCODE_FILE (file_found);
diff --git a/src/keyboard.c b/src/keyboard.c
index fe5815b..c3fce19 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -10387,7 +10387,7 @@ update_recent_keys (int new_size, int kept_keys)
 }
 
 DEFUN ("lossage-size", Flossage_size, Slossage_size, 0, 1,
-       "(list (read-number \"new-size: \" (lossage-size)))",
+       "(list (read-number \"Set maximum keystrokes to: \" (lossage-size)))",
        doc: /* Return or set the maximum number of keystrokes to save.
 If called with a non-nil ARG, set the limit to ARG and return it.
 Otherwise, return the current limit.
@@ -10673,10 +10673,7 @@ On such systems, Emacs starts a subshell instead of 
suspending.  */)
      with a window system; but suspend should be disabled in that case.  */
   get_tty_size (fileno (CURTTY ()->input), &width, &height);
   if (width != old_width || height != old_height)
-    change_frame_size (SELECTED_FRAME (), width,
-                      height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ())
-                      - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()),
-                      0, 0, 0, 0);
+    change_frame_size (SELECTED_FRAME (), width, height, false, false, false);
 
   run_hook (intern ("suspend-resume-hook"));
 
diff --git a/src/lisp.h b/src/lisp.h
index c67c8b0..f83c55f 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -294,12 +294,12 @@ DEFINE_GDB_SYMBOL_END (VALMASK)
 
 /* Lisp_Word is a scalar word suitable for holding a tagged pointer or
    integer.  Usually it is a pointer to a deliberately-incomplete type
-   'union Lisp_X'.  However, it is EMACS_INT when Lisp_Objects and
+   'struct Lisp_X'.  However, it is EMACS_INT when Lisp_Objects and
    pointers differ in width.  */
 
 #define LISP_WORDS_ARE_POINTERS (EMACS_INT_MAX == INTPTR_MAX)
 #if LISP_WORDS_ARE_POINTERS
-typedef union Lisp_X *Lisp_Word;
+typedef struct Lisp_X *Lisp_Word;
 #else
 typedef EMACS_INT Lisp_Word;
 #endif
@@ -563,6 +563,7 @@ enum Lisp_Fwd_Type
 
 #ifdef CHECK_LISP_OBJECT_TYPE
 typedef struct Lisp_Object { Lisp_Word i; } Lisp_Object;
+# define LISP_OBJECT_IS_STRUCT
 # define LISP_INITIALLY(w) {w}
 # undef CHECK_LISP_OBJECT_TYPE
 enum CHECK_LISP_OBJECT_TYPE { CHECK_LISP_OBJECT_TYPE = true };
@@ -1068,6 +1069,7 @@ enum pvec_type
   PVEC_MUTEX,
   PVEC_CONDVAR,
   PVEC_MODULE_FUNCTION,
+  PVEC_NATIVE_COMP_UNIT,
 
   /* These should be last, for internal_equal and sxhash_obj.  */
   PVEC_COMPILED,
@@ -1313,6 +1315,7 @@ dead_object (void)
 #define XSETTHREAD(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_THREAD))
 #define XSETMUTEX(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_MUTEX))
 #define XSETCONDVAR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CONDVAR))
+#define XSETNATIVE_COMP_UNIT(a, b) (XSETPSEUDOVECTOR (a, b, 
PVEC_NATIVE_COMP_UNIT))
 
 /* Efficiently convert a pointer to a Lisp object and back.  The
    pointer is represented as a fixnum, so the garbage collector
@@ -2036,6 +2039,8 @@ CHAR_TABLE_SET (Lisp_Object ct, int idx, Lisp_Object val)
     char_table_set (ct, idx, val);
 }
 
+#include "comp.h"
+
 /* This structure describes a built-in function.
    It is generated by the DEFUN macro only.
    defsubr makes it into a Lisp object.  */
@@ -2058,8 +2063,15 @@ struct Lisp_Subr
     } function;
     short min_args, max_args;
     const char *symbol_name;
-    const char *intspec;
+    union {
+      const char *intspec;
+      Lisp_Object native_intspec;
+    };
     EMACS_INT doc;
+    Lisp_Object native_comp_u[NATIVE_COMP_FLAG];
+    char *native_c_name[NATIVE_COMP_FLAG];
+    Lisp_Object lambda_list[NATIVE_COMP_FLAG];
+    Lisp_Object type[NATIVE_COMP_FLAG];
   } GCALIGNED_STRUCT;
 union Aligned_Lisp_Subr
   {
@@ -2972,6 +2984,12 @@ CHECK_INTEGER (Lisp_Object x)
 {
   CHECK_TYPE (INTEGERP (x), Qnumberp, x);
 }
+
+INLINE void
+CHECK_SUBR (Lisp_Object x)
+{
+  CHECK_TYPE (SUBRP (x), Qsubrp, x);
+}
 
 
 /* If we're not dumping using the legacy dumper and we might be using
@@ -3019,7 +3037,7 @@ CHECK_INTEGER (Lisp_Object x)
   static union Aligned_Lisp_Subr sname =                                \
      {{{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS },                                
\
        { .a ## maxargs = fnname },                                     \
-       minargs, maxargs, lname, intspec, 0}};                          \
+       minargs, maxargs, lname, {intspec}, 0}};                                
\
    Lisp_Object fnname
 
 /* defsubr (Sname);
@@ -4066,10 +4084,11 @@ LOADHIST_ATTACH (Lisp_Object x)
   if (initialized)
     Vcurrent_load_list = Fcons (x, Vcurrent_load_list);
 }
+extern bool suffix_p (Lisp_Object, const char *);
 extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object,
                                         Lisp_Object, Lisp_Object);
 extern int openp (Lisp_Object, Lisp_Object, Lisp_Object,
-                  Lisp_Object *, Lisp_Object, bool);
+                  Lisp_Object *, Lisp_Object, bool, bool);
 enum { S2N_IGNORE_TRAILING = 1 };
 extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *);
 extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object),
@@ -4140,6 +4159,9 @@ extern Lisp_Object internal_lisp_condition_case 
(Lisp_Object, Lisp_Object, Lisp_
 extern Lisp_Object internal_condition_case (Lisp_Object (*) (void), 
Lisp_Object, Lisp_Object (*) (Lisp_Object));
 extern Lisp_Object internal_condition_case_1 (Lisp_Object (*) (Lisp_Object), 
Lisp_Object, Lisp_Object, Lisp_Object (*) (Lisp_Object));
 extern Lisp_Object internal_condition_case_2 (Lisp_Object (*) (Lisp_Object, 
Lisp_Object), Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object (*) 
(Lisp_Object));
+extern Lisp_Object internal_condition_case_3 (Lisp_Object (*) (Lisp_Object, 
Lisp_Object, Lisp_Object), Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, 
Lisp_Object (*) (Lisp_Object));
+extern Lisp_Object internal_condition_case_4 (Lisp_Object (*) (Lisp_Object, 
Lisp_Object, Lisp_Object, Lisp_Object), Lisp_Object, Lisp_Object, Lisp_Object, 
Lisp_Object, Lisp_Object, Lisp_Object (*) (Lisp_Object));
+extern Lisp_Object internal_condition_case_5 (Lisp_Object (*) (Lisp_Object, 
Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object), Lisp_Object, Lisp_Object, 
Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object (*) 
(Lisp_Object));
 extern Lisp_Object internal_condition_case_n
     (Lisp_Object (*) (ptrdiff_t, Lisp_Object *), ptrdiff_t, Lisp_Object *,
      Lisp_Object, Lisp_Object (*) (Lisp_Object, ptrdiff_t, Lisp_Object *));
@@ -4705,7 +4727,11 @@ extern void syms_of_lcms2 (void);
 #endif
 
 #ifdef HAVE_ZLIB
+
+#include <stdio.h>
+
 /* Defined in decompress.c.  */
+extern int md5_gz_stream (FILE *, void *);
 extern void syms_of_decompress (void);
 #endif
 
@@ -4727,6 +4753,46 @@ extern void syms_of_profiler (void);
 extern char *emacs_root_dir (void);
 #endif /* DOS_NT */
 
+#ifdef HAVE_NATIVE_COMP
+INLINE bool
+SUBR_NATIVE_COMPILEDP (Lisp_Object a)
+{
+  return SUBRP (a) && !NILP (XSUBR (a)->native_comp_u[0]);
+}
+
+INLINE bool
+SUBR_NATIVE_COMPILED_DYNP (Lisp_Object a)
+{
+  return SUBR_NATIVE_COMPILEDP (a) && !NILP (XSUBR (a)->lambda_list[0]);
+}
+
+INLINE Lisp_Object
+SUBR_TYPE (Lisp_Object a)
+{
+  return XSUBR (a)->type[0];
+}
+
+INLINE struct Lisp_Native_Comp_Unit *
+allocate_native_comp_unit (void)
+{
+  return ALLOCATE_ZEROED_PSEUDOVECTOR (struct Lisp_Native_Comp_Unit,
+                                      data_impure_vec, PVEC_NATIVE_COMP_UNIT);
+}
+#else
+INLINE bool
+SUBR_NATIVE_COMPILEDP (Lisp_Object a)
+{
+  return false;
+}
+
+INLINE bool
+SUBR_NATIVE_COMPILED_DYNP (Lisp_Object a)
+{
+  return false;
+}
+
+#endif
+
 /* Defined in lastfile.c.  */
 extern char my_edata[];
 extern char my_endbss[];
diff --git a/src/lread.c b/src/lread.c
index dea1b23..e53e1f6 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1119,7 +1119,7 @@ This uses the variables `load-suffixes' and 
`load-file-rep-suffixes'.  */)
 }
 
 /* Return true if STRING ends with SUFFIX.  */
-static bool
+bool
 suffix_p (Lisp_Object string, const char *suffix)
 {
   ptrdiff_t suffix_len = strlen (suffix);
@@ -1138,6 +1138,24 @@ close_infile_unwind (void *arg)
   infile = prev_infile;
 }
 
+/* Compute the filename we want in `load-history' and `load-file-name'.  */
+
+static Lisp_Object
+compute_found_effective (Lisp_Object found)
+{
+  /* Reconstruct the .elc filename.  */
+  Lisp_Object src_name =
+    Fgethash (Ffile_name_nondirectory (found), Vcomp_eln_to_el_h, Qnil);
+
+  if (NILP (src_name))
+    /* Manual eln load.  */
+    return found;
+
+  if (suffix_p (src_name, "el.gz"))
+    src_name = Fsubstring (src_name, make_fixnum (0), make_fixnum (-3));
+  return concat2 (src_name, build_string ("c"));
+}
+
 DEFUN ("load", Fload, Sload, 1, 5, 0,
        doc: /* Execute a file of Lisp code named FILE.
 First try FILE with `.elc' appended, then try with `.el', then try
@@ -1222,6 +1240,8 @@ Return t if the file exists and loads successfully.  */)
   else
     file = Fsubstitute_in_file_name (file);
 
+  bool no_native = suffix_p (file, ".elc");
+
   /* Avoid weird lossage with null string as arg,
      since it would try to load a directory as a Lisp file.  */
   if (SCHARS (file) == 0)
@@ -1245,7 +1265,7 @@ Return t if the file exists and loads successfully.  */)
               || suffix_p (file, MODULES_SECONDARY_SUFFIX)
 #endif
 #endif
-             )
+              || (NATIVE_COMP_FLAG && suffix_p (file, NATIVE_ELISP_SUFFIX)))
            must_suffix = Qnil;
          /* Don't insist on adding a suffix
             if the argument includes a directory name.  */
@@ -1262,7 +1282,9 @@ Return t if the file exists and loads successfully.  */)
            suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
        }
 
-      fd = openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer);
+      fd =
+       openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer,
+              no_native);
     }
 
   if (fd == -1)
@@ -1323,6 +1345,9 @@ Return t if the file exists and loads successfully.  */)
   bool is_module = false;
 #endif
 
+  bool is_native_elisp =
+    NATIVE_COMP_FLAG && suffix_p (found, NATIVE_ELISP_SUFFIX) ? true : false;
+
   /* Check if we're stuck in a recursive load cycle.
 
      2000-09-21: It's not possible to just check for the file loaded
@@ -1349,11 +1374,15 @@ Return t if the file exists and loads successfully.  */)
      Vload_source_file_function.  */
   specbind (Qlexical_binding, Qnil);
 
-  /* Get the name for load-history.  */
+  Lisp_Object found_eff =
+    is_native_elisp
+    ? compute_found_effective (found)
+    : found;
+
   hist_file_name = (! NILP (Vpurify_flag)
                     ? concat2 (Ffile_name_directory (file),
-                               Ffile_name_nondirectory (found))
-                    : found) ;
+                               Ffile_name_nondirectory (found_eff))
+                    : found_eff);
 
   version = -1;
 
@@ -1417,7 +1446,7 @@ Return t if the file exists and loads successfully.  */)
             } /* !load_prefer_newer */
        }
     }
-  else if (!is_module)
+  else if (!is_module && !is_native_elisp)
     {
       /* We are loading a source file (*.el).  */
       if (!NILP (Vload_source_file_function))
@@ -1444,7 +1473,7 @@ Return t if the file exists and loads successfully.  */)
       stream = NULL;
       errno = EINVAL;
     }
-  else if (!is_module)
+  else if (!is_module && !is_native_elisp)
     {
 #ifdef WINDOWSNT
       emacs_close (fd);
@@ -1460,7 +1489,7 @@ Return t if the file exists and loads successfully.  */)
      might be accessed by the unbind_to call below.  */
   struct infile input;
 
-  if (is_module)
+  if (is_module || is_native_elisp)
     {
       /* `module-load' uses the file name, so we can close the stream
          now.  */
@@ -1487,6 +1516,8 @@ Return t if the file exists and loads successfully.  */)
     {
       if (is_module)
         message_with_string ("Loading %s (module)...", file, 1);
+      else if (is_native_elisp)
+        message_with_string ("Loading %s (native compiled elisp)...", file, 1);
       else if (!compiled)
        message_with_string ("Loading %s (source)...", file, 1);
       else if (newer)
@@ -1496,7 +1527,8 @@ Return t if the file exists and loads successfully.  */)
        message_with_string ("Loading %s...", file, 1);
     }
 
-  specbind (Qload_file_name, found);
+  specbind (Qload_file_name, found_eff);
+  specbind (Qload_true_file_name, found);
   specbind (Qinhibit_file_name_operation, Qnil);
   specbind (Qload_in_progress, Qt);
 
@@ -1512,6 +1544,19 @@ Return t if the file exists and loads successfully.  */)
       emacs_abort ();
 #endif
     }
+  else if (is_native_elisp)
+    {
+#ifdef HAVE_NATIVE_COMP
+      specbind (Qcurrent_load_list, Qnil);
+      LOADHIST_ATTACH (hist_file_name);
+      Fnative_elisp_load (found, Qnil);
+      build_load_history (hist_file_name, true);
+#else
+      /* This cannot happen.  */
+      emacs_abort ();
+#endif
+
+    }
   else
     {
       if (lisp_file_lexically_bound_p (Qget_file_char))
@@ -1547,6 +1592,8 @@ Return t if the file exists and loads successfully.  */)
     {
       if (is_module)
         message_with_string ("Loading %s (module)...done", file, 1);
+      else if (is_native_elisp)
+       message_with_string ("Loading %s (native compiled elisp)...done", file, 
1);
       else if (!compiled)
        message_with_string ("Loading %s (source)...done", file, 1);
       else if (newer)
@@ -1592,12 +1639,108 @@ directories, make sure the PREDICATE function returns 
`dir-ok' for them.  */)
   (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object 
predicate)
 {
   Lisp_Object file;
-  int fd = openp (path, filename, suffixes, &file, predicate, false);
+  int fd = openp (path, filename, suffixes, &file, predicate, false, false);
   if (NILP (predicate) && fd >= 0)
     emacs_close (fd);
   return file;
 }
 
+#ifdef HAVE_NATIVE_COMP
+static bool
+maybe_swap_for_eln1 (Lisp_Object src_name, Lisp_Object eln_name,
+                    Lisp_Object *filename, int *fd, struct timespec mtime)
+{
+  struct stat eln_st;
+  int eln_fd = emacs_open (SSDATA (ENCODE_FILE (eln_name)), O_RDONLY, 0);
+
+  if (eln_fd > 0)
+    {
+      if (fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
+       emacs_close (eln_fd);
+      else
+       {
+         struct timespec eln_mtime = get_stat_mtime (&eln_st);
+         if (timespec_cmp (eln_mtime, mtime) >= 0)
+           {
+             emacs_close (*fd);
+             *fd = eln_fd;
+             *filename = eln_name;
+             /* Store the eln -> el relation.  */
+             Fputhash (Ffile_name_nondirectory (eln_name),
+                       src_name, Vcomp_eln_to_el_h);
+             return true;
+           }
+         else
+           emacs_close (eln_fd);
+       }
+    }
+
+  return false;
+}
+#endif
+
+/* Look for a suitable .eln file to be loaded in place of FILENAME.
+   If found replace the content of FILENAME and FD. */
+
+static void
+maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd,
+                   struct timespec mtime)
+{
+#ifdef HAVE_NATIVE_COMP
+
+  if (no_native
+      || load_no_native)
+    Fputhash (*filename, Qt, V_comp_no_native_file_h);
+  else
+    Fremhash (*filename, V_comp_no_native_file_h);
+
+  if (no_native
+      || load_no_native
+      || !suffix_p (*filename, ".elc"))
+    return;
+
+  /* Search eln in the eln-cache directories.  */
+  Lisp_Object eln_path_tail = Vcomp_eln_load_path;
+  Lisp_Object src_name =
+    Fsubstring (*filename, Qnil, make_fixnum (-1));
+  if (NILP (Ffile_exists_p (src_name)))
+    {
+      src_name = concat2 (src_name, build_string (".gz"));
+      if (NILP (Ffile_exists_p (src_name)))
+       {
+         if (!NILP (find_symbol_value (Qcomp_warning_on_missing_source)))
+           call2 (intern_c_string ("display-warning"),
+                  Qcomp,
+                  CALLN (Fformat,
+                         build_string ("Cannot look-up eln file as no source "
+                                       "file was found for %s"),
+                         *filename));
+         return;
+       }
+    }
+  Lisp_Object eln_rel_name = Fcomp_el_to_eln_rel_filename (src_name);
+
+  Lisp_Object dir = Qnil;
+  FOR_EACH_TAIL_SAFE (eln_path_tail)
+    {
+      dir = XCAR (eln_path_tail);
+      Lisp_Object eln_name =
+       Fexpand_file_name (eln_rel_name,
+                          Fexpand_file_name (Vcomp_native_version_dir, dir));
+      if (maybe_swap_for_eln1 (src_name, eln_name, filename, fd, mtime))
+       return;
+    }
+
+  /* Look also in preloaded subfolder of the last entry in
+     `comp-eln-load-path'.  */
+  dir = Fexpand_file_name (build_string ("preloaded"),
+                          Fexpand_file_name (Vcomp_native_version_dir,
+                                             dir));
+  maybe_swap_for_eln1 (src_name, Fexpand_file_name (eln_rel_name, dir),
+                      filename, fd, mtime);
+#endif
+}
+
 /* Search for a file whose name is STR, looking in directories
    in the Lisp list PATH, and trying suffixes from SUFFIX.
    On success, return a file descriptor (or 1 or -2 as described below).
@@ -1622,11 +1765,14 @@ directories, make sure the PREDICATE function returns 
`dir-ok' for them.  */)
 
    If NEWER is true, try all SUFFIXes and return the result for the
    newest file that exists.  Does not apply to remote files,
-   or if a non-nil and non-t PREDICATE is specified.  */
+   or if a non-nil and non-t PREDICATE is specified.
+
+   if NO_NATIVE is true do not try to load native code.  */
 
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
-       Lisp_Object *storeptr, Lisp_Object predicate, bool newer)
+       Lisp_Object *storeptr, Lisp_Object predicate, bool newer,
+       bool no_native)
 {
   ptrdiff_t fn_size = 100;
   char buf[100];
@@ -1836,6 +1982,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
                  }
                else
                  {
+                   maybe_swap_for_eln (no_native, &string, &fd,
+                                       get_stat_mtime (&st));
                    /* We succeeded; return this descriptor and filename.  */
                    if (storeptr)
                      *storeptr = string;
@@ -1847,6 +1995,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object 
suffixes,
            /* No more suffixes.  Return the newest.  */
            if (0 <= save_fd && ! CONSP (XCDR (tail)))
              {
+               maybe_swap_for_eln (no_native, &save_string, &save_fd,
+                                   save_mtime);
                if (storeptr)
                  *storeptr = save_string;
                SAFE_FREE ();
@@ -1942,8 +2092,8 @@ readevalloop_1 (int old)
 static AVOID
 end_of_file_error (void)
 {
-  if (STRINGP (Vload_file_name))
-    xsignal1 (Qend_of_file, Vload_file_name);
+  if (STRINGP (Vload_true_file_name))
+    xsignal1 (Qend_of_file, Vload_true_file_name);
 
   xsignal0 (Qend_of_file);
 }
@@ -4204,10 +4354,14 @@ intern_c_string_1 (const char *str, ptrdiff_t len)
 
   if (!SYMBOLP (tem))
     {
-      /* Creating a non-pure string from a string literal not implemented yet.
-        We could just use make_string here and live with the extra copy.  */
-      eassert (!NILP (Vpurify_flag));
-      tem = intern_driver (make_pure_c_string (str, len), obarray, tem);
+      Lisp_Object string;
+
+      if (NILP (Vpurify_flag))
+       string = make_string (str, len);
+      else
+       string = make_pure_c_string (str, len);
+
+      tem = intern_driver (string, obarray, tem);
     }
   return tem;
 }
@@ -4467,6 +4621,10 @@ defsubr (union Aligned_Lisp_Subr *aname)
   XSETPVECTYPE (sname, PVEC_SUBR);
   XSETSUBR (tem, sname);
   set_symbol_function (sym, tem);
+#ifdef HAVE_NATIVE_COMP
+  eassert (NILP (Vcomp_abi_hash));
+  Vcomp_subr_list = Fpurecopy (Fcons (tem, Vcomp_subr_list));
+#endif
 }
 
 #ifdef NOTDEF /* Use fset in subr.el now!  */
@@ -4767,6 +4925,7 @@ init_lread (void)
 
   load_in_progress = 0;
   Vload_file_name = Qnil;
+  Vload_true_file_name = Qnil;
   Vstandard_input = Qt;
   Vloads_in_progress = Qnil;
 }
@@ -4891,20 +5050,15 @@ This list includes suffixes for both compiled and 
source Emacs Lisp files.
 This list should not include the empty string.
 `load' and related functions try to append these suffixes, in order,
 to the specified file name if a suffix is allowed or required.  */);
+  Vload_suffixes = list2 (build_pure_c_string (".elc"),
+                         build_pure_c_string (".el"));
 #ifdef HAVE_MODULES
+  Vload_suffixes = Fcons (build_pure_c_string (MODULES_SUFFIX), 
Vload_suffixes);
 #ifdef MODULES_SECONDARY_SUFFIX
-  Vload_suffixes = list4 (build_pure_c_string (".elc"),
-                         build_pure_c_string (".el"),
-                         build_pure_c_string (MODULES_SUFFIX),
-                          build_pure_c_string (MODULES_SECONDARY_SUFFIX));
-#else
-  Vload_suffixes = list3 (build_pure_c_string (".elc"),
-                         build_pure_c_string (".el"),
-                         build_pure_c_string (MODULES_SUFFIX));
+  Vload_suffixes =
+    Fcons (build_pure_c_string (MODULES_SECONDARY_SUFFIX), Vload_suffixes);
 #endif
-#else
-  Vload_suffixes = list2 (build_pure_c_string (".elc"),
-                         build_pure_c_string (".el"));
+
 #endif
   DEFVAR_LISP ("module-file-suffix", Vmodule_file_suffix,
               doc: /* Suffix of loadable module file, or nil if modules are 
not supported.  */);
@@ -4971,9 +5125,17 @@ directory.  These file names are converted to absolute 
at startup.  */);
   Vload_history = Qnil;
 
   DEFVAR_LISP ("load-file-name", Vload_file_name,
-              doc: /* Full name of file being loaded by `load'.  */);
+              doc: /* Full name of file being loaded by `load'.
+
+In case of native code being loaded this is indicating the
+corresponding bytecode filename.  Use `load-true-file-name' to obtain
+the .eln filename.  */);
   Vload_file_name = Qnil;
 
+  DEFVAR_LISP ("load-true-file-name", Vload_true_file_name,
+              doc: /* Full name of file being loaded by `load'.  */);
+  Vload_true_file_name = Qnil;
+
   DEFVAR_LISP ("user-init-file", Vuser_init_file,
               doc: /* File name, including directory, of user's initialization 
file.
 If the file loaded had extension `.elc', and the corresponding source file
@@ -5093,6 +5255,10 @@ Note that if you customize this, obviously it will not 
affect files
 that are loaded before your customizations are read!  */);
   load_prefer_newer = 0;
 
+  DEFVAR_BOOL ("load-no-native", load_no_native,
+               doc: /* Non-nil means not to load a .eln file when a .elc was 
requested.  */);
+  load_no_native = false;
+
   /* Vsource_directory was initialized in init_lread.  */
 
   DEFSYM (Qcurrent_load_list, "current-load-list");
@@ -5115,6 +5281,7 @@ that are loaded before your customizations are read!  */);
   DEFSYM (Qfunction, "function");
   DEFSYM (Qload, "load");
   DEFSYM (Qload_file_name, "load-file-name");
+  DEFSYM (Qload_true_file_name, "load-true-file-name");
   DEFSYM (Qeval_buffer_list, "eval-buffer-list");
   DEFSYM (Qdir_ok, "dir-ok");
   DEFSYM (Qdo_after_load_evaluation, "do-after-load-evaluation");
diff --git a/src/nsfns.m b/src/nsfns.m
index 5c4cc91..054777a 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -669,21 +669,9 @@ ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, 
Lisp_Object oldval)
     }
 
   {
-    int inhibit
-      = ((f->after_make_frame
-         && !f->tool_bar_resized
-         && (EQ (frame_inhibit_implied_resize, Qt)
-             || (CONSP (frame_inhibit_implied_resize)
-                 && !NILP (Fmemq (Qtool_bar_lines,
-                                  frame_inhibit_implied_resize))))
-         && NILP (get_frame_param (f, Qfullscreen)))
-        ? 0
-        : 2);
-
     NSTRACE_MSG ("inhibit:%d", inhibit);
 
-    frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
-    adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
+    adjust_frame_size (f, -1, -1, 2, false, Qtool_bar_lines);
   }
 }
 
@@ -1332,8 +1320,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
                          RES_TYPE_STRING);
 
   parms = get_geometry_from_preferences (dpyinfo, parms);
-  window_prompting = gui_figure_window_size (f, parms, false, true,
-                                             &x_width, &x_height);
+  window_prompting = gui_figure_window_size (f, parms, false, true);
 
   tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
                              RES_TYPE_BOOLEAN);
@@ -1400,13 +1387,8 @@ DEFUN ("x-create-frame", Fx_create_frame, 
Sx_create_frame,
   /* Allow set_window_size_hook, now.  */
   f->can_set_window_size = true;
 
-  if (x_width > 0)
-    SET_FRAME_WIDTH (f, x_width);
-  if (x_height > 0)
-    SET_FRAME_HEIGHT (f, x_height);
-
-  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 0, 1,
-                    Qx_create_frame_2);
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qx_create_frame_2);
 
   if (! f->output_data.ns->explicit_parent)
     {
diff --git a/src/nsterm.m b/src/nsterm.m
index a650110..b135e35 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1876,10 +1876,9 @@ static void
 ns_set_window_size (struct frame *f,
                     bool change_gravity,
                     int width,
-                    int height,
-                    bool pixelwise)
+                    int height)
 /* --------------------------------------------------------------------------
-     Adjust window pixel size based on given character grid size
+     Adjust window pixel size based on native sizes WIDTH and HEIGHT.
      Impl is a bit more complex than other terms, need to do some
      internal clipping.
    -------------------------------------------------------------------------- 
*/
@@ -1887,7 +1886,6 @@ ns_set_window_size (struct frame *f,
   EmacsView *view = FRAME_NS_VIEW (f);
   NSWindow *window = [view window];
   NSRect wr = [window frame];
-  int pixelwidth, pixelheight;
   int orig_height = wr.size.height;
 
   NSTRACE ("ns_set_window_size");
@@ -1896,24 +1894,13 @@ ns_set_window_size (struct frame *f,
     return;
 
   NSTRACE_RECT ("current", wr);
-  NSTRACE_MSG ("Width:%d Height:%d Pixelwise:%d", width, height, pixelwise);
+  NSTRACE_MSG ("Width:%d Height:%d", width, height);
   NSTRACE_MSG ("Font %d x %d", FRAME_COLUMN_WIDTH (f), FRAME_LINE_HEIGHT (f));
 
   block_input ();
 
-  if (pixelwise)
-    {
-      pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
-      pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
-    }
-  else
-    {
-      pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
-      pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
-    }
-
-  wr.size.width = pixelwidth + f->border_width;
-  wr.size.height = pixelheight;
+  wr.size.width = width + f->border_width;
+  wr.size.height = height;
   if (! [view isFullscreen])
     wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f)
       + FRAME_TOOLBAR_HEIGHT (f);
@@ -1926,21 +1913,10 @@ ns_set_window_size (struct frame *f,
  else
    wr.origin.y += orig_height - wr.size.height;
 
- frame_size_history_add
-   (f, Qx_set_window_size_1, width, height,
-    list5 (Fcons (make_fixnum (pixelwidth), make_fixnum (pixelheight)),
-          Fcons (make_fixnum (wr.size.width), make_fixnum (wr.size.height)),
-          make_fixnum (f->border_width),
-          make_fixnum (FRAME_NS_TITLEBAR_HEIGHT (f)),
-          make_fixnum (FRAME_TOOLBAR_HEIGHT (f))));
-
  /* Usually it seems safe to delay changing the frame size, but when a
     series of actions are taken with no redisplay between them then we
     can end up using old values so don't delay here.  */
- change_frame_size (f,
-                    FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth),
-                    FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight),
-                    0, NO, 0, 1);
+ change_frame_size (f, width, height, false, NO, false);
 
   [window setFrame:wr display:NO];
 
@@ -7359,10 +7335,7 @@ not_in_argv (NSString *arg)
      changes size, as Emacs may already know about the change.
      Unfortunately there doesn't seem to be a bullet-proof method of
      determining whether we need to call it or not.  */
-  change_frame_size (emacsframe,
-                     FRAME_PIXEL_TO_TEXT_WIDTH (emacsframe, neww),
-                     FRAME_PIXEL_TO_TEXT_HEIGHT (emacsframe, newh),
-                     0, YES, 0, 1);
+  change_frame_size (emacsframe, neww, newh, false, YES, false);
 
   SET_FRAME_GARBAGED (emacsframe);
   cancel_mouse_face (emacsframe);
diff --git a/src/pdumper.c b/src/pdumper.c
index d32a147..dfc7388 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -121,6 +121,9 @@ static const char dump_magic[16] = {
 static pdumper_hook dump_hooks[24];
 static int nr_dump_hooks = 0;
 
+static pdumper_hook dump_late_hooks[24];
+static int nr_dump_late_hooks = 0;
+
 static struct
 {
   void *mem;
@@ -175,6 +178,8 @@ enum dump_reloc_type
     /* dump_ptr = dump_ptr + dump_base  */
     RELOC_DUMP_TO_DUMP_PTR_RAW,
     /* dump_mpz = [rebuild bignum]  */
+    RELOC_NATIVE_COMP_UNIT,
+    RELOC_NATIVE_SUBR,
     RELOC_BIGNUM,
     /* dump_lv = make_lisp_ptr (dump_lv + dump_base,
                                type - RELOC_DUMP_TO_DUMP_LV)
@@ -317,6 +322,20 @@ dump_fingerprint (char const *label,
   fprintf (stderr, "%s: %.*s\n", label, hexbuf_size, hexbuf);
 }
 
+/* To be used if some order in the relocation process has to be enforced. */
+enum reloc_phase
+  {
+    /* First to run.  Place every relocation with no dependency here.  */
+    EARLY_RELOCS,
+    /* Late and very late relocs are relocated at the very last after
+       all hooks has been run.  All lisp machinery is at disposal
+       (memory allocation allowed too).  */
+    LATE_RELOCS,
+    VERY_LATE_RELOCS,
+    /* Fake, must be last.  */
+    RELOC_NUM_PHASES
+  };
+
 /* Format of an Emacs dump file.  All offsets are relative to
    the beginning of the file.  An Emacs dump file is coupled
    to exactly the Emacs binary that produced it, so details of
@@ -344,7 +363,7 @@ struct dump_header
 
   /* Relocation table for the dump file; each entry is a
      struct dump_reloc.  */
-  struct dump_table_locator dump_relocs;
+  struct dump_table_locator dump_relocs[RELOC_NUM_PHASES];
 
   /* "Relocation" table we abuse to hold information about the
      location and type of each lisp object in the dump.  We need for
@@ -425,6 +444,7 @@ enum cold_op
     COLD_OP_CHARSET,
     COLD_OP_BUFFER,
     COLD_OP_BIGNUM,
+    COLD_OP_NATIVE_SUBR,
   };
 
 /* This structure controls what operations we perform inside
@@ -524,7 +544,7 @@ struct dump_context
   Lisp_Object cold_queue;
 
   /* Relocations in the dump.  */
-  Lisp_Object dump_relocs;
+  Lisp_Object dump_relocs[RELOC_NUM_PHASES];
 
   /* Object starts.  */
   Lisp_Object object_starts;
@@ -919,7 +939,7 @@ dump_note_reachable (struct dump_context *ctx, Lisp_Object 
object)
 static void *
 dump_object_emacs_ptr (Lisp_Object lv)
 {
-  if (SUBRP (lv))
+  if (SUBRP (lv) && !SUBR_NATIVE_COMPILEDP (lv))
     return XSUBR (lv);
   if (dump_builtin_symbol_p (lv))
     return XSYMBOL (lv);
@@ -1405,7 +1425,7 @@ dump_reloc_dump_to_emacs_ptr_raw (struct dump_context 
*ctx,
                                   dump_off dump_offset)
 {
   if (ctx->flags.dump_object_contents)
-    dump_push (&ctx->dump_relocs,
+    dump_push (&ctx->dump_relocs[EARLY_RELOCS],
                list2 (make_fixnum (RELOC_DUMP_TO_EMACS_PTR_RAW),
                       dump_off_to_lisp (dump_offset)));
 }
@@ -1438,7 +1458,7 @@ dump_reloc_dump_to_dump_lv (struct dump_context *ctx,
       emacs_abort ();
     }
 
-  dump_push (&ctx->dump_relocs,
+  dump_push (&ctx->dump_relocs[EARLY_RELOCS],
              list2 (make_fixnum (reloc_type),
                     dump_off_to_lisp (dump_offset)));
 }
@@ -1454,7 +1474,7 @@ dump_reloc_dump_to_dump_ptr_raw (struct dump_context *ctx,
                                  dump_off dump_offset)
 {
   if (ctx->flags.dump_object_contents)
-    dump_push (&ctx->dump_relocs,
+    dump_push (&ctx->dump_relocs[EARLY_RELOCS],
                list2 (make_fixnum (RELOC_DUMP_TO_DUMP_PTR_RAW),
                       dump_off_to_lisp (dump_offset)));
 }
@@ -1487,7 +1507,7 @@ dump_reloc_dump_to_emacs_lv (struct dump_context *ctx,
       emacs_abort ();
     }
 
-  dump_push (&ctx->dump_relocs,
+  dump_push (&ctx->dump_relocs[EARLY_RELOCS],
              list2 (make_fixnum (reloc_type),
                     dump_off_to_lisp (dump_offset)));
 }
@@ -2200,7 +2220,7 @@ dump_bignum (struct dump_context *ctx, Lisp_Object object)
          Lisp_Bignum instead of the actual mpz field so that the
          relocation offset is aligned.  The relocation-application
          code knows to actually advance past the header.  */
-      dump_push (&ctx->dump_relocs,
+      dump_push (&ctx->dump_relocs[EARLY_RELOCS],
                  list2 (make_fixnum (RELOC_BIGNUM),
                         dump_off_to_lisp (bignum_offset)));
     }
@@ -2840,20 +2860,73 @@ dump_bool_vector (struct dump_context *ctx, const 
struct Lisp_Vector *v)
 static dump_off
 dump_subr (struct dump_context *ctx, const struct Lisp_Subr *subr)
 {
-#if CHECK_STRUCTS && !defined (HASH_Lisp_Subr_594AB72B54)
+#if CHECK_STRUCTS && !defined (HASH_Lisp_Subr_AA236F7759)
 # error "Lisp_Subr changed. See CHECK_STRUCTS comment in config.h."
 #endif
   struct Lisp_Subr out;
   dump_object_start (ctx, &out, sizeof (out));
   DUMP_FIELD_COPY (&out, subr, header.size);
-  dump_field_emacs_ptr (ctx, &out, subr, &subr->function.a0);
+  if (NATIVE_COMP_FLAG && !NILP (subr->native_comp_u[0]))
+    out.function.a0 = NULL;
+  else
+    dump_field_emacs_ptr (ctx, &out, subr, &subr->function.a0);
   DUMP_FIELD_COPY (&out, subr, min_args);
   DUMP_FIELD_COPY (&out, subr, max_args);
-  dump_field_emacs_ptr (ctx, &out, subr, &subr->symbol_name);
-  dump_field_emacs_ptr (ctx, &out, subr, &subr->intspec);
+  if (NATIVE_COMP_FLAG && !NILP (subr->native_comp_u[0]))
+    {
+      dump_field_fixup_later (ctx, &out, subr, &subr->symbol_name);
+      dump_remember_cold_op (ctx,
+                             COLD_OP_NATIVE_SUBR,
+                            make_lisp_ptr ((void *) subr, Lisp_Vectorlike));
+      dump_field_lv (ctx, &out, subr, &subr->native_intspec, WEIGHT_NORMAL);
+    }
+  else
+    {
+      dump_field_emacs_ptr (ctx, &out, subr, &subr->symbol_name);
+      dump_field_emacs_ptr (ctx, &out, subr, &subr->intspec);
+    }
   DUMP_FIELD_COPY (&out, subr, doc);
-  return dump_object_finish (ctx, &out, sizeof (out));
+  if (NATIVE_COMP_FLAG)
+    {
+      dump_field_lv (ctx, &out, subr, &subr->native_comp_u[0], WEIGHT_NORMAL);
+      if (!NILP (subr->native_comp_u[0]))
+       dump_field_fixup_later (ctx, &out, subr, &subr->native_c_name[0]);
+
+      dump_field_lv (ctx, &out, subr, &subr->lambda_list[0], WEIGHT_NORMAL);
+      dump_field_lv (ctx, &out, subr, &subr->type[0], WEIGHT_NORMAL);
+    }
+  dump_off subr_off = dump_object_finish (ctx, &out, sizeof (out));
+  if (NATIVE_COMP_FLAG
+      && ctx->flags.dump_object_contents
+      && !NILP (subr->native_comp_u[0]))
+    /* We'll do the final addr relocation during VERY_LATE_RELOCS time
+       after the compilation units has been loaded. */
+    dump_push (&ctx->dump_relocs[VERY_LATE_RELOCS],
+              list2 (make_fixnum (RELOC_NATIVE_SUBR),
+                     dump_off_to_lisp (subr_off)));
+  return subr_off;
+}
+
+#ifdef HAVE_NATIVE_COMP
+static dump_off
+dump_native_comp_unit (struct dump_context *ctx,
+                      struct Lisp_Native_Comp_Unit *comp_u)
+{
+  /* Have function documentation always lazy loaded to optimize load-time.  */
+  comp_u->data_fdoc_v = Qnil;
+  START_DUMP_PVEC (ctx, &comp_u->header, struct Lisp_Native_Comp_Unit, out);
+  dump_pseudovector_lisp_fields (ctx, &out->header, &comp_u->header);
+  out->handle = NULL;
+
+  dump_off comp_u_off = finish_dump_pvec (ctx, &out->header);
+  if (ctx->flags.dump_object_contents)
+    /* We'll do the real elf load during LATE_RELOCS relocation time. */
+    dump_push (&ctx->dump_relocs[LATE_RELOCS],
+              list2 (make_fixnum (RELOC_NATIVE_COMP_UNIT),
+                     dump_off_to_lisp (comp_u_off)));
+  return comp_u_off;
 }
+#endif
 
 static void
 fill_pseudovec (union vectorlike_header *header, Lisp_Object item)
@@ -2879,7 +2952,7 @@ dump_vectorlike (struct dump_context *ctx,
                  Lisp_Object lv,
                  dump_off offset)
 {
-#if CHECK_STRUCTS && !defined HASH_pvec_type_A4A6E9984D
+#if CHECK_STRUCTS && !defined HASH_pvec_type_F5BA506141
 # error "pvec_type changed. See CHECK_STRUCTS comment in config.h."
 #endif
   const struct Lisp_Vector *v = XVECTOR (lv);
@@ -2932,6 +3005,11 @@ dump_vectorlike (struct dump_context *ctx,
     case PVEC_BIGNUM:
       offset = dump_bignum (ctx, lv);
       break;
+#ifdef HAVE_NATIVE_COMP
+    case PVEC_NATIVE_COMP_UNIT:
+      offset = dump_native_comp_unit (ctx, XNATIVE_COMP_UNIT (lv));
+      break;
+#endif
     case PVEC_WINDOW_CONFIGURATION:
       error_unsupported_dump_object (ctx, lv, "window configuration");
     case PVEC_OTHER:
@@ -3167,6 +3245,12 @@ dump_metadata_for_pdumper (struct dump_context *ctx)
                                       (void const *) dump_hooks[i]);
   dump_emacs_reloc_immediate_int (ctx, &nr_dump_hooks, nr_dump_hooks);
 
+  for (int i = 0; i < nr_dump_late_hooks; ++i)
+    dump_emacs_reloc_to_emacs_ptr_raw (ctx, &dump_late_hooks[i],
+                                      (void const *) dump_late_hooks[i]);
+  dump_emacs_reloc_immediate_int (ctx, &nr_dump_late_hooks,
+                                 nr_dump_late_hooks);
+
   for (int i = 0; i < nr_remembered_data; ++i)
     {
       dump_emacs_reloc_to_emacs_ptr_raw (ctx, &remembered_data[i].mem,
@@ -3328,6 +3412,29 @@ dump_cold_bignum (struct dump_context *ctx, Lisp_Object 
object)
     }
 }
 
+#ifdef HAVE_NATIVE_COMP
+static void
+dump_cold_native_subr (struct dump_context *ctx, Lisp_Object subr)
+{
+  /* Dump subr contents.  */
+  dump_off subr_offset = dump_recall_object (ctx, subr);
+  eassert (subr_offset > 0);
+  dump_remember_fixup_ptr_raw
+    (ctx,
+     subr_offset + dump_offsetof (struct Lisp_Subr, symbol_name),
+     ctx->offset);
+  const char *symbol_name = XSUBR (subr)->symbol_name;
+  dump_write (ctx, symbol_name, 1 + strlen (symbol_name));
+
+  dump_remember_fixup_ptr_raw
+    (ctx,
+     subr_offset + dump_offsetof (struct Lisp_Subr, native_c_name[0]),
+     ctx->offset);
+  const char *c_name = XSUBR (subr)->native_c_name[0];
+  dump_write (ctx, c_name, 1 + strlen (c_name));
+}
+#endif
+
 static void
 dump_drain_cold_data (struct dump_context *ctx)
 {
@@ -3371,6 +3478,11 @@ dump_drain_cold_data (struct dump_context *ctx)
         case COLD_OP_BIGNUM:
           dump_cold_bignum (ctx, data);
           break;
+#ifdef HAVE_NATIVE_COMP
+       case COLD_OP_NATIVE_SUBR:
+         dump_cold_native_subr (ctx, data);
+         break;
+#endif
         default:
           emacs_abort ();
         }
@@ -3779,7 +3891,7 @@ dump_do_fixup (struct dump_context *ctx,
       /* Dump wants a pointer to a Lisp object.
          If DUMP_FIXUP_LISP_OBJECT_RAW, we should stick a C pointer in
          the dump; otherwise, a Lisp_Object.  */
-      if (SUBRP (arg))
+      if (SUBRP (arg) && !SUBR_NATIVE_COMPILEDP (arg))
         {
           dump_value = emacs_offset (XSUBR (arg));
           if (type == DUMP_FIXUP_LISP_OBJECT)
@@ -3960,7 +4072,8 @@ types.  */)
   ctx->symbol_aux = Qnil;
   ctx->copied_queue = Qnil;
   ctx->cold_queue = Qnil;
-  ctx->dump_relocs = Qnil;
+  for (int i = 0; i < RELOC_NUM_PHASES; ++i)
+    ctx->dump_relocs[i] = Qnil;
   ctx->object_starts = Qnil;
   ctx->emacs_relocs = Qnil;
   ctx->bignum_data = make_eq_hash_table ();
@@ -4128,8 +4241,9 @@ types.  */)
   /* Emit instructions for Emacs to execute when loading the dump.
      Note that this relocation information ends up in the cold section
      of the dump.  */
-  drain_reloc_list (ctx, dump_emit_dump_reloc, emacs_reloc_merger,
-                   &ctx->dump_relocs, &ctx->header.dump_relocs);
+  for (int i = 0; i < RELOC_NUM_PHASES; ++i)
+    drain_reloc_list (ctx, dump_emit_dump_reloc, emacs_reloc_merger,
+                     &ctx->dump_relocs[i], &ctx->header.dump_relocs[i]);
   dump_off number_hot_relocations = ctx->number_hot_relocations;
   ctx->number_hot_relocations = 0;
   dump_off number_discardable_relocations = 
ctx->number_discardable_relocations;
@@ -4147,7 +4261,8 @@ types.  */)
   eassert (NILP (ctx->deferred_symbols));
   eassert (NILP (ctx->deferred_hash_tables));
   eassert (NILP (ctx->fixups));
-  eassert (NILP (ctx->dump_relocs));
+  for (int i = 0; i < RELOC_NUM_PHASES; ++i)
+    eassert (NILP (ctx->dump_relocs[i]));
   eassert (NILP (ctx->emacs_relocs));
 
   /* Dump is complete.  Go back to the header and write the magic
@@ -4207,6 +4322,15 @@ pdumper_do_now_and_after_load_impl (pdumper_hook hook)
   hook ();
 }
 
+void
+pdumper_do_now_and_after_late_load_impl (pdumper_hook hook)
+{
+  if (nr_dump_late_hooks == ARRAYELTS (dump_late_hooks))
+    fatal ("out of dump hooks: make dump_late_hooks[] bigger");
+  dump_late_hooks[nr_dump_late_hooks++] = hook;
+  hook ();
+}
+
 static void
 pdumper_remember_user_data_1 (void *mem, int nbytes)
 {
@@ -4232,6 +4356,16 @@ pdumper_remember_lv_ptr_raw_impl (void *ptr, enum 
Lisp_Type type)
 }
 
 
+#ifdef HAVE_NATIVE_COMP
+/* This records the directory where the Emacs executable lives, to be
+   used for locating the native-lisp directory from which we need to
+   load the preloaded *.eln files.  See pdumper_set_emacs_execdir
+   below.  */
+static char *emacs_execdir;
+static ptrdiff_t execdir_size;
+static ptrdiff_t execdir_len;
+#endif
+
 /* Dump runtime */
 enum dump_memory_protection
 {
@@ -5138,6 +5272,117 @@ dump_do_dump_relocation (const uintptr_t dump_base,
         dump_write_word_to_dump (dump_base, reloc_offset, value);
         break;
       }
+#ifdef HAVE_NATIVE_COMP
+    case RELOC_NATIVE_COMP_UNIT:
+      {
+       static enum { UNKNOWN, LOCAL_BUILD, INSTALLED } installation_state;
+       struct Lisp_Native_Comp_Unit *comp_u =
+         dump_ptr (dump_base, reloc_offset);
+       comp_u->lambda_gc_guard_h = CALLN (Fmake_hash_table, QCtest, Qeq);
+       if (STRINGP (comp_u->file))
+         error ("Trying to load incoherent dumped eln file %s",
+                SSDATA (comp_u->file));
+
+       /* emacs_execdir is always unibyte, but the file names in
+          comp_u->file could be multibyte, so we need to encode
+          them.  */
+       Lisp_Object cu_file1 = ENCODE_FILE (XCAR (comp_u->file));
+       Lisp_Object cu_file2 = ENCODE_FILE (XCDR (comp_u->file));
+       ptrdiff_t fn1_len = SBYTES (cu_file1), fn2_len = SBYTES (cu_file2);
+       Lisp_Object eln_fname;
+       char *fndata;
+
+       /* Check just once if this is a local build or Emacs was installed.  */
+       /* Can't use expand-file-name here, because we are too early
+          in the startup, and we will crash at least on WINDOWSNT.  */
+       if (installation_state == UNKNOWN)
+         {
+           eln_fname = make_uninit_string (execdir_len + fn1_len);
+           fndata = SSDATA (eln_fname);
+           memcpy (fndata, emacs_execdir, execdir_len);
+           memcpy (fndata + execdir_len, SSDATA (cu_file1), fn1_len);
+           if (file_access_p (fndata, F_OK))
+             installation_state = INSTALLED;
+           else
+             {
+               eln_fname = make_uninit_string (execdir_len + fn2_len);
+               fndata = SSDATA (eln_fname);
+               memcpy (fndata, emacs_execdir, execdir_len);
+               memcpy (fndata + execdir_len, SSDATA (cu_file2), fn2_len);
+               installation_state = LOCAL_BUILD;
+             }
+           fixup_eln_load_path (eln_fname);
+         }
+       else
+         {
+           ptrdiff_t fn_len =
+             installation_state == INSTALLED ? fn1_len : fn2_len;
+           Lisp_Object cu_file =
+             installation_state == INSTALLED ? cu_file1 : cu_file2;
+           eln_fname = make_uninit_string (execdir_len + fn_len);
+           fndata = SSDATA (eln_fname);
+           memcpy (fndata, emacs_execdir, execdir_len);
+           memcpy (fndata + execdir_len, SSDATA (cu_file), fn_len);
+         }
+
+       /* FIXME: This records the names of the *.eln files in an
+          unexpanded form, with one or more ".." elements (and on
+          Windows with the first part using backslashes).  The file
+          names are also unibyte.  If we care about this, we need to
+          loop in startup.el over all the preloaded modules and run
+          their file names through expand-file-name and
+          decode-coding-string.  */
+       comp_u->file = eln_fname;
+       comp_u->handle = dynlib_open (SSDATA (eln_fname));
+       if (!comp_u->handle)
+         {
+           fprintf (stderr, "Error using execdir %s:\n",
+                    emacs_execdir);
+           error ("%s", dynlib_error ());
+         }
+       load_comp_unit (comp_u, true, false);
+       break;
+      }
+    case RELOC_NATIVE_SUBR:
+      {
+       if (!NATIVE_COMP_FLAG)
+         /* This cannot happen.  */
+         emacs_abort ();
+
+       /* When resurrecting from a dump given non all the original
+          native compiled subrs may be still around we can't rely on
+          a 'top_level_run' mechanism, we revive them one-by-one
+          here.  */
+       struct Lisp_Subr *subr = dump_ptr (dump_base, reloc_offset);
+       struct Lisp_Native_Comp_Unit *comp_u =
+         XNATIVE_COMP_UNIT (subr->native_comp_u[0]);
+       if (!comp_u->handle)
+         error ("NULL handle in compilation unit %s", SSDATA (comp_u->file));
+       const char *c_name = subr->native_c_name[0];
+       eassert (c_name);
+       void *func = dynlib_sym (comp_u->handle, c_name);
+       if (!func)
+         error ("can't find function \"%s\" in compilation unit %s", c_name,
+                SSDATA (comp_u->file));
+       subr->function.a0 = func;
+       Lisp_Object lambda_data_idx =
+         Fgethash (build_string (c_name), comp_u->lambda_c_name_idx_h, Qnil);
+       if (!NILP (lambda_data_idx))
+         {
+           /* This is an anonymous lambda.
+              We must fixup d_reloc_imp so the lambda can be referenced
+              by code.  */
+           Lisp_Object tem;
+           XSETSUBR (tem, subr);
+           Lisp_Object *fixup =
+             &(comp_u->data_imp_relocs[XFIXNUM (lambda_data_idx)]);
+           eassert (EQ (*fixup, Qlambda_fixup));
+           *fixup = tem;
+           Fputhash (tem, Qt, comp_u->lambda_gc_guard_h);
+         }
+       break;
+      }
+#endif
     case RELOC_BIGNUM:
       {
         struct Lisp_Bignum *bignum = dump_ptr (dump_base, reloc_offset);
@@ -5160,11 +5405,12 @@ dump_do_dump_relocation (const uintptr_t dump_base,
 }
 
 static void
-dump_do_all_dump_relocations (const struct dump_header *const header,
-                             const uintptr_t dump_base)
+dump_do_all_dump_reloc_for_phase (const struct dump_header *const header,
+                                 const uintptr_t dump_base,
+                                 const enum reloc_phase phase)
 {
-  struct dump_reloc *r = dump_ptr (dump_base, header->dump_relocs.offset);
-  dump_off nr_entries = header->dump_relocs.nr_entries;
+  struct dump_reloc *r = dump_ptr (dump_base, 
header->dump_relocs[phase].offset);
+  dump_off nr_entries = header->dump_relocs[phase].nr_entries;
   for (dump_off i = 0; i < nr_entries; ++i)
     dump_do_dump_relocation (dump_base, r[i]);
 }
@@ -5229,6 +5475,26 @@ dump_do_all_emacs_relocations (const struct dump_header 
*const header,
     dump_do_emacs_relocation (dump_base, r[i]);
 }
 
+#ifdef HAVE_NATIVE_COMP
+/* Compute and record the directory of the Emacs executable given the
+   file name of that executable.  */
+static void
+pdumper_set_emacs_execdir (char *emacs_executable)
+{
+  char *p = emacs_executable + strlen (emacs_executable);
+
+  while (p > emacs_executable
+        && !IS_DIRECTORY_SEP (p[-1]))
+    --p;
+  eassert (p > emacs_executable);
+  emacs_execdir = xpalloc (emacs_execdir, &execdir_size,
+                          p - emacs_executable + 1 - execdir_size, -1, 1);
+  memcpy (emacs_execdir, emacs_executable, p - emacs_executable);
+  execdir_len = p - emacs_executable;
+  emacs_execdir[execdir_len] = '\0';
+}
+#endif
+
 enum dump_section
   {
    DS_HOT,
@@ -5245,7 +5511,7 @@ static Lisp_Object *pdumper_hashes = &zero_vector;
    N.B. We run very early in initialization, so we can't use lisp,
    unwinding, xmalloc, and so on.  */
 int
-pdumper_load (const char *dump_filename)
+pdumper_load (const char *dump_filename, char *argv0)
 {
   intptr_t dump_size;
   struct stat stat;
@@ -5380,7 +5646,7 @@ pdumper_load (const char *dump_filename)
   dump_public.start = dump_base;
   dump_public.end = dump_public.start + dump_size;
 
-  dump_do_all_dump_relocations (header, dump_base);
+  dump_do_all_dump_reloc_for_phase (header, dump_base, EARLY_RELOCS);
   dump_do_all_emacs_relocations (header, dump_base);
 
   dump_mmap_discard_contents (&sections[DS_DISCARDABLE]);
@@ -5400,6 +5666,21 @@ pdumper_load (const char *dump_filename)
      initialization.  */
   for (int i = 0; i < nr_dump_hooks; ++i)
     dump_hooks[i] ();
+
+#ifdef HAVE_NATIVE_COMP
+  pdumper_set_emacs_execdir (argv0);
+#else
+  (void) argv0;
+#endif
+
+  dump_do_all_dump_reloc_for_phase (header, dump_base, LATE_RELOCS);
+  dump_do_all_dump_reloc_for_phase (header, dump_base, VERY_LATE_RELOCS);
+
+  /* Run the functions Emacs registered for doing post-dump-load
+     initialization.  */
+  for (int i = 0; i < nr_dump_late_hooks; ++i)
+    dump_late_hooks[i] ();
+
   initialized = true;
 
   struct timespec load_timespec =
diff --git a/src/pdumper.h b/src/pdumper.h
index ed665ac..deec9af 100644
--- a/src/pdumper.h
+++ b/src/pdumper.h
@@ -81,6 +81,7 @@ pdumper_remember_lv_ptr_raw (void *ptr, enum Lisp_Type type)
 
 typedef void (*pdumper_hook)(void);
 extern void pdumper_do_now_and_after_load_impl (pdumper_hook hook);
+extern void pdumper_do_now_and_after_late_load_impl (pdumper_hook hook);
 
 INLINE void
 pdumper_do_now_and_after_load (pdumper_hook hook)
@@ -92,6 +93,18 @@ pdumper_do_now_and_after_load (pdumper_hook hook)
 #endif
 }
 
+/* Same as 'pdumper_do_now_and_after_load' but for hooks running code
+   that can call into Lisp.  */
+INLINE void
+pdumper_do_now_and_after_late_load (pdumper_hook hook)
+{
+#ifdef HAVE_PDUMPER
+  pdumper_do_now_and_after_late_load_impl (hook);
+#else
+  hook ();
+#endif
+}
+
 /* Macros useful in pdumper callback functions.  Assign a value if
    we're loading a dump and the value needs to be reset to its
    original value, and if we're initializing for the first time,
@@ -127,7 +140,7 @@ enum pdumper_load_result
     PDUMPER_LOAD_ERROR /* Must be last, as errno may be added.  */
   };
 
-int pdumper_load (const char *dump_filename);
+int pdumper_load (const char *dump_filename, char *argv0);
 
 struct pdumper_loaded_dump
 {
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
index e9ee2ba..65c81f7 100644
--- a/src/pgtkfns.c
+++ b/src/pgtkfns.c
@@ -439,7 +439,7 @@ x_change_tab_bar_height (struct frame *f, int height)
   int unit = FRAME_LINE_HEIGHT (f);
   int old_height = FRAME_TAB_BAR_HEIGHT (f);
   int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -447,16 +447,8 @@ x_change_tab_bar_height (struct frame *f, int height)
   /* Recalculate tab bar and frame text sizes.  */
   FRAME_TAB_BAR_HEIGHT (f) = height;
   FRAME_TAB_BAR_LINES (f) = lines;
-  /* Store the `tab-bar-lines' and `height' frame parameters.  */
   store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
-  store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
-
-  /* We also have to make sure that the internal border at the top of
-     the frame, below the menu bar or tab bar, is redrawn when the
-     tab bar disappears.  This is so because the internal border is
-     below the tab bar if one is displayed, but is below the menu bar
-     if there isn't a tab bar.  The tab bar draws into the area
-     below the menu bar.  */
+
   if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
     {
       clear_frame (f);
@@ -466,25 +458,21 @@ x_change_tab_bar_height (struct frame *f, int height)
   if ((height < old_height) && WINDOWP (f->tab_bar_window))
     clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
 
-  /* Recalculate tabbar height.  */
-  f->n_tab_bar_rows = 0;
-  if (old_height == 0
-      && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             &&
-             NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
-    f->tab_bar_redisplayed = f->tab_bar_resized = false;
-
-  adjust_frame_size (f, -1, -1,
-                    ((!f->tab_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4), false, Qtab_bar_lines);
-
-  f->tab_bar_resized = f->tab_bar_redisplayed;
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtab_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
 
   /* adjust_frame_size might not have done anything, garbage frame
      here.  */
@@ -1238,7 +1226,6 @@ This function is an internal primitive--use `make-frame' 
instead.  */ )
   struct pgtk_display_info *dpyinfo = NULL;
   Lisp_Object parent, parent_frame;
   struct kboard *kb;
-  int x_width = 0, x_height = 0;
 
   parms = Fcopy_alist (parms);
 
@@ -1578,7 +1565,7 @@ This function is an internal primitive--use `make-frame' 
instead.  */ )
 
   /* Compute the size of the X window.  */
   window_prompting =
-    gui_figure_window_size (f, parms, true, true, &x_width, &x_height);
+    gui_figure_window_size (f, parms, true, true);
 
   tem =
     gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
@@ -1694,11 +1681,6 @@ This function is an internal primitive--use `make-frame' 
instead.  */ )
   /* Consider frame official, now.  */
   f->can_set_window_size = true;
 
-  if (x_width > 0)
-    SET_FRAME_WIDTH (f, x_width);
-  if (x_height > 0)
-    SET_FRAME_HEIGHT (f, x_height);
-
   /* Tell the server what size and position, etc, we want, and how
      badly we want them.  This should be done after we have the menu
      bar so that its size can be taken into account.  */
@@ -2793,10 +2775,8 @@ x_create_tip_frame (struct pgtk_display_info *dpyinfo, 
Lisp_Object parms, struct
   struct frame *f;
   Lisp_Object frame;
   Lisp_Object name;
-  int width, height;
   ptrdiff_t count = SPECPDL_INDEX ();
   bool face_change_before = face_change;
-  int x_width = 0, x_height = 0;
 
   if (!dpyinfo->terminal->name)
     error ("Terminal is not live, can't create new frames on it");
@@ -2940,7 +2920,7 @@ x_create_tip_frame (struct pgtk_display_info *dpyinfo, 
Lisp_Object parms, struct
                          "inhibitDoubleBuffering", "InhibitDoubleBuffering",
                          RES_TYPE_BOOLEAN);
 
-  gui_figure_window_size (f, parms, false, false, &x_width, &x_height);
+  gui_figure_window_size (f, parms, false, false);
 
   xg_create_frame_widgets (f);
   pgtk_set_event_handler (f);
@@ -2969,15 +2949,6 @@ x_create_tip_frame (struct pgtk_display_info *dpyinfo, 
Lisp_Object parms, struct
   gui_default_parameter (f, parms, Qalpha, Qnil,
                          "alpha", "Alpha", RES_TYPE_NUMBER);
 
-  /* Dimensions, especially FRAME_LINES (f), must be done via 
change_frame_size.
-     Change will not be effected unless different from the current
-     FRAME_LINES (f).  */
-  width = FRAME_COLS (f);
-  height = FRAME_LINES (f);
-  SET_FRAME_COLS (f, 0);
-  SET_FRAME_LINES (f, 0);
-  change_frame_size (f, width, height, true, false, false, false);
-
   /* Add `tooltip' frame parameter's default value. */
   if (NILP (Fframe_parameter (frame, Qtooltip)))
     {
@@ -3033,6 +3004,8 @@ x_create_tip_frame (struct pgtk_display_info *dpyinfo, 
Lisp_Object parms, struct
      visible won't work.  */
   Vframe_list = Fcons (frame, Vframe_list);
   f->can_set_window_size = true;
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qtip_frame);
 
   /* Setting attributes of faces of the tooltip frame from resources
      and similar will set face_change, which leads to the clearing of
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 55c139f..3839b2b 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -457,9 +457,8 @@ x_set_offset (struct frame *f, int xoff, int yoff, int 
change_gravity)
 }
 
 static void
-pgtk_set_window_size (struct frame *f,
-                     bool change_gravity,
-                     int width, int height, bool pixelwise)
+pgtk_set_window_size (struct frame *f, bool change_gravity,
+                     int width, int height)
 /* --------------------------------------------------------------------------
      Adjust window pixel size based on given character grid size
      Impl is a bit more complex than other terms, need to do some
@@ -475,6 +474,7 @@ pgtk_set_window_size (struct frame *f,
   gtk_widget_get_size_request (FRAME_GTK_WIDGET (f), &pixelwidth,
                               &pixelheight);
 
+#if 0
   if (pixelwise)
     {
       pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
@@ -485,7 +485,12 @@ pgtk_set_window_size (struct frame *f,
       pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, width);
       pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
     }
+#else
+  pixelwidth = width;
+  pixelheight = height;
+#endif
 
+#if 0
   frame_size_history_add
     (f, Qx_set_window_size_1, width, height,
      list5 (Fcons (make_fixnum (pixelwidth), make_fixnum (pixelheight)),
@@ -493,6 +498,7 @@ pgtk_set_window_size (struct frame *f,
            make_fixnum (f->border_width),
            make_fixnum (FRAME_PGTK_TITLEBAR_HEIGHT (f)),
            make_fixnum (FRAME_TOOLBAR_HEIGHT (f))));
+#endif
 
   for (GtkWidget * w = FRAME_GTK_WIDGET (f); w != NULL;
        w = gtk_widget_get_parent (w))
@@ -692,6 +698,9 @@ pgtk_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
   get_font_ascent_descent (font, &font_ascent, &font_descent);
   FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
 
+  /* We could use a more elaborate calculation here.  */
+  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+
   /* Compute the scroll bar width in character columns.  */
   if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
     {
diff --git a/src/print.c b/src/print.c
index 14af919..d4301fd 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1841,7 +1841,18 @@ print_vectorlike (Lisp_Object obj, Lisp_Object 
printcharfun, bool escapeflag,
       }
       break;
 #endif
-
+#ifdef HAVE_NATIVE_COMP
+    case PVEC_NATIVE_COMP_UNIT:
+      {
+       struct Lisp_Native_Comp_Unit *cu = XNATIVE_COMP_UNIT (obj);
+       print_c_string ("#<native compilation unit: ", printcharfun);
+       print_string (cu->file, printcharfun);
+       printchar (' ', printcharfun);
+       print_object (cu->optimize_qualities, printcharfun, escapeflag);
+       printchar ('>', printcharfun);
+      }
+      break;
+#endif
     default:
       emacs_abort ();
     }
diff --git a/src/process.c b/src/process.c
index dbf8552..98a6dca 100644
--- a/src/process.c
+++ b/src/process.c
@@ -1936,7 +1936,7 @@ usage: (make-process &rest ARGS)  */)
        {
          tem = Qnil;
          openp (Vexec_path, program, Vexec_suffixes, &tem,
-                make_fixnum (X_OK), false);
+                make_fixnum (X_OK), false, false);
          if (NILP (tem))
            report_file_error ("Searching for program", program);
          tem = Fexpand_file_name (tem, Qnil);
diff --git a/src/sound.c b/src/sound.c
index e5f66f8..9041076 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -1370,8 +1370,9 @@ Internal use only, use `play-sound' instead.  */)
   if (STRINGP (attrs[SOUND_FILE]))
     {
       /* Open the sound file.  */
-      current_sound->fd = openp (list1 (Vdata_directory),
-                                attrs[SOUND_FILE], Qnil, &file, Qnil, false);
+      current_sound->fd =
+       openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil,
+              false, false);
       if (current_sound->fd < 0)
        sound_perror ("Could not open sound file");
 
diff --git a/src/term.c b/src/term.c
index 1059b06..c995a44 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2356,9 +2356,7 @@ frame's terminal). */)
             was suspended.  */
          get_tty_size (fileno (t->display_info.tty->input), &width, &height);
          if (width != old_width || height != old_height)
-           change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
-                              - FRAME_TAB_BAR_LINES (f),
-                              0, 0, 0, 0);
+           change_frame_size (f, width, height, false, false, false);
          SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1);
        }
 
diff --git a/src/termhooks.h b/src/termhooks.h
index 524590f..12f5d0c 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -589,7 +589,7 @@ struct terminal
    window gravity for this size change and subsequent size changes.
    Otherwise we leave the window gravity unchanged.  */
   void (*set_window_size_hook) (struct frame *f, bool change_gravity,
-                                int width, int height, bool pixelwise);
+                                int width, int height);
 
   /* CHANGE_GRAVITY is 1 when calling from Fset_frame_position,
    to really change the position, and 0 when calling from
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index e55fd63..085a05a 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -34,7 +34,15 @@ AM_V_AR = @echo "  AR      " $@;
 AM_V_at = @
 AM_V_CC = @echo "  CC      " $@;
 AM_V_CCLD = @echo "  CCLD    " $@;
+ifeq ($(HAVE_NATIVE_COMP),yes)
+ifeq ($(NATIVE_DISABLED),1)
 AM_V_ELC = @echo "  ELC     " $@;
+else
+AM_V_ELC = @echo "  ELC+ELN     " $@;
+endif
+else
+AM_V_ELC = @echo "  ELC     " $@;
+endif
 AM_V_GEN = @echo "  GEN     " $@;
 AM_V_GLOBALS = @echo "  GEN     " globals.h;
 AM_V_NO_PD = --no-print-directory
diff --git a/src/w32.c b/src/w32.c
index aade802..467e6cb 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -1941,11 +1941,10 @@ buf_prev (int from)
   return prev_idx;
 }
 
-static void
-sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
+unsigned
+w32_get_nproc (void)
 {
   SYSTEM_INFO sysinfo;
-  FILETIME ft_idle, ft_user, ft_kernel;
 
   /* Initialize the number of processors on this machine.  */
   if (num_of_processors <= 0)
@@ -1960,6 +1959,15 @@ sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, 
ULONGLONG *user)
       if (num_of_processors <= 0)
        num_of_processors = 1;
     }
+  return num_of_processors;
+}
+
+static void
+sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
+{
+  FILETIME ft_idle, ft_user, ft_kernel;
+
+  (void) w32_get_nproc ();
 
   /* TODO: Take into account threads that are ready to run, by
      sampling the "\System\Processor Queue Length" performance
@@ -10247,7 +10255,8 @@ check_windows_init_file (void)
         need to ENCODE_FILE here, but we do need to convert the file
         names from UTF-8 to ANSI.  */
       init_file = build_string ("term/w32-win");
-      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0);
+      fd =
+       openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0);
       if (fd < 0)
        {
          Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
@@ -10439,6 +10448,13 @@ shutdown_handler (DWORD type)
       || type == CTRL_LOGOFF_EVENT    /* User logs off.  */
       || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown.  */
     {
+      /* If we are being shut down in noninteractive mode, we don't
+        care about the message stack, so clear it to avoid abort in
+        shut_down_emacs.  This happens when an noninteractive Emacs
+        is invoked as a subprocess of Emacs, and the parent wants to
+        kill us, e.g. because it's about to exit.  */
+      if (noninteractive)
+       clear_message_stack ();
       /* Shut down cleanly, making sure autosave files are up to date.  */
       shut_down_emacs (0, Qnil);
     }
@@ -10657,6 +10673,10 @@ globals_of_w32 (void)
 #endif
 
   w32_crypto_hprov = (HCRYPTPROV)0;
+
+  /* We need to forget about libraries that were loaded during the
+     dumping process (e.g. libgccjit) */
+  Vlibrary_cache = Qnil;
 }
 
 /* For make-serial-process  */
diff --git a/src/w32.h b/src/w32.h
index 3f8eb25..a382dbe 100644
--- a/src/w32.h
+++ b/src/w32.h
@@ -233,6 +233,9 @@ extern int w32_memory_info (unsigned long long *, unsigned 
long long *,
 /* Compare 2 UTF-8 strings in locale-dependent fashion.  */
 extern int w32_compare_strings (const char *, const char *, char *, int);
 
+/* Return the number of processor execution units on this system.  */
+extern unsigned w32_get_nproc (void);
+
 /* Return a cryptographically secure seed for PRNG.  */
 extern int w32_init_random (void *, ptrdiff_t);
 
diff --git a/src/w32common.h b/src/w32common.h
index 714a238..cbe05c5 100644
--- a/src/w32common.h
+++ b/src/w32common.h
@@ -86,6 +86,14 @@ get_proc_addr (HINSTANCE handle, LPCSTR fname)
     }                                                                  \
   while (false)
 
+/* Load a function from the DLL, and don't fail if it does not exist.  */
+#define LOAD_DLL_FN_OPT(lib, func)                                      \
+  do                                                                   \
+    {                                                                  \
+      fn_##func = (W32_PFN_##func) get_proc_addr (lib, #func);         \
+    }                                                                  \
+  while (false)
+
 #ifdef HAVE_HARFBUZZ
 extern bool hbfont_init_w32_funcs (HMODULE);
 #endif
diff --git a/src/w32fns.c b/src/w32fns.c
index c07f32a..66baeae 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -1701,7 +1701,7 @@ w32_change_tab_bar_height (struct frame *f, int height)
   int unit = FRAME_LINE_HEIGHT (f);
   int old_height = FRAME_TAB_BAR_HEIGHT (f);
   int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -1728,25 +1728,21 @@ w32_change_tab_bar_height (struct frame *f, int height)
   if ((height < old_height) && WINDOWP (f->tab_bar_window))
     clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
 
-  /* Recalculate tabbar height.  */
-  f->n_tab_bar_rows = 0;
-  if (old_height == 0
-      && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
-    f->tab_bar_redisplayed = f->tab_bar_resized = false;
-
-  adjust_frame_size (f, -1, -1,
-                    ((!f->tab_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4),
-                    false, Qtab_bar_lines);
-
-  f->tab_bar_resized = f->tab_bar_redisplayed;
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtab_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
 
   /* adjust_frame_size might not have done anything, garbage frame
      here.  */
@@ -1790,7 +1786,7 @@ w32_change_tool_bar_height (struct frame *f, int height)
   int unit = FRAME_LINE_HEIGHT (f);
   int old_height = FRAME_TOOL_BAR_HEIGHT (f);
   int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 
   /* Make sure we redisplay all windows in this frame.  */
   windows_or_buffers_changed = 23;
@@ -1811,25 +1807,21 @@ w32_change_tool_bar_height (struct frame *f, int height)
   if ((height < old_height) && WINDOWP (f->tool_bar_window))
     clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
 
-  /* Recalculate toolbar height.  */
-  f->n_tool_bar_rows = 0;
-  if (old_height == 0
-      && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             && NILP (Fmemq (Qtool_bar_lines, frame_inhibit_implied_resize)))))
-    f->tool_bar_redisplayed = f->tool_bar_resized = false;
-
-  adjust_frame_size (f, -1, -1,
-                    ((!f->tool_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4),
-                    false, Qtool_bar_lines);
-
-  f->tool_bar_resized = f->tool_bar_redisplayed;
+  if (!f->tool_bar_resized)
+    {
+      /* As long as tool_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtool_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+      f->tool_bar_resized =  f->tool_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
 
   /* adjust_frame_size might not have done anything, garbage frame
      here.  */
@@ -5718,7 +5710,6 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
   struct w32_display_info *dpyinfo = NULL;
   Lisp_Object parent, parent_frame;
   struct kboard *kb;
-  int x_width = 0, x_height = 0;
 
   if (!FRAME_W32_P (SELECTED_FRAME ())
       && !FRAME_INITIAL_P (SELECTED_FRAME ()))
@@ -6045,8 +6036,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
 
   f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor;
 
-  window_prompting = gui_figure_window_size (f, parameters, true, true,
-                                             &x_width, &x_height);
+  window_prompting = gui_figure_window_size (f, parameters, true, true);
 
   tem = gui_display_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0,
                              RES_TYPE_BOOLEAN);
@@ -6081,11 +6071,6 @@ DEFUN ("x-create-frame", Fx_create_frame, 
Sx_create_frame,
   /* Allow set_window_size_hook, now.  */
   f->can_set_window_size = true;
 
-  if (x_width > 0)
-    SET_FRAME_WIDTH (f, x_width);
-  if (x_height > 0)
-    SET_FRAME_HEIGHT (f, x_height);
-
   /* Tell the server what size and position, etc, we want, and how
      badly we want them.  This should be done after we have the menu
      bar so that its size can be taken into account.  */
@@ -6093,8 +6078,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
   w32_wm_set_size_hint (f, window_prompting, false);
   unblock_input ();
 
-  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 0, true,
-                    Qx_create_frame_2);
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qx_create_frame_2);
 
   /* Process fullscreen parameter here in the hope that normalizing a
      fullheight/fullwidth frame will produce the size set by the last
@@ -6888,11 +6873,9 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, 
Lisp_Object parms)
   struct frame *f;
   Lisp_Object frame;
   Lisp_Object name;
-  int width, height;
   ptrdiff_t count = SPECPDL_INDEX ();
   struct kboard *kb;
   bool face_change_before = face_change;
-  int x_width = 0, x_height = 0;
 
   /* Use this general default value to start with until we know if
      this frame has a specified name.  */
@@ -7013,7 +6996,7 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, 
Lisp_Object parms)
   f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
   f->output_data.w32->explicit_parent = false;
 
-  gui_figure_window_size (f, parms, true, true, &x_width, &x_height);
+  gui_figure_window_size (f, parms, true, true);
 
   /* No fringes on tip frame.  */
   f->fringe_cols = 0;
@@ -7039,15 +7022,6 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, 
Lisp_Object parms)
   gui_default_parameter (f, parms, Qalpha, Qnil,
                          "alpha", "Alpha", RES_TYPE_NUMBER);
 
-  /* Dimensions, especially FRAME_LINES (f), must be done via
-     change_frame_size.  Change will not be effected unless different
-     from the current FRAME_LINES (f).  */
-  width = FRAME_COLS (f);
-  height = FRAME_LINES (f);
-  SET_FRAME_COLS (f, 0);
-  SET_FRAME_LINES (f, 0);
-  adjust_frame_size (f, width * FRAME_COLUMN_WIDTH (f),
-                    height * FRAME_LINE_HEIGHT (f), 0, true, Qtip_frame);
   /* Add `tooltip' frame parameter's default value. */
   if (NILP (Fframe_parameter (frame, Qtooltip)))
     Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil));
@@ -7088,6 +7062,8 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, 
Lisp_Object parms)
      visible won't work.  */
   Vframe_list = Fcons (frame, Vframe_list);
   f->can_set_window_size = true;
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qtip_frame);
 
   /* Setting attributes of faces of the tooltip frame from resources
      and similar will set face_change, which leads to the
diff --git a/src/w32inevt.c b/src/w32inevt.c
index 1a80a00..1255072 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -608,9 +608,7 @@ resize_event (WINDOW_BUFFER_SIZE_RECORD *event)
 {
   struct frame *f = get_frame ();
 
-  change_frame_size (f, event->dwSize.X, event->dwSize.Y
-                    - FRAME_MENU_BAR_LINES (f)
-                    - FRAME_TAB_BAR_LINES (f), 0, 1, 0, 0);
+  change_frame_size (f, event->dwSize.X, event->dwSize.Y, false, true, false);
   SET_FRAME_GARBAGED (f);
 }
 
@@ -624,11 +622,9 @@ maybe_generate_resize_event (void)
 
   /* It is okay to call this unconditionally, since it will do nothing
      if the size hasn't actually changed.  */
-  change_frame_size (f,
-                    1 + info.srWindow.Right - info.srWindow.Left,
-                    1 + info.srWindow.Bottom - info.srWindow.Top
-                    - FRAME_MENU_BAR_LINES (f)
-                    - FRAME_TAB_BAR_LINES (f), 0, 1, 0, 0);
+  change_frame_size (f, 1 + info.srWindow.Right - info.srWindow.Left,
+                    1 + info.srWindow.Bottom - info.srWindow.Top,
+                    false, true, false);
 }
 
 #if HAVE_W32NOTIFY
diff --git a/src/w32proc.c b/src/w32proc.c
index 2b6cb9c..ffa56e1 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -1918,7 +1918,8 @@ sys_spawnve (int mode, char *cmdname, char **argv, char 
**envp)
     {
       program = build_string (cmdname);
       full = Qnil;
-      openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK), 
0);
+      openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK),
+            0, 0);
       if (NILP (full))
        {
          errno = EINVAL;
@@ -3877,6 +3878,14 @@ w32_compare_strings (const char *s1, const char *s2, 
char *locname,
   return val - 2;
 }
 
+DEFUN ("w32-get-nproc", Fw32_get_nproc,
+       Sw32_get_nproc, 0, 0, 0,
+       doc: /* Return the number of system's processor execution units.  */)
+  (void)
+{
+  return make_fixnum (w32_get_nproc ());
+}
+
 
 void
 syms_of_ntproc (void)
@@ -3911,6 +3920,8 @@ syms_of_ntproc (void)
   defsubr (&Sw32_get_keyboard_layout);
   defsubr (&Sw32_set_keyboard_layout);
 
+  defsubr (&Sw32_get_nproc);
+
   DEFVAR_LISP ("w32-quote-process-args", Vw32_quote_process_args,
               doc: /* Non-nil enables quoting of process arguments to ensure 
correct parsing.
 Because Windows does not directly pass argv arrays to child processes,
diff --git a/src/w32term.c b/src/w32term.c
index 361cf33..4f91029 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -5352,7 +5352,7 @@ w32_read_socket (struct terminal *terminal,
          if (f)
            {
              RECT rect;
-             int /* rows, columns, */ width, height, text_width, text_height;
+             int /* rows, columns, */ width, height;
 
              if (GetClientRect (msg.msg.hwnd, &rect)
                  /* GetClientRect evidently returns (0, 0, 0, 0) if
@@ -5365,23 +5365,11 @@ w32_read_socket (struct terminal *terminal,
                {
                  height = rect.bottom - rect.top;
                  width = rect.right - rect.left;
-                 text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, width);
-                 text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, height);
-                 /* rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height); */
-                 /* columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width); */
-
-                 /* TODO: Clip size to the screen dimensions.  */
-
-                 /* Even if the number of character rows and columns
-                    has not changed, the font size may have changed,
-                    so we need to check the pixel dimensions as well.  */
-
                  if (width != FRAME_PIXEL_WIDTH (f)
-                     || height != FRAME_PIXEL_HEIGHT (f)
-                     || text_width != FRAME_TEXT_WIDTH (f)
-                     || text_height != FRAME_TEXT_HEIGHT (f))
+                     || height != FRAME_PIXEL_HEIGHT (f))
                    {
-                     change_frame_size (f, text_width, text_height, 0, 1, 0, 
1);
+                     change_frame_size
+                       (f, width, height, false, true, false);
                      SET_FRAME_GARBAGED (f);
                      cancel_mouse_face (f);
                      f->win_gravity = NorthWestGravity;
@@ -5565,7 +5553,7 @@ w32_read_socket (struct terminal *terminal,
          if (f && !FRAME_ICONIFIED_P (f) && msg.msg.wParam != SIZE_MINIMIZED)
            {
              RECT rect;
-             int /* rows, columns, */ width, height, text_width, text_height;
+             int /* rows, columns, */ width, height;
 
              if (GetClientRect (msg.msg.hwnd, &rect)
                  /* GetClientRect evidently returns (0, 0, 0, 0) if
@@ -5578,23 +5566,12 @@ w32_read_socket (struct terminal *terminal,
                {
                  height = rect.bottom - rect.top;
                  width = rect.right - rect.left;
-                 text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, width);
-                 text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, height);
-                 /* rows = FRAME_PIXEL_HEIGHT_TO_TEXT_LINES (f, height); */
-                 /* columns = FRAME_PIXEL_WIDTH_TO_TEXT_COLS (f, width); */
-
-                 /* TODO: Clip size to the screen dimensions.  */
-
-                 /* Even if the number of character rows and columns
-                    has not changed, the font size may have changed,
-                    so we need to check the pixel dimensions as well.  */
 
                  if (width != FRAME_PIXEL_WIDTH (f)
-                     || height != FRAME_PIXEL_HEIGHT (f)
-                     || text_width != FRAME_TEXT_WIDTH (f)
-                     || text_height != FRAME_TEXT_HEIGHT (f))
+                     || height != FRAME_PIXEL_HEIGHT (f))
                    {
-                     change_frame_size (f, text_width, text_height, 0, 1, 0, 
1);
+                     change_frame_size
+                       (f, width, height, false, true, false);
                      SET_FRAME_GARBAGED (f);
                      cancel_mouse_face (f);
                      f->win_gravity = NorthWestGravity;
@@ -6267,17 +6244,15 @@ w32_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
        FRAME_CONFIG_SCROLL_BAR_COLS (f) * unit;
     }
 
-  /* Now make the frame display the given font.  */
-  if (FRAME_NATIVE_WINDOW (f) != 0)
-    {
-      /* Don't change the size of a tip frame; there's no point in
-        doing it because it's done in Fx_show_tip, and it leads to
-        problems because the tip frame has no widget.  */
-      if (!FRAME_TOOLTIP_P (f))
-       adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
-                          FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
-                          false, Qfont);
-    }
+  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+
+/* Don't change the size of a tip frame; there's no point in
+   doing it because it's done in Fx_show_tip, and it leads to
+   problems because the tip frame has no widget.  */
+  if (FRAME_NATIVE_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
+    adjust_frame_size
+      (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+       FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, false, Qfont);
 
   /* X version sets font of input methods here also.  */
 
@@ -6490,7 +6465,8 @@ w32fullscreen_hook (struct frame *f)
        ShowWindow (hwnd, SW_SHOWNORMAL);
       else if (f->want_fullscreen == FULLSCREEN_MAXIMIZED)
        {
-         if (prev_fsmode == FULLSCREEN_BOTH || prev_fsmode == FULLSCREEN_WIDTH
+         if (prev_fsmode == FULLSCREEN_BOTH
+             || prev_fsmode == FULLSCREEN_WIDTH
              || prev_fsmode == FULLSCREEN_HEIGHT)
            /* Make window normal since otherwise the subsequent
               maximization might fail in some cases.  */
@@ -6499,52 +6475,31 @@ w32fullscreen_hook (struct frame *f)
        }
       else if (f->want_fullscreen == FULLSCREEN_BOTH)
         {
-         int menu_bar_height = GetSystemMetrics (SM_CYMENU);
-
-         w32_fullscreen_rect (hwnd, f->want_fullscreen,
-                              FRAME_NORMAL_PLACEMENT (f).rcNormalPosition, 
&rect);
+         w32_fullscreen_rect
+           (hwnd, f->want_fullscreen,
+            FRAME_NORMAL_PLACEMENT (f).rcNormalPosition, &rect);
          if (!FRAME_UNDECORATED (f))
            SetWindowLong (hwnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW);
           SetWindowPos (hwnd, HWND_TOP, rect.left, rect.top,
                         rect.right - rect.left, rect.bottom - rect.top,
                         SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
          change_frame_size
-           (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, rect.right - rect.left),
-            FRAME_PIXEL_TO_TEXT_HEIGHT (f, (rect.bottom - rect.top
-                                            - menu_bar_height)),
-            0, 1, 0, 1);
+           (f, rect.right - rect.left, rect.bottom - rect.top,
+            false, true, false);
         }
       else
         {
          ShowWindow (hwnd, SW_SHOWNORMAL);
-         w32_fullscreen_rect (hwnd, f->want_fullscreen,
-                              FRAME_NORMAL_PLACEMENT (f).rcNormalPosition, 
&rect);
+         w32_fullscreen_rect
+           (hwnd, f->want_fullscreen,
+            FRAME_NORMAL_PLACEMENT (f).rcNormalPosition, &rect);
           SetWindowPos (hwnd, HWND_TOP, rect.left, rect.top,
                         rect.right - rect.left, rect.bottom - rect.top, 0);
 
-         if (f->want_fullscreen == FULLSCREEN_WIDTH)
-           {
-             int border_width = GetSystemMetrics (SM_CXFRAME);
-
-             change_frame_size
-               (f, (FRAME_PIXEL_TO_TEXT_WIDTH
-                    (f, rect.right - rect.left - 2 * border_width)),
-                0, 0, 1, 0, 1);
-           }
-         else
-           {
-             int border_height = GetSystemMetrics (SM_CYFRAME);
-             /* Won't work for wrapped menu bar.  */
-             int menu_bar_height = GetSystemMetrics (SM_CYMENU);
-             int title_height = GetSystemMetrics (SM_CYCAPTION);
-
-             change_frame_size
-               (f, 0, (FRAME_PIXEL_TO_TEXT_HEIGHT
-                       (f, rect.bottom - rect.top - 2 * border_height
-                        - title_height - menu_bar_height)),
-                0, 1, 0, 1);
-           }
-        }
+         change_frame_size
+           (f, rect.right - rect.left, rect.bottom - rect.top,
+            false, true, false);
+       }
 
       f->want_fullscreen = FULLSCREEN_NONE;
       unblock_input ();
@@ -6559,16 +6514,14 @@ w32fullscreen_hook (struct frame *f)
     f->want_fullscreen |= FULLSCREEN_WAIT;
 }
 
-/* Call this to change the size of frame F's native window.
-   If CHANGE_GRAVITY, change to top-left-corner window gravity
-   for this size change and subsequent size changes.
-   Otherwise we leave the window gravity unchanged.  */
-
+/* Change the size of frame F's Windows window to WIDTH and HEIGHT
+   pixels.  If CHANGE_GRAVITY, change to top-left-corner window gravity
+   for this size change and subsequent size changes.  Otherwise leave
+   the window gravity unchanged.  */
 static void
 w32_set_window_size (struct frame *f, bool change_gravity,
-                  int width, int height, bool pixelwise)
+                    int width, int height)
 {
-  int pixelwidth, pixelheight;
   Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
   RECT rect;
   MENUBARINFO info;
@@ -6584,17 +6537,6 @@ w32_set_window_size (struct frame *f, bool 
change_gravity,
   GetMenuBarInfo (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &info);
   menu_bar_height = info.rcBar.bottom - info.rcBar.top;
 
-  if (pixelwise)
-    {
-      pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
-      pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
-    }
-  else
-    {
-      pixelwidth = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, width);
-      pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
-    }
-
   if (w32_add_wrapped_menu_bar_lines)
     {
       /* When the menu bar wraps sending a SetWindowPos shrinks the
@@ -6610,15 +6552,15 @@ w32_set_window_size (struct frame *f, bool 
change_gravity,
       if ((default_menu_bar_height > 0)
          && (menu_bar_height > default_menu_bar_height)
          && ((menu_bar_height % default_menu_bar_height) == 0))
-       pixelheight = pixelheight + menu_bar_height - default_menu_bar_height;
+       height = height + menu_bar_height - default_menu_bar_height;
     }
 
   f->win_gravity = NorthWestGravity;
   w32_wm_set_size_hint (f, (long) 0, false);
 
   rect.left = rect.top = 0;
-  rect.right = pixelwidth;
-  rect.bottom = pixelheight;
+  rect.right = width;
+  rect.bottom = height;
 
   AdjustWindowRect (&rect, f->output_data.w32->dwStyle, menu_bar_height > 0);
 
@@ -6636,7 +6578,7 @@ w32_set_window_size (struct frame *f, bool change_gravity,
        {
          rect.left = window_rect.left;
          rect.right = window_rect.right;
-         pixelwidth = 0;
+         width = -1;
        }
       if (EQ (fullscreen, Qmaximized)
          || EQ (fullscreen, Qfullboth)
@@ -6644,19 +6586,12 @@ w32_set_window_size (struct frame *f, bool 
change_gravity,
        {
          rect.top = window_rect.top;
          rect.bottom = window_rect.bottom;
-         pixelheight = 0;
+         height = -1;
        }
     }
 
-  if (pixelwidth > 0 || pixelheight > 0)
+  if (width > 0 || height > 0)
     {
-      frame_size_history_add
-       (f, Qx_set_window_size_1, width, height,
-        list2 (Fcons (make_fixnum (pixelwidth),
-                      make_fixnum (pixelheight)),
-               Fcons (make_fixnum (rect.right - rect.left),
-                      make_fixnum (rect.bottom - rect.top))));
-
       if (!FRAME_PARENT_FRAME (f))
        my_set_window_pos (FRAME_W32_WINDOW (f), NULL,
                           0, 0,
@@ -6670,12 +6605,7 @@ w32_set_window_size (struct frame *f, bool 
change_gravity,
                           rect.bottom - rect.top,
                           SWP_NOMOVE | SWP_NOACTIVATE);
 
-      change_frame_size (f,
-                        ((pixelwidth == 0)
-                            ? 0 : FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth)),
-                        ((pixelheight == 0)
-                         ? 0 : FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight)),
-                        0, 1, 0, 1);
+      change_frame_size (f, width, height, false, true, false);
       SET_FRAME_GARBAGED (f);
 
       /* If cursor was outside the new size, mark it as off.  */
diff --git a/src/widget.c b/src/widget.c
index 43f0307..dd43fd1 100644
--- a/src/widget.c
+++ b/src/widget.c
@@ -169,14 +169,6 @@ pixel_to_char_size (EmacsFrame ew, Dimension pixel_width, 
Dimension pixel_height
 }
 
 static void
-pixel_to_text_size (EmacsFrame ew, Dimension pixel_width, Dimension 
pixel_height, int *text_width, int *text_height)
-{
-  struct frame *f = ew->emacs_frame.frame;
-  *text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, (int) pixel_width);
-  *text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, (int) pixel_height);
-}
-
-static void
 char_to_pixel_size (EmacsFrame ew, int char_width, int char_height, Dimension 
*pixel_width, Dimension *pixel_height)
 {
   struct frame *f = ew->emacs_frame.frame;
@@ -257,27 +249,14 @@ set_frame_size (EmacsFrame ew)
 
    */
 
-  /* Hairily merged geometry */
   struct frame *f = ew->emacs_frame.frame;
-  int w = FRAME_COLS (f);
-  int h = FRAME_LINES (f);
-  Widget wmshell = get_wm_shell ((Widget) ew);
-  Dimension pixel_width, pixel_height;
-  /* Each Emacs shell is now independent and top-level.  */
-
-  if (! XtIsSubclass (wmshell, shellWidgetClass)) emacs_abort ();
-
-  char_to_pixel_size (ew, w, h, &pixel_width, &pixel_height);
-  ew->core.width = (frame_resize_pixelwise
-                   ? FRAME_PIXEL_WIDTH (f)
-                   : pixel_width);
-  ew->core.height = (frame_resize_pixelwise
-                    ? FRAME_PIXEL_HEIGHT (f)
-                    : pixel_height);
-
-  frame_size_history_add
-    (f, Qset_frame_size, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
-     list2i (ew->core.width, ew->core.height));
+
+  ew->core.width = FRAME_PIXEL_WIDTH (f);
+  ew->core.height = FRAME_PIXEL_HEIGHT (f);
+
+  if (CONSP (frame_size_history))
+    frame_size_history_plain
+      (f, build_string ("set_frame_size"));
 }
 
 static void
@@ -350,6 +329,13 @@ update_from_various_frame_slots (EmacsFrame ew)
   ew->emacs_frame.foreground_pixel = FRAME_FOREGROUND_PIXEL (f);
   ew->emacs_frame.cursor_color = x->cursor_pixel;
   ew->core.border_pixel = x->border_pixel;
+
+  if (CONSP (frame_size_history))
+    frame_size_history_extra
+      (f, build_string ("update_from_various_frame_slots"),
+       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+       ew->core.width, ew->core.height,
+       f->new_width, f->new_height);
 }
 
 static void
@@ -381,6 +367,7 @@ static void
 EmacsFrameRealize (Widget widget, XtValueMask *mask, XSetWindowAttributes 
*attrs)
 {
   EmacsFrame ew = (EmacsFrame) widget;
+  struct frame *f = ew->emacs_frame.frame;
 
   /* This used to contain SubstructureRedirectMask, but this turns out
      to be a problem with XIM on Solaris, and events from that mask
@@ -394,6 +381,11 @@ EmacsFrameRealize (Widget widget, XtValueMask *mask, 
XSetWindowAttributes *attrs
   /* Some ConfigureNotify events does not end up in EmacsFrameResize so
      make sure we get them all.  Seen with xfcwm4 for example.  */
   XtAddRawEventHandler (widget, StructureNotifyMask, False, resize_cb, NULL);
+
+  if (CONSP (frame_size_history))
+    frame_size_history_plain
+      (f, build_string ("EmacsFrameRealize"));
+
   update_wm_hints (ew);
 }
 
@@ -408,18 +400,15 @@ EmacsFrameResize (Widget widget)
 {
   EmacsFrame ew = (EmacsFrame) widget;
   struct frame *f = ew->emacs_frame.frame;
-  int width, height;
-
-  pixel_to_text_size (ew, ew->core.width, ew->core.height, &width, &height);
 
-  frame_size_history_add
-    (f, QEmacsFrameResize, width, height,
-     list5 (make_fixnum (ew->core.width), make_fixnum (ew->core.height),
-           make_fixnum (FRAME_TOP_MARGIN_HEIGHT (f)),
-           make_fixnum (FRAME_SCROLL_BAR_AREA_HEIGHT (f)),
-           make_fixnum (2 * FRAME_INTERNAL_BORDER_WIDTH (f))));
+  if (CONSP (frame_size_history))
+    frame_size_history_extra
+      (f, build_string ("EmacsFrameResize"),
+       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+       ew->core.width, ew->core.height,
+       f->new_width, f->new_height);
 
-  change_frame_size (f, width, height, 0, 1, 0, 1);
+  change_frame_size (f, ew->core.width, ew->core.height, false, true, false);
 
   update_wm_hints (ew);
   update_various_frame_slots (ew);
@@ -463,9 +452,17 @@ EmacsFrameSetCharSize (Widget widget, int columns, int 
rows)
   EmacsFrame ew = (EmacsFrame) widget;
   struct frame *f = ew->emacs_frame.frame;
 
+  if (CONSP (frame_size_history))
+    frame_size_history_extra
+      (f, build_string ("EmacsFrameSetCharSize"),
+       FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+       columns, rows,
+       f->new_width, f->new_height);
+
   if (!frame_inhibit_resize (f, 0, Qfont)
       && !frame_inhibit_resize (f, 1, Qfont))
-    x_set_window_size (f, 0, columns, rows, 0);
+    x_set_window_size (f, 0, columns * FRAME_COLUMN_WIDTH (f),
+                      rows * FRAME_LINE_HEIGHT (f));
 }
 
 
diff --git a/src/window.c b/src/window.c
index 5134c3d..0a14eca 100644
--- a/src/window.c
+++ b/src/window.c
@@ -8159,7 +8159,7 @@ init_window_once (void)
   minibuf_selected_window = Qnil;
   staticpro (&minibuf_selected_window);
 
-  pdumper_do_now_and_after_load (init_window_once_for_pdumper);
+  pdumper_do_now_and_after_late_load (init_window_once_for_pdumper);
 }
 
 static void init_window_once_for_pdumper (void)
diff --git a/src/xdisp.c b/src/xdisp.c
index dd7a4a8..eea3f81 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11860,7 +11860,7 @@ resize_mini_window (struct window *w, bool exact_p)
       int height, max_height;
       struct text_pos start;
       struct buffer *old_current_buffer = NULL;
-      int windows_height = FRAME_WINDOWS_HEIGHT (f);
+      int windows_height = FRAME_INNER_HEIGHT (f);
 
       if (current_buffer != XBUFFER (w->contents))
        {
@@ -13477,8 +13477,6 @@ PIXELWISE non-nil means return the height of the tab 
bar in pixels.  */)
 static bool
 redisplay_tab_bar (struct frame *f)
 {
-  f->tab_bar_redisplayed = true;
-
   struct window *w;
   struct it it;
   struct glyph_row *row;
@@ -13492,6 +13490,8 @@ redisplay_tab_bar (struct frame *f)
           WINDOW_TOTAL_LINES (w) == 0))
     return false;
 
+  f->tab_bar_redisplayed = true;
+
   /* Set up an iterator for the tab-bar window.  */
   init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TAB_BAR_FACE_ID);
   it.first_visible_x = 0;
@@ -14402,21 +14402,13 @@ PIXELWISE non-nil means return the height of the tool 
bar in pixels.  */)
   return make_fixnum (height);
 }
 
+#ifndef HAVE_EXT_TOOL_BAR
 
-/* Display the tool-bar of frame F.  Value is true if tool-bar's
-   height should be changed.  */
+/* Display the internal tool-bar of frame F.  Value is true if
+   tool-bar's height should be changed.  */
 static bool
 redisplay_tool_bar (struct frame *f)
 {
-  f->tool_bar_redisplayed = true;
-#ifdef HAVE_EXT_TOOL_BAR
-
-  if (FRAME_EXTERNAL_TOOL_BAR (f))
-    update_frame_tool_bar (f);
-  return false;
-
-#else /* ! (HAVE_EXT_TOOL_BAR) */
-
   struct window *w;
   struct it it;
   struct glyph_row *row;
@@ -14430,6 +14422,8 @@ redisplay_tool_bar (struct frame *f)
           WINDOW_TOTAL_LINES (w) == 0))
     return false;
 
+  f->tool_bar_redisplayed = true;
+
   /* Set up an iterator for the tool-bar window.  */
   init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TOOL_BAR_FACE_ID);
   it.first_visible_x = 0;
@@ -14565,13 +14559,10 @@ redisplay_tool_bar (struct frame *f)
     }
 
   f->minimize_tool_bar_window_p = false;
-  return false;
 
-#endif /* HAVE_EXT_TOOL_BAR */
+  return false;
 }
 
-#ifndef HAVE_EXT_TOOL_BAR
-
 /* Get information about the tool-bar item which is displayed in GLYPH
    on frame F.  Return in *PROP_IDX the index where tool-bar item
    properties start in F->tool_bar_items.  Value is false if
@@ -19334,7 +19325,7 @@ redisplay_window (Lisp_Object window, bool 
just_this_one_p)
 
 #ifdef HAVE_EXT_TOOL_BAR
          if (FRAME_EXTERNAL_TOOL_BAR (f))
-           redisplay_tool_bar (f);
+           update_frame_tool_bar (f);
 #else
          if (WINDOWP (f->tool_bar_window)
              && (FRAME_TOOL_BAR_LINES (f) > 0
diff --git a/src/xfns.c b/src/xfns.c
index f120653..2c95065 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1563,7 +1563,6 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, 
Lisp_Object oldval)
 #else /* not USE_X_TOOLKIT && not USE_GTK */
   FRAME_MENU_BAR_LINES (f) = nlines;
   FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
-  adjust_frame_size (f, -1, -1, 2, true, Qx_set_menu_bar_lines);
   if (FRAME_X_WINDOW (f))
     x_clear_under_internal_border (f);
 
@@ -1577,6 +1576,8 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, 
Lisp_Object oldval)
       int width = FRAME_PIXEL_WIDTH (f);
       int y;
 
+      adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
+
       /* height can be zero here. */
       if (FRAME_X_WINDOW (f) && height > 0 && width > 0)
        {
@@ -1637,7 +1638,7 @@ x_change_tab_bar_height (struct frame *f, int height)
   int unit = FRAME_LINE_HEIGHT (f);
   int old_height = FRAME_TAB_BAR_HEIGHT (f);
   int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -1645,16 +1646,8 @@ x_change_tab_bar_height (struct frame *f, int height)
   /* Recalculate tab bar and frame text sizes.  */
   FRAME_TAB_BAR_HEIGHT (f) = height;
   FRAME_TAB_BAR_LINES (f) = lines;
-  /* Store the `tab-bar-lines' and `height' frame parameters.  */
   store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
-  store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
-
-  /* We also have to make sure that the internal border at the top of
-     the frame, below the menu bar or tab bar, is redrawn when the
-     tab bar disappears.  This is so because the internal border is
-     below the tab bar if one is displayed, but is below the menu bar
-     if there isn't a tab bar.  The tab bar draws into the area
-     below the menu bar.  */
+
   if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
     {
       clear_frame (f);
@@ -1664,25 +1657,21 @@ x_change_tab_bar_height (struct frame *f, int height)
   if ((height < old_height) && WINDOWP (f->tab_bar_window))
     clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
 
-  /* Recalculate tabbar height.  */
-  f->n_tab_bar_rows = 0;
-  if (old_height == 0
-      && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
-    f->tab_bar_redisplayed = f->tab_bar_resized = false;
-
-  adjust_frame_size (f, -1, -1,
-                    ((!f->tab_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4),
-                    false, Qtab_bar_lines);
-
-  f->tab_bar_resized = f->tab_bar_redisplayed;
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtab_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
 
   /* adjust_frame_size might not have done anything, garbage frame
      here.  */
@@ -1743,24 +1732,15 @@ x_change_tool_bar_height (struct frame *f, int height)
   int unit = FRAME_LINE_HEIGHT (f);
   int old_height = FRAME_TOOL_BAR_HEIGHT (f);
   int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
 
-  /* Recalculate tool bar and frame text sizes.  */
   FRAME_TOOL_BAR_HEIGHT (f) = height;
   FRAME_TOOL_BAR_LINES (f) = lines;
-  /* Store the `tool-bar-lines' and `height' frame parameters.  */
   store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
-  store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
-
-  /* We also have to make sure that the internal border at the top of
-     the frame, below the menu bar or tool bar, is redrawn when the
-     tool bar disappears.  This is so because the internal border is
-     below the tool bar if one is displayed, but is below the menu bar
-     if there isn't a tool bar.  The tool bar draws into the area
-     below the menu bar.  */
+
   if (FRAME_X_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
     {
       clear_frame (f);
@@ -1770,25 +1750,21 @@ x_change_tool_bar_height (struct frame *f, int height)
   if ((height < old_height) && WINDOWP (f->tool_bar_window))
     clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
 
-  /* Recalculate toolbar height.  */
-  f->n_tool_bar_rows = 0;
-  if (old_height == 0
-      && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             && NILP (Fmemq (Qtool_bar_lines, frame_inhibit_implied_resize)))))
-    f->tool_bar_redisplayed = f->tool_bar_resized = false;
-
-  adjust_frame_size (f, -1, -1,
-                    ((!f->tool_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4),
-                    false, Qtool_bar_lines);
-
-  f->tool_bar_resized = f->tool_bar_redisplayed;
+  if (!f->tool_bar_resized)
+    {
+      /* As long as tool_bar_resized is false, effectively try to change
+        F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+       adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                          1, false, Qtool_bar_lines);
+      else
+       adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+      f->tool_bar_resized =  f->tool_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
 
   /* adjust_frame_size might not have done anything, garbage frame
      here.  */
@@ -3687,7 +3663,6 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
   struct x_display_info *dpyinfo = NULL;
   Lisp_Object parent, parent_frame;
   struct kboard *kb;
-  int x_width = 0, x_height = 0;
 
   parms = Fcopy_alist (parms);
 
@@ -3999,18 +3974,6 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
      init_iterator with a null face cache, which should not happen.  */
   init_frame_faces (f);
 
-  /* We have to call adjust_frame_size here since otherwise
-     x_set_tool_bar_lines will already work with the character sizes
-     installed by init_frame_faces while the frame's pixel size is still
-     calculated from a character size of 1 and we subsequently hit the
-     (height >= 0) assertion in window_box_height.
-
-     The non-pixelwise code apparently worked around this because it
-     had one frame line vs one toolbar line which left us with a zero
-     root window height which was obviously wrong as well ...
-
-     Also process `min-width' and `min-height' parameters right here
-     because `frame-windows-min-size' needs them.  */
   tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
                              RES_TYPE_NUMBER);
   if (FIXNUMP (tem))
@@ -4019,6 +3982,7 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
                              RES_TYPE_NUMBER);
   if (FIXNUMP (tem))
     store_frame_param (f, Qmin_height, tem);
+
   adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
                     FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true,
                     Qx_create_frame_1);
@@ -4055,8 +4019,7 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
                          RES_TYPE_BOOLEAN);
 
   /* Compute the size of the X window.  */
-  window_prompting = gui_figure_window_size (f, parms, true, true,
-                                             &x_width, &x_height);
+  window_prompting = gui_figure_window_size (f, parms, true, true);
 
   tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
                              RES_TYPE_BOOLEAN);
@@ -4140,11 +4103,6 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
   /* Consider frame official, now.  */
   f->can_set_window_size = true;
 
-  if (x_width > 0)
-    SET_FRAME_WIDTH (f, x_width);
-  if (x_height > 0)
-    SET_FRAME_HEIGHT (f, x_height);
-
   /* Tell the server what size and position, etc, we want, and how
      badly we want them.  This should be done after we have the menu
      bar so that its size can be taken into account.  */
@@ -6291,10 +6249,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
   struct frame *f;
   Lisp_Object frame;
   Lisp_Object name;
-  int width, height;
   ptrdiff_t count = SPECPDL_INDEX ();
   bool face_change_before = face_change;
-  int x_width = 0, x_height = 0;
 
   if (!dpyinfo->terminal->name)
     error ("Terminal is not live, can't create new frames on it");
@@ -6418,7 +6374,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
   gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
                          "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
 
-  /* This defaults to 2 in order to match xterm.  We recognize either
+  /* This defaults to 1 in order to match xterm.  We recognize either
      internalBorderWidth or internalBorder (which is what xterm calls
      it).  */
   if (NILP (Fassq (Qinternal_border_width, parms)))
@@ -6466,7 +6422,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
                          "inhibitDoubleBuffering", "InhibitDoubleBuffering",
                          RES_TYPE_BOOLEAN);
 
-  gui_figure_window_size (f, parms, false, false, &x_width, &x_height);
+  gui_figure_window_size (f, parms, false, false);
 
   {
     XSetWindowAttributes attrs;
@@ -6518,15 +6474,6 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
   gui_default_parameter (f, parms, Qalpha, Qnil,
                          "alpha", "Alpha", RES_TYPE_NUMBER);
 
-  /* Dimensions, especially FRAME_LINES (f), must be done via 
change_frame_size.
-     Change will not be effected unless different from the current
-     FRAME_LINES (f).  */
-  width = FRAME_COLS (f);
-  height = FRAME_LINES (f);
-  SET_FRAME_COLS (f, 0);
-  SET_FRAME_LINES (f, 0);
-  change_frame_size (f, width, height, true, false, false, false);
-
   /* Add `tooltip' frame parameter's default value. */
   if (NILP (Fframe_parameter (frame, Qtooltip)))
     {
@@ -6588,6 +6535,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
      visible won't work.  */
   Vframe_list = Fcons (frame, Vframe_list);
   f->can_set_window_size = true;
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qtip_frame);
 
   /* Setting attributes of faces of the tooltip frame from resources
      and similar will set face_change, which leads to the clearing of
diff --git a/src/xmenu.c b/src/xmenu.c
index a83fffb..a6762236 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -641,7 +641,7 @@ update_frame_menubar (struct frame *f)
   lw_refigure_widget (x->column_widget, True);
 
   /* Force the pane widget to resize itself.  */
-  adjust_frame_size (f, -1, -1, 2, false, Qupdate_frame_menubar);
+  adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
   unblock_input ();
 #endif /* USE_GTK */
 }
@@ -1044,6 +1044,7 @@ free_frame_menubar (struct frame *f)
   /* Motif automatically shrinks the frame in lw_destroy_all_widgets.
      If we want to preserve the old height, calculate it now so we can
      restore it below.  */
+  int old_width = FRAME_TEXT_WIDTH (f);
   int old_height = FRAME_TEXT_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f);
 #endif
 
@@ -1077,26 +1078,43 @@ free_frame_menubar (struct frame *f)
       lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
       f->output_data.x->menubar_widget = NULL;
 
+      /* When double-buffering is enabled and the frame shall not be
+        resized either because resizing is inhibited or the frame is
+        fullheight, some (usually harmless) display artifacts like a
+        doubled mode line may show up.  Sometimes the configuration
+        gets messed up in a more serious fashion though and you may
+        have to resize the frame to get it back in a normal state.  */
       if (f->output_data.x->widget)
        {
 #ifdef USE_MOTIF
          XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL);
          if (x1 == 0 && y1 == 0)
            XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
-         if (frame_inhibit_resize (f, false, Qmenu_bar_lines))
-           adjust_frame_size (f, -1, old_height, 1, false, 
Qfree_frame_menubar_1);
+         /* When resizing is inhibited and a normal Motif frame is not
+            fullheight, we have to explicitly request its old sizes
+            here since otherwise turning off the menu bar will shrink
+            the frame but turning them on again will not resize it
+            back.  For a fullheight frame we let the window manager
+            deal with this problem.  */
+         if (frame_inhibit_resize (f, false, Qmenu_bar_lines)
+             && !EQ (get_frame_param (f, Qfullscreen), Qfullheight))
+           adjust_frame_size (f, old_width, old_height, 1, false,
+                              Qmenu_bar_lines);
          else
-           adjust_frame_size (f, -1, -1, 2, false, Qfree_frame_menubar_1);
+           adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
 #else
-         adjust_frame_size (f, -1, -1, 2, false, Qfree_frame_menubar_1);
+         adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
 #endif /* USE_MOTIF */
        }
       else
        {
 #ifdef USE_MOTIF
          if (WINDOWP (FRAME_ROOT_WINDOW (f))
-             && frame_inhibit_resize (f, false, Qmenu_bar_lines))
-           adjust_frame_size (f, -1, old_height, 1, false, 
Qfree_frame_menubar_2);
+             /* See comment above.  */
+             && frame_inhibit_resize (f, false, Qmenu_bar_lines)
+             && !EQ (get_frame_param (f, Qfullscreen), Qfullheight))
+           adjust_frame_size (f, old_width, old_height, 1, false,
+                              Qmenu_bar_lines);
 #endif
        }
 
diff --git a/src/xterm.c b/src/xterm.c
index 744b80c..5049f72 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -6223,7 +6223,7 @@ x_create_toolkit_scroll_bar (struct frame *f, struct 
scroll_bar *bar)
     /* But only if we have a small colormap.  Xaw3d can allocate nice
        colors itself.  */
     {
-      XtSetArg (av[ac], XtNbeNiceToColormap,
+      XtSetArg (av[ac], (String) XtNbeNiceToColormap,
                 DefaultDepthOfScreen (FRAME_X_SCREEN (f)) < 16);
       ++ac;
     }
@@ -6234,20 +6234,20 @@ x_create_toolkit_scroll_bar (struct frame *f, struct 
scroll_bar *bar)
     {
       /* This tells Xaw3d to use real colors instead of dithering for
         the shadows.  */
-      XtSetArg (av[ac], XtNbeNiceToColormap, False);
+      XtSetArg (av[ac], (String) XtNbeNiceToColormap, False);
       ++ac;
 
       /* Specify the colors.  */
       pixel = f->output_data.x->scroll_bar_top_shadow_pixel;
       if (pixel != -1)
        {
-         XtSetArg (av[ac], XtNtopShadowPixel, pixel);
+         XtSetArg (av[ac], (String) XtNtopShadowPixel, pixel);
          ++ac;
        }
       pixel = f->output_data.x->scroll_bar_bottom_shadow_pixel;
       if (pixel != -1)
        {
-         XtSetArg (av[ac], XtNbottomShadowPixel, pixel);
+         XtSetArg (av[ac], (String) XtNbottomShadowPixel, pixel);
          ++ac;
        }
     }
@@ -6424,7 +6424,7 @@ x_create_horizontal_toolkit_scroll_bar (struct frame *f, 
struct scroll_bar *bar)
     /* But only if we have a small colormap.  Xaw3d can allocate nice
        colors itself.  */
     {
-      XtSetArg (av[ac], XtNbeNiceToColormap,
+      XtSetArg (av[ac], (String) XtNbeNiceToColormap,
                 DefaultDepthOfScreen (FRAME_X_SCREEN (f)) < 16);
       ++ac;
     }
@@ -6435,20 +6435,20 @@ x_create_horizontal_toolkit_scroll_bar (struct frame 
*f, struct scroll_bar *bar)
     {
       /* This tells Xaw3d to use real colors instead of dithering for
         the shadows.  */
-      XtSetArg (av[ac], XtNbeNiceToColormap, False);
+      XtSetArg (av[ac], (String) XtNbeNiceToColormap, False);
       ++ac;
 
       /* Specify the colors.  */
       pixel = f->output_data.x->scroll_bar_top_shadow_pixel;
       if (pixel != -1)
        {
-         XtSetArg (av[ac], XtNtopShadowPixel, pixel);
+         XtSetArg (av[ac], (String) XtNtopShadowPixel, pixel);
          ++ac;
        }
       pixel = f->output_data.x->scroll_bar_bottom_shadow_pixel;
       if (pixel != -1)
        {
-         XtSetArg (av[ac], XtNbottomShadowPixel, pixel);
+         XtSetArg (av[ac], (String) XtNbottomShadowPixel, pixel);
          ++ac;
        }
     }
@@ -7833,10 +7833,6 @@ x_net_wm_state (struct frame *f, Window window)
       break;
     }
 
-  frame_size_history_add
-    (f, Qx_net_wm_state, 0, 0,
-     list2 (get_frame_param (f, Qfullscreen), lval));
-
   store_frame_param (f, Qfullscreen, lval);
 /**   store_frame_param (f, Qsticky, sticky ? Qt : Qnil); **/
 }
@@ -8167,19 +8163,29 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       if (f && event->xproperty.atom == dpyinfo->Xatom_net_wm_state)
        {
           bool not_hidden = x_handle_net_wm_state (f, &event->xproperty);
+
          if (not_hidden && FRAME_ICONIFIED_P (f))
            {
+             if (CONSP (frame_size_history))
+               frame_size_history_plain
+                 (f, build_string ("PropertyNotify, not hidden & iconified"));
+
              /* Gnome shell does not iconify us when C-z is pressed.
                 It hides the frame.  So if our state says we aren't
                 hidden anymore, treat it as deiconified.  */
              SET_FRAME_VISIBLE (f, 1);
              SET_FRAME_ICONIFIED (f, false);
+
              f->output_data.x->has_been_visible = true;
              inev.ie.kind = DEICONIFY_EVENT;
              XSETFRAME (inev.ie.frame_or_window, f);
            }
-         else if (! not_hidden && ! FRAME_ICONIFIED_P (f))
+         else if (!not_hidden && !FRAME_ICONIFIED_P (f))
            {
+             if (CONSP (frame_size_history))
+               frame_size_history_plain
+                 (f, build_string ("PropertyNotify, hidden & not iconified"));
+
              SET_FRAME_VISIBLE (f, 0);
              SET_FRAME_ICONIFIED (f, true);
              inev.ie.kind = ICONIFY_EVENT;
@@ -8357,10 +8363,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              and that way, we know the window is not iconified now.  */
           if (visible || FRAME_ICONIFIED_P (f))
             {
+             if (CONSP (frame_size_history))
+               frame_size_history_plain
+                 (f, build_string ("UnmapNotify, visible | iconified"));
+
               SET_FRAME_ICONIFIED (f, true);
-              inev.ie.kind = ICONIFY_EVENT;
+             inev.ie.kind = ICONIFY_EVENT;
               XSETFRAME (inev.ie.frame_or_window, f);
             }
+         else if (CONSP (frame_size_history))
+           frame_size_history_plain
+             (f, build_string ("UnmapNotify, not visible & not iconified"));
         }
       goto OTHER;
 
@@ -8372,8 +8385,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       if (f)
         {
          bool iconified = FRAME_ICONIFIED_P (f);
-
-          /* Check if fullscreen was specified before we where mapped the
+         int value;
+         bool sticky;
+          bool not_hidden = x_get_current_wm_state (f, event->xmap.window, 
&value, &sticky);
+
+         if (CONSP (frame_size_history))
+           frame_size_history_extra
+             (f,
+              iconified
+              ? (not_hidden
+                 ? build_string ("MapNotify, not hidden & iconified")
+                 : build_string ("MapNotify, hidden & iconified"))
+              : (not_hidden
+                 ? build_string ("MapNotify, not hidden & not iconified")
+                 : build_string ("MapNotify, hidden & not iconified")),
+              FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+              -1, -1, f->new_width, f->new_height);
+
+         /* Check if fullscreen was specified before we where mapped the
              first time, i.e. from the command line.  */
           if (!f->output_data.x->has_been_visible)
            {
@@ -8974,7 +9003,16 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              || !(configureEvent.xconfigure.width <= 1
                   && configureEvent.xconfigure.height <= 1)))
         {
-          block_input ();
+
+         if (CONSP (frame_size_history))
+           frame_size_history_extra
+             (f, build_string ("ConfigureNotify"),
+              FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+              configureEvent.xconfigure.width,
+              configureEvent.xconfigure.height,
+              f->new_width, f->new_height);
+
+         block_input ();
           if (FRAME_X_DOUBLE_BUFFERED_P (f))
             font_drop_xrender_surfaces (f);
           unblock_input ();
@@ -9015,24 +9053,28 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
 #ifndef USE_X_TOOLKIT
 #ifndef USE_GTK
-          int width =
-           FRAME_PIXEL_TO_TEXT_WIDTH (f, configureEvent.xconfigure.width);
-          int height =
-           FRAME_PIXEL_TO_TEXT_HEIGHT (f, configureEvent.xconfigure.height);
+          int width = configureEvent.xconfigure.width;
+          int height = configureEvent.xconfigure.height;
+
+         if (CONSP (frame_size_history))
+           frame_size_history_extra
+             (f, build_string ("ConfigureNotify"),
+              FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+              width, height, f->new_width, f->new_height);
 
-          /* In the toolkit version, change_frame_size
+         /* In the toolkit version, change_frame_size
              is called by the code that handles resizing
              of the EmacsFrame widget.  */
 
           /* Even if the number of character rows and columns has
              not changed, the font size may have changed, so we need
              to check the pixel dimensions as well.  */
-          if (width != FRAME_TEXT_WIDTH (f)
-              || height != FRAME_TEXT_HEIGHT (f)
-              || configureEvent.xconfigure.width != FRAME_PIXEL_WIDTH (f)
-              || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f))
+          if (width != FRAME_PIXEL_WIDTH (f)
+              || height != FRAME_PIXEL_HEIGHT (f)
+             || (delayed_size_change
+                 && (width != f->new_width || height != f->new_height)))
             {
-              change_frame_size (f, width, height, false, true, false, true);
+              change_frame_size (f, width, height, false, true, false);
               x_clear_under_internal_border (f);
               SET_FRAME_GARBAGED (f);
               cancel_mouse_face (f);
@@ -10217,11 +10259,6 @@ x_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
 {
   struct font *font = XFONT_OBJECT (font_object);
   int unit, font_ascent, font_descent;
-#ifndef USE_X_TOOLKIT
-  int old_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
-  int old_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
-  Lisp_Object fullscreen;
-#endif
 
   if (fontset < 0)
     fontset = fontset_from_font (font_object);
@@ -10239,8 +10276,9 @@ x_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
 
 #ifndef USE_X_TOOLKIT
   FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
-  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
 #endif
+  /* We could use a more elaborate calculation here.  */
+  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
 
   /* Compute character columns occupied by scrollbar.
 
@@ -10253,34 +10291,14 @@ x_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
   else
     FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
 
-  if (FRAME_X_WINDOW (f) != 0)
-    {
-      /* Don't change the size of a tip frame; there's no point in
-        doing it because it's done in Fx_show_tip, and it leads to
-        problems because the tip frame has no widget.  */
-      if (!FRAME_TOOLTIP_P (f))
-       {
-         adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
-                            FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
-                            false, Qfont);
-#ifndef USE_X_TOOLKIT
-         if ((FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height
-              || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height)
-             && !f->after_make_frame
-             && (EQ (frame_inhibit_implied_resize, Qt)
-                 || (CONSP (frame_inhibit_implied_resize)
-                     && NILP (Fmemq (Qfont, frame_inhibit_implied_resize))))
-             && (NILP (fullscreen = get_frame_param (f, Qfullscreen))
-                 || EQ (fullscreen, Qfullwidth)))
-           /* If the menu/tab bar height changes, try to keep text height
-              constant.  */
-           adjust_frame_size
-             (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f)
-              + FRAME_TAB_BAR_HEIGHT (f)
-              - old_menu_bar_height - old_tab_bar_height, 1, false, Qfont);
-#endif /* USE_X_TOOLKIT  */
-       }
-    }
+
+  /* Don't change the size of a tip frame; there's no point in doing it
+     because it's done in Fx_show_tip, and it leads to problems because
+     the tip frame has no widget.  */
+  if (FRAME_X_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
+    adjust_frame_size
+      (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+       FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, false, Qfont);
 
 #ifdef HAVE_X_I18N
   if (FRAME_XIC (f)
@@ -11164,10 +11182,6 @@ x_handle_net_wm_state (struct frame *f, const 
XPropertyEvent *event)
       break;
     }
 
-  frame_size_history_add
-    (f, Qx_handle_net_wm_state, 0, 0,
-     list2 (get_frame_param (f, Qfullscreen), lval));
-
   store_frame_param (f, Qfullscreen, lval);
   store_frame_param (f, Qsticky, sticky ? Qt : Qnil);
 
@@ -11222,9 +11236,6 @@ x_check_fullscreen (struct frame *f)
          emacs_abort ();
         }
 
-      frame_size_history_add
-       (f, Qx_check_fullscreen, width, height, Qnil);
-
       x_wm_set_size_hint (f, 0, false);
 
       XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
@@ -11234,8 +11245,7 @@ x_check_fullscreen (struct frame *f)
        x_wait_for_event (f, ConfigureNotify);
       else
        {
-         change_frame_size (f, width, height - FRAME_MENUBAR_HEIGHT (f),
-                            false, true, false, true);
+         change_frame_size (f, width, height, false, true, false);
          x_sync (f);
        }
     }
@@ -11389,57 +11399,12 @@ static void
 x_set_window_size_1 (struct frame *f, bool change_gravity,
                     int width, int height)
 {
-  int pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
-  int pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
-  int old_width = FRAME_PIXEL_WIDTH (f);
-  int old_height = FRAME_PIXEL_HEIGHT (f);
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
-
   if (change_gravity)
     f->win_gravity = NorthWestGravity;
   x_wm_set_size_hint (f, 0, false);
 
-  /* When the frame is fullheight and we only want to change the width
-     or it is fullwidth and we only want to change the height we should
-     be able to preserve the fullscreen property.  However, due to the
-     fact that we have to send a resize request anyway, the window
-     manager will abolish it.  At least the respective size should
-     remain unchanged but giving the frame back its normal size will
-     be broken ... */
-  if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
-    {
-      frame_size_history_add
-       (f, Qx_set_window_size_1, width, height,
-        list2i (old_height, pixelheight + FRAME_MENUBAR_HEIGHT (f)));
-
-      XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                    old_width, pixelheight + FRAME_MENUBAR_HEIGHT (f));
-    }
-  else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
-    {
-      frame_size_history_add
-       (f, Qx_set_window_size_2, width, height,
-        list2i (old_width, pixelwidth));
-
-      XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                    pixelwidth, old_height);
-    }
-
-  else
-    {
-      frame_size_history_add
-       (f, Qx_set_window_size_3, width, height,
-        list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f),
-                (pixelheight + FRAME_TOOLBAR_HEIGHT (f)
-                 + FRAME_MENUBAR_HEIGHT (f)),
-                FRAME_MENUBAR_HEIGHT (f)));
-
-      XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                    pixelwidth, pixelheight + FRAME_MENUBAR_HEIGHT (f));
-      fullscreen = Qnil;
-    }
-
-
+  XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                width, height + FRAME_MENUBAR_HEIGHT (f));
 
   /* We've set {FRAME,PIXEL}_{WIDTH,HEIGHT} to the values we hope to
      receive in the ConfigureNotify event; if we get what we asked
@@ -11468,66 +11433,42 @@ x_set_window_size_1 (struct frame *f, bool 
change_gravity,
     {
       x_wait_for_event (f, ConfigureNotify);
 
-      if (!NILP (fullscreen))
-       /* Try to restore fullscreen state.  */
-       {
-         store_frame_param (f, Qfullscreen, fullscreen);
-         gui_set_fullscreen (f, fullscreen, fullscreen);
-       }
+      if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("x_set_window_size_1, visible"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          f->new_width, f->new_height);
     }
   else
     {
-      change_frame_size (f, width, height, false, true, false, true);
+      if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("x_set_window_size_1, invisible"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          f->new_width, f->new_height);
+
+      /* Call adjust_frame_size right away as with GTK.  It might be
+        tempting to clear out f->new_width and f->new_height here.  */
+      adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+                        FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
+                        5, 0, Qx_set_window_size_1);
+
       x_sync (f);
     }
 }
 
 
-/* Call this to change the size of frame F's x-window.
-   If CHANGE_GRAVITY, change to top-left-corner window gravity
-   for this size change and subsequent size changes.
-   Otherwise we leave the window gravity unchanged.  */
+/* Change the size of frame F's X window to WIDTH and HEIGHT pixels.  If
+   CHANGE_GRAVITY, change to top-left-corner window gravity for this
+   size change and subsequent size changes.  Otherwise we leave the
+   window gravity unchanged.  */
 
 void
 x_set_window_size (struct frame *f, bool change_gravity,
-                  int width, int height, bool pixelwise)
+                  int width, int height)
 {
   block_input ();
 
-  /* The following breaks our calculations.  If it's really needed,
-     think of something else.  */
-#if false
-  if (!FRAME_TOOLTIP_P (f))
-    {
-      int text_width, text_height;
-
-      /* When the frame is maximized/fullscreen or running under for
-         example Xmonad, x_set_window_size_1 will be a no-op.
-         In that case, the right thing to do is extend rows/width to
-         the current frame size.  We do that first if x_set_window_size_1
-         turns out to not be a no-op (there is no way to know).
-         The size will be adjusted again if the frame gets a
-         ConfigureNotify event as a result of x_set_window_size.  */
-      int pixelh = FRAME_PIXEL_HEIGHT (f);
-#ifdef USE_X_TOOLKIT
-      /* The menu bar is not part of text lines.  The tool bar
-         is however.  */
-      pixelh -= FRAME_MENUBAR_HEIGHT (f);
-#endif
-      text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, FRAME_PIXEL_WIDTH (f));
-      text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelh);
-
-      change_frame_size (f, text_width, text_height, false, true, false, true);
-    }
-#endif
-
-  /* Pixelize width and height, if necessary.  */
-  if (! pixelwise)
-    {
-      width = width * FRAME_COLUMN_WIDTH (f);
-      height = height * FRAME_LINE_HEIGHT (f);
-    }
-
 #ifdef USE_GTK
   if (FRAME_GTK_WIDGET (f))
     xg_frame_set_char_size (f, width, height);
@@ -11880,6 +11821,11 @@ x_make_frame_visible (struct frame *f)
     poll_for_input_1 ();
     poll_suppress_count = old_poll_suppress_count;
 #endif
+
+    if (CONSP (frame_size_history))
+      frame_size_history_plain
+       (f, build_string ("x_make_frame_visible"));
+
     if (! FRAME_VISIBLE_P (f))
       x_wait_for_event (f, MapNotify);
   }
@@ -11937,6 +11883,10 @@ x_make_frame_invisible (struct frame *f)
   SET_FRAME_VISIBLE (f, 0);
   SET_FRAME_ICONIFIED (f, false);
 
+  if (CONSP (frame_size_history))
+    frame_size_history_plain
+      (f, build_string ("x_make_frame_invisible"));
+
   unblock_input ();
 }
 
diff --git a/src/xterm.h b/src/xterm.h
index ebc42b7..de6ea50 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1079,7 +1079,7 @@ extern bool x_had_errors_p (Display *);
 extern void x_uncatch_errors (void);
 extern void x_uncatch_errors_after_check (void);
 extern void x_clear_errors (Display *);
-extern void x_set_window_size (struct frame *f, bool, int, int, bool);
+extern void x_set_window_size (struct frame *f, bool, int, int);
 extern void x_make_frame_visible (struct frame *f);
 extern void x_make_frame_invisible (struct frame *f);
 extern void x_iconify_frame (struct frame *f);
diff --git a/test/Makefile.in b/test/Makefile.in
index 91a8ea1..84ab4e7 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -47,6 +47,8 @@ SO = @MODULES_SUFFIX@
 
 SEPCHAR = @SEPCHAR@
 
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
+
 -include ${top_builddir}/src/verbose.mk
 
 # Load any GNU ELPA dependencies that are present, for optional tests.
@@ -118,6 +120,8 @@ emacs = LANG=C EMACSLOADPATH=                  \
 # Set HOME to a nonexistent directory to prevent tests from accessing
 # it accidentally (e.g., popping up a gnupg dialog if ~/.authinfo.gpg
 # exists, or writing to ~/.bzr.log when running bzr commands).
+# NOTE if the '/nonexistent' name is changed `normal-top-level' in
+# startup.el must be updated too.
 TEST_HOME = /nonexistent
 
 test_module_dir := src/emacs-module-resources
@@ -126,9 +130,15 @@ test_module_dir := src/emacs-module-resources
 
 all: check
 
+ifeq ($(HAVE_NATIVE_COMP),yes)
 SELECTOR_DEFAULT = (not (or (tag :expensive-test) (tag :unstable)))
 SELECTOR_EXPENSIVE = (not (tag :unstable))
 SELECTOR_ALL = t
+else
+SELECTOR_DEFAULT = (not (or (tag :expensive-test) (tag :unstable) (tag 
:nativecomp)))
+SELECTOR_EXPENSIVE = (not (or (tag :unstable) (tag :nativecomp)))
+SELECTOR_ALL = (not (tag :nativecomp))
+endif
 ifdef SELECTOR
 SELECTOR_ACTUAL=$(SELECTOR)
 else ifndef MAKECMDGOALS
diff --git a/test/infra/gitlab-ci.yml b/test/infra/gitlab-ci.yml
index 6355513..4023437 100644
--- a/test/infra/gitlab-ci.yml
+++ b/test/infra/gitlab-ci.yml
@@ -238,14 +238,48 @@ test-lisp-net-inotify:
 test-filenotify-gio:
   # This tests file monitor libraries gfilemonitor and gio.
   stage: platforms
+  needs: [build-image-filenotify-gio]
   extends: [.job-template, .test-template, .filenotify-gio-template]
   variables:
     target: emacs-filenotify-gio
     make_params: "-k -C test autorevert-tests.log filenotify-tests.log"
 
+build-native-bootstrap-speed0:
+  # Test a full native bootstrap
+  # Run for now only speed 0 to limit memory usage and compilation time.
+  stage: slow
+  # Uncomment the following to run it only when scheduled.
+  # only:
+  #   - schedules
+  script:
+    - DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y 
-qq -o=Dpkg::Use-Pty=0 libgccjit-6-dev
+    - ./autogen.sh autoconf
+    - ./configure --with-nativecomp
+    - make bootstrap NATIVE_FULL_AOT=1 BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq 
comp-speed 0)"' -j2
+  timeout: 8 hours
+
+build-native-bootstrap-speed1:
+  stage: slow
+  script:
+    - DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y 
-qq -o=Dpkg::Use-Pty=0 libgccjit-6-dev
+    - ./autogen.sh autoconf
+    - ./configure --with-nativecomp
+    - make bootstrap BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq comp-speed 1)"'
+  timeout: 8 hours
+
+build-native-bootstrap-speed2:
+  stage: slow
+  script:
+    - DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y 
-qq -o=Dpkg::Use-Pty=0 libgccjit-6-dev
+    - ./autogen.sh autoconf
+    - ./configure --with-nativecomp
+    - make bootstrap
+  timeout: 8 hours
+
 test-gnustep:
   # This tests the GNUstep build process
   stage: platforms
+  needs: [build-image-gnustep]
   extends: [.job-template, .gnustep-template]
   variables:
     target: emacs-gnustep
@@ -262,3 +296,7 @@ test-all-inotify:
   variables:
     target: emacs-inotify
     make_params: check-expensive
+
+# Local Variables:
+# add-log-current-defun-header-regexp: "^\\([-_.[:alnum:]]+\\)[ \t]*:"
+# End:
diff --git a/test/lisp/auth-source-tests.el b/test/lisp/auth-source-tests.el
index 4f0d994..1c4bd8d 100644
--- a/test/lisp/auth-source-tests.el
+++ b/test/lisp/auth-source-tests.el
@@ -320,7 +320,9 @@
     ;; Redefine `read-*' in order to avoid interactive input.
     (cl-letf (((symbol-function 'read-passwd) (lambda (_) passwd))
               ((symbol-function 'read-string)
-               (lambda (_prompt _initial _history default) default)))
+               (lambda (_prompt &optional _initial _history default
+                                _inherit-input-method)
+                 default)))
       (setq auth-info
             (car (auth-source-search
                   :max 1 :host host :require '(:user :secret) :create t))))
diff --git a/test/lisp/emacs-lisp/comp-cstr-tests.el 
b/test/lisp/emacs-lisp/comp-cstr-tests.el
new file mode 100644
index 0000000..2e46285
--- /dev/null
+++ b/test/lisp/emacs-lisp/comp-cstr-tests.el
@@ -0,0 +1,229 @@
+;;; comp-cstr-tests.el --- unit tests for src/comp.c -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for lisp/emacs-lisp/comp-cstr.el
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'comp-cstr)
+
+(cl-eval-when (compile eval load)
+
+  (defun comp-cstr-test-ts (type-spec)
+    "Create a constraint from TYPE-SPEC and convert it back to type specifier."
+    (let ((comp-ctxt (make-comp-cstr-ctxt)))
+      (comp-cstr-to-type-spec (comp-type-spec-to-cstr type-spec))))
+
+  (defun comp-cstr-typespec-test (number type-spec expected-type-spec)
+    `(ert-deftest ,(intern (concat "comp-cstr-test-" (int-to-string number))) 
()
+       (should (equal (comp-cstr-test-ts ',type-spec)
+                      ',expected-type-spec))))
+
+  (defconst comp-cstr-typespec-tests-alist
+    `(;; 1
+      (symbol . symbol)
+      ;; 2
+      ((or string array) . array)
+      ;; 3
+      ((or symbol number) . (or number symbol))
+      ;; 4
+      ((or cons atom) . (or atom cons)) ;; SBCL return T
+      ;; 5
+      ((or integer number) . number)
+      ;; 6
+      ((or (or integer symbol) number) . (or number symbol))
+      ;; 7
+      ((or (or integer symbol) (or number list)) . (or list number symbol))
+      ;; 8
+      ((or (or integer number) nil) . number)
+      ;; 9
+      ((member foo) . (member foo))
+      ;; 10
+      ((member foo bar) . (member bar foo))
+      ;; 11
+      ((or (member foo) (member bar)) . (member bar foo))
+      ;; 12
+      ((or (member foo) symbol) . symbol) ;; SBCL return (OR SYMBOL (MEMBER 
FOO))
+      ;; 13
+      ((or (member foo) number) .  (or (member foo) number))
+      ;; 14
+      ((or (integer 1 3) number) . number)
+      ;; 15
+      (integer . integer)
+      ;; 16
+      ((integer 1 2) . (integer 1 2))
+      ;; 17
+      ((or (integer -1  0) (integer 3  4)) . (or (integer -1  0) (integer 3  
4)))
+      ;; 18
+      ((or (integer -1  2) (integer 3  4)) . (integer -1 4))
+      ;; 19
+      ((or (integer -1  3) (integer 3  4)) . (integer -1 4))
+      ;; 20
+      ((or (integer -1  4) (integer 3  4)) . (integer -1 4))
+      ;; 21
+      ((or (integer -1  5) (integer 3  4)) . (integer -1 5))
+      ;; 22
+      ((or (integer -1  *) (integer 3  4)) . (integer -1 *))
+      ;; 23
+      ((or (integer -1  2) (integer *  4)) . (integer * 4))
+      ;; 24
+      ((and string array) . string)
+      ;; 25
+      ((and cons atom) . nil)
+      ;; 26
+      ((and (member foo) (member foo bar baz)) . (member foo))
+      ;; 27
+      ((and (member foo) (member bar)) . nil)
+      ;; 28
+      ((and (member foo) symbol) . (member foo))
+      ;; 29
+      ((and (member foo) string) . nil)
+      ;; 30
+      ((and (member foo) (integer 1 2)) . nil)
+      ;; 31
+      ((and (member 1 2) (member 3 2)) . (integer 2 2))
+      ;; 32
+      ((and number (integer 1 2)) . (integer 1 2))
+      ;; 33
+      ((and integer (integer 1 2)) . (integer 1 2))
+      ;; 34
+      ((and (integer -1 0) (integer 3 5)) . nil)
+      ;; 35
+      ((and (integer -1 2) (integer 3 5)) . nil)
+      ;; 36
+      ((and (integer -1 3) (integer 3 5)) . (integer 3 3))
+      ;; 37
+      ((and (integer -1 4) (integer 3 5)) . (integer 3 4))
+      ;; 38
+      ((and (integer -1 5) nil) . nil)
+      ;; 39
+      ((not symbol) . (not symbol))
+      ;; 40
+      ((or (member foo) (not (member foo bar))) . (not (member bar)))
+      ;; 41
+      ((or (member foo bar) (not (member foo))) . t)
+      ;; 42
+      ((or symbol (not sequence)) . (not sequence))
+      ;; 43
+      ((or symbol (not symbol)) . t)
+      ;; 44
+      ((or symbol (not sequence)) . (not sequence))
+      ;; 45 Conservative.
+      ((or vector (not sequence)) . t)
+      ;; 46
+      ((or (integer 1 10) (not (integer * 5))) . (not (integer * 0)))
+      ;; 47
+      ((or symbol (integer 1 10) (not (integer * 5))) . (not (integer * 0)))
+      ;; 48
+      ((or (not symbol) (integer 1 10) (not (integer * 5))) . (not (or symbol 
(integer * 0))))
+      ;; 49
+      ((or symbol (not (member foo))) . (not (member foo)))
+      ;; 50
+      ((or (not symbol) (not (member foo))) . (not symbol))
+      ;; 51 Conservative.
+      ((or (not (member foo)) string) . (not (member foo)))
+      ;; 52 Conservative.
+      ((or (member foo) (not string)) . (not string))
+      ;; 53
+      ((or (not (integer 1 2)) integer) . t)
+      ;; 54
+      ((or (not (integer 1 2)) (not integer)) . (not integer))
+      ;; 55
+      ((or (integer 1 2) (not integer)) . (not (or (integer * 0) (integer 3 
*))))
+      ;; 56
+      ((or number (not (integer 1 2))) . t)
+      ;; 57
+      ((or atom (not (integer 1 2))) . t)
+      ;; 58
+      ((or atom (not (member foo))) . t)
+      ;; 59
+      ((and symbol (not cons)) . symbol)
+      ;; 60
+      ((and symbol (not symbol)) . nil)
+      ;; 61
+      ((and atom (not symbol)) . atom)
+      ;; 62
+      ((and atom (not string)) . (or array sequence atom))
+      ;; 63 Conservative
+      ((and symbol (not (member foo))) . symbol)
+      ;; 64 Conservative
+      ((and symbol (not (member 3))) . symbol)
+      ;; 65
+      ((and (not (member foo)) (integer 1 10)) . (integer 1 10))
+      ;; 66
+      ((and (member foo) (not (integer 1 10))) . (member foo))
+      ;; 67
+      ((and t (not (member foo))) . (not (member foo)))
+      ;; 68
+      ((and integer (not (integer 3 4))) . (or (integer * 2) (integer 5 *)))
+      ;; 69
+      ((and (integer 0 20) (not (integer 5 10))) . (or (integer 0 4) (integer 
11 20)))
+      ;; 70
+      ((and (not (member a)) (not (member b))) . (not (member a b)))
+      ;; 71
+      ((and (not boolean) (not (member b))) . (not (or (member b) boolean)))
+      ;; 72
+      ((and t (integer 1 1)) . (integer 1 1))
+      ;; 73
+      ((not (integer -1 5)) . (not (integer -1 5)))
+      ;; 74
+      ((and boolean (or number marker)) . nil)
+      ;; 75
+      ((and atom (or number marker)) . (or marker number))
+      ;; 76
+      ((and symbol (or number marker)) . nil)
+      ;; 77
+      ((and (or symbol string) (or number marker)) . nil)
+      ;; 78
+      ((and t t) . t)
+      ;; 80
+      ((and (or marker number) (integer 0 0)) . (integer 0 0))
+      ;; 81
+      ((and t (not t)) . nil)
+      ;; 82
+      ((or (integer 1 1) (not (integer 1 1))) . t)
+      ;; 83
+      ((not t) . nil)
+      ;; 84
+      ((not nil) . t)
+      ;; 85
+      ((or (not string) t) . t)
+      ;; 86
+      ((or (not vector) sequence) . sequence)
+      ;; 87
+      ((or (not symbol) null) . t))
+    "Alist type specifier -> expected type specifier."))
+
+(defmacro comp-cstr-synthesize-tests ()
+  "Generate all tests from `comp-cstr-typespec-tests-alist'."
+  `(progn
+     ,@(cl-loop
+        for i from 1
+        for (ts . exp-ts) in comp-cstr-typespec-tests-alist
+        append (list (comp-cstr-typespec-test i ts exp-ts)))))
+
+(comp-cstr-synthesize-tests)
+
+;;; comp-cstr-tests.el ends here
diff --git a/test/lisp/help-fns-tests.el b/test/lisp/help-fns-tests.el
index 80d90da..099d627 100644
--- a/test/lisp/help-fns-tests.el
+++ b/test/lisp/help-fns-tests.el
@@ -62,12 +62,16 @@ Return first line of the output of (describe-function-1 
FUNC)."
     (should (string-match regexp result))))
 
 (ert-deftest help-fns-test-lisp-defun ()
-  (let ((regexp "a compiled Lisp function in .+subr\\.el")
+  (let ((regexp (if (boundp 'comp-ctxt)
+                    "a native compiled Lisp function in .+subr\\.el"
+                  "a compiled Lisp function in .+subr\\.el"))
         (result (help-fns-tests--describe-function 'last)))
     (should (string-match regexp result))))
 
 (ert-deftest help-fns-test-lisp-defsubst ()
-  (let ((regexp "a compiled Lisp function in .+subr\\.el")
+  (let ((regexp (if (boundp 'comp-ctxt)
+                    "a native compiled Lisp function in .+subr\\.el"
+                  "a compiled Lisp function in .+subr\\.el"))
         (result (help-fns-tests--describe-function 'posn-window)))
     (should (string-match regexp result))))
 
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 7a116aa..1e14673 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -382,7 +382,7 @@ cf. Bug#25477."
   "Test for https://debbugs.gnu.org/22027 ."
   (let ((default "foo") res)
     (cl-letf (((symbol-function 'read-string)
-               (lambda (_prompt _init _hist def) def)))
+               (lambda (_prompt &optional _init _hist def _inher-input) def)))
       (setq res (read-passwd "pass: " 'confirm (mapconcat #'string default 
"")))
       (should (string= default res)))))
 
diff --git a/test/src/comp-resources/comp-test-45603.el 
b/test/src/comp-resources/comp-test-45603.el
new file mode 100644
index 0000000..f1c0daf
--- /dev/null
+++ b/test/src/comp-resources/comp-test-45603.el
@@ -0,0 +1,28 @@
+;;; -*- lexical-binding: t; -*-
+
+;; Reduced from ivy.el.
+
+(defvar comp-test-45603-last)
+(defvar comp-test-45603-mark-prefix)
+(defvar comp-test-45603-directory)
+(defvar comp-test-45603-marked-candidates)
+
+(defun comp-test-45603--call-marked (action)
+  (let* ((prefix-len (length comp-test-45603-mark-prefix))
+         (marked-candidates
+          (mapcar
+           (lambda (s)
+             (let ((cand (substring s prefix-len)))
+               (if comp-test-45603-directory
+                   (expand-file-name cand comp-test-45603-directory)
+                 cand)))
+           comp-test-45603-marked-candidates))
+         (multi-action (comp-test-45603--get-multi-action 
comp-test-45603-last)))))
+
+(defalias 'comp-test-45603--file-local-name
+  (if (fboundp 'file-local-name)
+      #'file-local-name
+    (lambda (file)
+      (or (file-remote-p file 'localname) file))))
+
+(provide 'comp-test-45603)
diff --git a/test/src/comp-resources/comp-test-funcs-dyn.el 
b/test/src/comp-resources/comp-test-funcs-dyn.el
new file mode 100644
index 0000000..3118455
--- /dev/null
+++ b/test/src/comp-resources/comp-test-funcs-dyn.el
@@ -0,0 +1,50 @@
+;;; comp-test-funcs-dyn.el --- compilation unit tested by comp-tests.el -*- 
lexical-binding: nil; -*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'cl-lib)
+
+(defun comp-tests-ffuncall-callee-dyn-f (a b)
+  (list a b))
+
+(defun comp-tests-ffuncall-callee-opt-dyn-f (a b &optional c d)
+  (list a b c d))
+
+(defun comp-tests-ffuncall-callee-rest-dyn-f (a b &rest c)
+  (list a b c))
+
+(defun comp-tests-ffuncall-callee-opt-rest-dyn-f (a b &optional c &rest d)
+  (list a b c d))
+
+(defun comp-tests-cl-macro-exp-f ()
+  (cl-loop for xxx in '(a b)
+          for yyy = xxx
+          collect xxx))
+
+(cl-defun comp-tests-cl-uninterned-arg-parse-f (a &optional b &aux)
+  (list a b))
+
+(provide 'comp-test-dyn-funcs)
+
+;;; comp-test-funcs-dyn.el ends here
diff --git a/test/src/comp-resources/comp-test-funcs.el 
b/test/src/comp-resources/comp-test-funcs.el
new file mode 100644
index 0000000..f2a2463
--- /dev/null
+++ b/test/src/comp-resources/comp-test-funcs.el
@@ -0,0 +1,710 @@
+;;; comp-test-funcs.el --- compilation unit tested by comp-tests.el -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defvar comp-tests-var1 3)
+
+(defun comp-tests-varref-f ()
+  comp-tests-var1)
+
+(defun comp-tests-list-f ()
+  (list 1 2 3))
+(defun comp-tests-list2-f (a b c)
+  (list a b c))
+(defun comp-tests-car-f (x)
+  ;; Bcar
+  (car x))
+(defun comp-tests-cdr-f (x)
+  ;; Bcdr
+  (cdr x))
+(defun comp-tests-car-safe-f (x)
+  ;; Bcar_safe
+  (car-safe x))
+(defun comp-tests-cdr-safe-f (x)
+  ;; Bcdr_safe
+  (cdr-safe x))
+
+(defun comp-tests-cons-car-f ()
+  (car (cons 1 2)))
+(defun comp-tests-cons-cdr-f (x)
+  (cdr (cons 'foo x)))
+
+(defun comp-tests-hint-fixnum-f (n)
+  (1+ (comp-hint-fixnum n)))
+
+(defun comp-tests-hint-cons-f (c)
+  (car (comp-hint-cons c)))
+
+(defun comp-tests-varset0-f ()
+  (setq comp-tests-var1 55))
+(defun comp-tests-varset1-f ()
+  (setq comp-tests-var1 66)
+  4)
+
+(defun comp-tests-length-f ()
+  (length '(1 2 3)))
+
+(defun comp-tests-aref-aset-f ()
+  (let ((vec (make-vector 3 0)))
+    (aset vec 2 100)
+    (aref vec 2)))
+
+(defvar comp-tests-var2 3)
+(defun comp-tests-symbol-value-f ()
+  (symbol-value 'comp-tests-var2))
+
+(defun comp-tests-concat-f (x)
+  (concat "a" "b" "c" "d"
+          (concat "a" "b" "c" (concat "a" "b" (concat "foo" x)))))
+
+(defun comp-tests-ffuncall-callee-f (x y z)
+  (list x y z))
+
+(defun comp-tests-ffuncall-callee-optional-f (a b &optional c d)
+  (list a b c d))
+
+(defun comp-tests-ffuncall-callee-rest-f (a b &rest c)
+  (list a b c))
+
+(defun comp-tests-ffuncall-callee-more8-f (p1 p2 p3 p4 p5 p6 p7 p8 p9 p10)
+  ;; More then 8 args.
+  (list p1 p2 p3 p4 p5 p6 p7 p8 p9 p10))
+
+(defun comp-tests-ffuncall-callee-more8-rest-f (p1 p2 p3 p4 p5 p6 p7 p8 p9 
&rest p10)
+  ;; More then 8 args.
+  (list p1 p2 p3 p4 p5 p6 p7 p8 p9 p10))
+
+(defun comp-tests-ffuncall-native-f ()
+  "Call a primitive with no dedicate op."
+  (make-vector 1 nil))
+
+(defun comp-tests-ffuncall-native-rest-f ()
+  "Call a primitive with no dedicate op with &rest."
+  (vector 1 2 3))
+
+(defun comp-tests-ffuncall-apply-many-f (x)
+  (apply #'list x))
+
+(defun comp-tests-ffuncall-lambda-f (x)
+  (let ((fun (lambda (x)
+               (1+ x))))
+    (funcall fun x)))
+
+(defun comp-tests-jump-table-1-f (x)
+  (pcase x
+    ('x 'a)
+    ('y 'b)
+    (_ 'c)))
+
+(defun comp-tests-jump-table-2-f (x)
+  (pcase x
+    ("aaa" 'a)
+    ("bbb" 'b)))
+
+(defun comp-tests-conditionals-1-f (x)
+  ;; Generate goto-if-nil
+  (if x 1 2))
+(defun comp-tests-conditionals-2-f (x)
+  ;; Generate goto-if-nil-else-pop
+  (when x
+    1340))
+
+(defun comp-tests-fixnum-1-minus-f (x)
+  ;; Bsub1
+  (1- x))
+(defun comp-tests-fixnum-1-plus-f (x)
+  ;; Badd1
+  (1+ x))
+(defun comp-tests-fixnum-minus-f (x)
+  ;; Bnegate
+  (- x))
+
+(defun comp-tests-eqlsign-f (x y)
+  ;; Beqlsign
+  (= x y))
+(defun comp-tests-gtr-f (x y)
+  ;; Bgtr
+  (> x y))
+(defun comp-tests-lss-f (x y)
+  ;; Blss
+  (< x y))
+(defun comp-tests-les-f (x y)
+  ;; Bleq
+  (<= x y))
+(defun comp-tests-geq-f (x y)
+  ;; Bgeq
+  (>= x y))
+
+(defun comp-tests-setcar-f (x y)
+  (setcar x y)
+  x)
+(defun comp-tests-setcdr-f (x y)
+  (setcdr x y)
+  x)
+
+(defun comp-bubble-sort-f (list)
+  (let ((i (length list)))
+    (while (> i 1)
+      (let ((b list))
+        (while (cdr b)
+          (when (< (cadr b) (car b))
+            (setcar b (prog1 (cadr b)
+                        (setcdr b (cons (car b) (cddr b))))))
+          (setq b (cdr b))))
+      (setq i (1- i)))
+    list))
+
+(defun comp-tests-consp-f (x)
+  ;; Bconsp
+  (consp x))
+(defun comp-tests-setcar2-f (x)
+  ;; Bsetcar
+  (setcar x 3))
+
+(defun comp-tests-integerp-f (x)
+  ;; Bintegerp
+  (integerp x))
+(defun comp-tests-numberp-f (x)
+  ;; Bnumberp
+  (numberp x))
+
+(defun comp-tests-discardn-f (x)
+  ;; BdiscardN
+  (1+ (let ((a 1)
+            (_b)
+            (_c))
+        a)))
+(defun comp-tests-insertn-f (a b c d)
+  ;; Binsert
+  (insert a b c d))
+
+(defun comp-tests-err-arith-f ()
+  (/ 1 0))
+(defun comp-tests-err-foo-f ()
+  (error "foo"))
+
+(defun comp-tests-condition-case-0-f ()
+  ;; Bpushhandler Bpophandler
+  (condition-case
+      err
+      (comp-tests-err-arith-f)
+    (arith-error (concat "arith-error "
+                         (error-message-string err)
+                         " catched"))
+    (error (concat "error "
+                   (error-message-string err)
+                   " catched"))))
+(defun comp-tests-condition-case-1-f ()
+  ;; Bpushhandler Bpophandler
+  (condition-case
+      err
+      (comp-tests-err-foo-f)
+    (arith-error (concat "arith-error "
+                         (error-message-string err)
+                         " catched"))
+    (error (concat "error "
+                   (error-message-string err)
+                   " catched"))))
+(defun comp-tests-catch-f (f)
+  (catch 'foo
+    (funcall f)))
+(defun comp-tests-throw-f (x)
+  (throw 'foo x))
+
+(defun comp-tests-buff0-f ()
+  (with-temp-buffer
+    (insert "foo")
+    (buffer-string)))
+
+(defun comp-tests-lambda-return-f ()
+  (lambda (x) (1+ x)))
+
+(defun comp-tests-fib-f (n)
+  (cond ((= n 0) 0)
+       ((= n 1) 1)
+       (t (+ (comp-tests-fib-f (- n 1))
+             (comp-tests-fib-f (- n 2))))))
+
+(defmacro comp-tests-macro-m (x)
+  x)
+
+(defun comp-tests-string-trim-f (url)
+  (string-trim url))
+
+(defun comp-tests-trampoline-removal-f ()
+  (make-hash-table))
+
+(defun comp-tests-signal-f ()
+  (signal 'foo t))
+
+(defun comp-tests-func-call-removal-f ()
+  (let ((a 10)
+       (b 3))
+    (% a b)))
+
+(defun comp-tests-doc-f ()
+  "A nice docstring"
+  t)
+
+(defun comp-test-interactive-form0-f (dir)
+  (interactive "D")
+  dir)
+
+(defun comp-test-interactive-form1-f (x y)
+  (interactive '(1 2))
+  (+ x y))
+
+(defun comp-test-interactive-form2-f ()
+  (interactive))
+
+(defun comp-test-40187-2-f ()
+  'foo)
+
+(defalias 'comp-test-40187-1-f (symbol-function 'comp-test-40187-2-f))
+
+(defun comp-test-40187-2-f ()
+  'bar)
+
+(defun comp-test-speed--1-f ()
+  (declare (speed -1))
+  3)
+
+(defun comp-test-42360-f (str end-column
+                             &optional start-column padding ellipsis
+                              ellipsis-text-property)
+  ;; From `truncate-string-to-width'.  A large enough function to
+  ;; potentially use all registers and that is modifying local
+  ;; variables inside condition-case.
+  (let ((str-len (length str))
+       (str-width 14)
+       (ellipsis-width 3)
+       (idx 0)
+       (column 0)
+       (head-padding "") (tail-padding "")
+       ch last-column last-idx from-idx)
+    (condition-case nil
+       (while (< column start-column)
+         (setq ch (aref str idx)
+               column (+ column (char-width ch))
+               idx (1+ idx)))
+      (args-out-of-range (setq idx str-len)))
+    (if (< column start-column)
+       (if padding (make-string end-column padding) "")
+      (when (and padding (> column start-column))
+       (setq head-padding (make-string (- column start-column) padding)))
+      (setq from-idx idx)
+      (when (>= end-column column)
+       (condition-case nil
+           (while (< column end-column)
+             (setq last-column column
+                   last-idx idx
+                   ch (aref str idx)
+                   column (+ column (char-width ch))
+                   idx (1+ idx)))
+         (args-out-of-range (setq idx str-len)))
+       (when (> column end-column)
+         (setq column last-column
+               idx last-idx))
+       (when (and padding (< column end-column))
+         (setq tail-padding (make-string (- end-column column) padding))))
+      (if (and ellipsis-text-property
+               (not (equal ellipsis ""))
+               idx)
+         (concat head-padding
+                  (substring str from-idx idx)
+                 (propertize (substring str idx) 'display (or ellipsis "")))
+        (concat head-padding (substring str from-idx idx)
+               tail-padding ellipsis)))))
+
+(defun comp-test-primitive-advice-f (x y)
+  (declare (speed 2))
+  (+ x y))
+
+(defun comp-test-primitive-redefine-f (x y)
+  (declare (speed 2))
+  (- x y))
+
+(defsubst comp-test-defsubst-f ()
+  t)
+
+(defvar comp-test-and-3-var 1)
+(defun comp-test-and-3-f (x)
+  (and (atom x)
+       comp-test-and-3-var
+       2))
+
+(defun comp-test-copy-insn-f (insn)
+  ;; From `comp-copy-insn'.
+  (if (consp insn)
+      (let (result)
+       (while (consp insn)
+         (let ((newcar (car insn)))
+           (if (or (consp (car insn)) (comp-mvar-p (car insn)))
+               (setf newcar (comp-copy-insn (car insn))))
+           (push newcar result))
+         (setf insn (cdr insn)))
+       (nconc (nreverse result)
+               (if (comp-mvar-p insn) (comp-copy-insn insn) insn)))
+    (if (comp-mvar-p insn)
+        (copy-comp-mvar insn)
+      insn)))
+
+(defun comp-test-cond-rw-1-1-f ())
+
+(defun comp-test-cond-rw-1-2-f ()
+  (let ((it (comp-test-cond-rw-1-1-f))
+       (key 't))
+    (if (or (equal it key)
+           (eq key t))
+       it
+      nil)))
+
+(defun comp-test-44968-f (start end)
+  (let ((dirlist)
+        (dir (expand-file-name start))
+        (end (expand-file-name end)))
+    (while (not (or (equal dir (car dirlist))
+                    (file-equal-p dir end)))
+      (push dir dirlist)
+      (setq dir (directory-file-name (file-name-directory dir))))
+    (nreverse dirlist)))
+
+(defun comp-test-45342-f (n)
+  (pcase n
+    (1 " ➊") (2 " ➋") (3 " ➌") (4 " ➍") (5 " ➎") (6 " ➏")
+    (7 " ➐") (8 " ➑") (9 " ➒") (10 " ➓") (_ "")))
+
+(defun comp-test-assume-double-neg-f (collection value)
+  ;; Reduced from `auth-source-search-collection'.
+  (when (atom collection)
+    (setq collection (list collection)))
+  (or (eq value t)
+      ;; value is (not (member t))
+      (eq collection value)
+      ;; collection is t, not (member t)!
+      (member value collection)))
+
+(defun comp-test-assume-in-loop-1-f (arg)
+  ;; Reduced from `comint-delim-arg'.
+  (let ((args nil)
+       (pos 0)
+       (len (length arg)))
+    (while (< pos len)
+      (let ((start pos))
+       (while (< pos len)
+         (setq pos (1+ pos)))
+       (setq args (cons (substring arg start pos) args))))
+    args))
+
+(defun comp-test-45376-1-f ()
+  ;; Reduced from `eshell-ls-find-column-lengths'.
+  (let* (res
+        (len 2)
+        (i 0)
+        (j 0))
+    (while (< j len)
+      (if (= i len)
+         (setq i 0))
+      (setq res (cons i res)
+           j (1+ j)
+           i (1+ i)))
+    res))
+
+(defun comp-test-45376-2-f ()
+  ;; Also reduced from `eshell-ls-find-column-lengths'.
+  (let* ((x 1)
+        res)
+    (while x
+      (let* ((y 4)
+            (i 0))
+       (while (> y 0)
+         (when (= i x)
+           (setq i 0))
+         (setf res (cons i res))
+         (setq y (1- y)
+               i (1+ i)))
+       (if (>= x 3)
+           (setq x nil)
+         (setq x (1+ x)))))
+    res))
+
+(defun comp-test-not-cons-f (x)
+  ;; Reduced from `cl-copy-list'.
+  (if (consp x)
+      (print x)
+    (car x)))
+
+(defun comp-test-45576-f ()
+  ;; Reduced from `eshell-find-alias-function'.
+  (let ((sym (intern-soft "eval")))
+    (if (and (functionp sym)
+            '(eshell-ls eshell-pred eshell-prompt eshell-script
+                        eshell-term eshell-unix))
+       sym)))
+
+(defun comp-test-45635-f (&rest args)
+  ;; Reduced from `set-face-attribute'.
+  (let ((spec args)
+       family)
+    (while spec
+      (cond ((eq (car spec) :family)
+            (setq family (cadr spec))))
+      (setq spec (cddr spec)))
+    (when (and (stringp family)
+              (string-match "\\([^-]*\\)-\\([^-]*\\)" family))
+      (setq family (match-string 2 family)))
+    (when (or (stringp family)
+             (eq family 'unspecified))
+      family)))
+
+(defun comp-test-46670-1-f (_)
+  "foo")
+
+(defun comp-test-46670-2-f (s)
+  (and (equal (comp-test-46670-1-f (length s)) s)
+       s))
+
+(cl-defun comp-test-46824-1-f ()
+  (let ((next-repos '(1)))
+    (while t
+      (let ((recipe (car next-repos)))
+        (cl-block loop
+          (while t
+            (let ((err
+                   (condition-case e
+                       (progn
+                         (setq next-repos
+                               (cdr next-repos))
+                         (cl-return-from loop))
+                     (error e))))
+              (format "%S"
+                      (error-message-string err))))))
+      (cl-return-from comp-test-46824-1-f))))
+
+(defun comp-test-47868-1-f ()
+  " ")
+
+(defun comp-test-47868-2-f ()
+  #(" " 0 1 (face font-lock-keyword-face)))
+
+(defun comp-test-47868-3-f ()
+  " ")
+
+(defun comp-test-47868-4-f ()
+  #(" " 0 1 (face font-lock-keyword-face)))
+
+
+
+;;;;;;;;;;;;;;;;;;;;
+;; Tromey's tests ;;
+;;;;;;;;;;;;;;;;;;;;
+
+;; Test Bconsp.
+(defun comp-test-consp (x) (consp x))
+
+;; Test Blistp.
+(defun comp-test-listp (x) (listp x))
+
+;; Test Bstringp.
+(defun comp-test-stringp (x) (stringp x))
+
+;; Test Bsymbolp.
+(defun comp-test-symbolp (x) (symbolp x))
+
+;; Test Bintegerp.
+(defun comp-test-integerp (x) (integerp x))
+
+;; Test Bnumberp.
+(defun comp-test-numberp (x) (numberp x))
+
+;; Test Badd1.
+(defun comp-test-add1 (x) (1+ x))
+
+;; Test Bsub1.
+(defun comp-test-sub1 (x) (1- x))
+
+;; Test Bneg.
+(defun comp-test-negate (x) (- x))
+
+;; Test Bnot.
+(defun comp-test-not (x) (not x))
+
+;; Test Bbobp, Beobp, Bpoint, Bpoint_min, Bpoint_max.
+(defun comp-test-bobp () (bobp))
+(defun comp-test-eobp () (eobp))
+(defun comp-test-point () (point))
+(defun comp-test-point-min () (point-min))
+(defun comp-test-point-max () (point-max))
+
+;; Test Bcar and Bcdr.
+(defun comp-test-car (x) (car x))
+(defun comp-test-cdr (x) (cdr x))
+
+;; Test Bcar_safe and Bcdr_safe.
+(defun comp-test-car-safe (x) (car-safe x))
+(defun comp-test-cdr-safe (x) (cdr-safe x))
+
+;; Test Beq.
+(defun comp-test-eq (x y) (eq x y))
+
+;; Test Bgotoifnil.
+(defun comp-test-if (x y) (if x x y))
+
+;; Test Bgotoifnilelsepop.
+(defun comp-test-and (x y) (and x y))
+
+;; Test Bgotoifnonnilelsepop.
+(defun comp-test-or (x y) (or x y))
+
+;; Test Bsave_excursion.
+(defun comp-test-save-excursion ()
+  (save-excursion
+    (insert "XYZ")))
+
+;; Test Bcurrent_buffer.
+(defun comp-test-current-buffer () (current-buffer))
+
+;; Test Bgtr.
+(defun comp-test-> (a b)
+  (> a b))
+
+;; Test Bpushcatch.
+(defun comp-test-catch (&rest l)
+  (catch 'done
+    (dolist (v l)
+      (when (> v 23)
+        (throw 'done v)))))
+
+;; Test Bmemq.
+(defun comp-test-memq (val list)
+  (memq val list))
+
+;; Test BlistN.
+(defun comp-test-listN (x)
+  (list x x x x x x x x x x x x x x x x))
+
+;; Test BconcatN.
+(defun comp-test-concatN (x)
+  (concat x x x x x x))
+
+;; Test optional and rest arguments.
+(defun comp-test-opt-rest (a &optional b &rest c)
+  (list a b c))
+
+;; Test for too many arguments.
+(defun comp-test-opt (a &optional b)
+  (cons a b))
+
+;; Test for unwind-protect.
+(defvar comp-test-up-val nil)
+(defun comp-test-unwind-protect (fun)
+  (setq comp-test-up-val nil)
+  (unwind-protect
+      (progn
+        (setq comp-test-up-val 23)
+        (funcall fun)
+        (setq comp-test-up-val 24))
+    (setq comp-test-up-val 999)))
+
+;; Non tested functions that proved just to be difficult to compile.
+
+(defun comp-test-callee (_ __) t)
+(defun comp-test-silly-frame1 (x)
+  ;; Check robustness against dead code.
+  (cl-case x
+    (0 (comp-test-callee
+        (pcase comp-tests-var1
+          (1 1)
+          (2 2))
+        3))))
+
+(defun comp-test-silly-frame2 (token)
+  ;; Check robustness against dead code.
+  (while c
+    (cl-case c
+      (?< 1)
+      (?> 2))))
+
+(defun comp-test-big-interactive (filename &optional force arg load)
+  ;; Check non trivial interactive form using `byte-recompile-file'.
+  (interactive
+   (let ((file buffer-file-name)
+        (file-name nil)
+        (file-dir nil))
+     (and file
+         (derived-mode-p 'emacs-lisp-mode)
+         (setq file-name (file-name-nondirectory file)
+               file-dir (file-name-directory file)))
+     (list (read-file-name (if current-prefix-arg
+                              "Byte compile file: "
+                            "Byte recompile file: ")
+                          file-dir file-name nil)
+          current-prefix-arg)))
+  (let ((dest (byte-compile-dest-file filename))
+        ;; Expand now so we get the current buffer's defaults
+        (filename (expand-file-name filename)))
+    (if (if (file-exists-p dest)
+            ;; File was already compiled
+            ;; Compile if forced to, or filename newer
+            (or force
+                (file-newer-than-file-p filename dest))
+          (and arg
+               (or (eq 0 arg)
+                   (y-or-n-p (concat "Compile "
+                                     filename "? ")))))
+        (progn
+          (if (and noninteractive (not byte-compile-verbose))
+              (message "Compiling %s..." filename))
+          (byte-compile-file filename load))
+      (when load
+       (load (if (file-exists-p dest) dest filename)))
+      'no-byte-compile)))
+
+(defun comp-test-no-return-1 (x)
+  (while x
+   (error "foo")))
+
+(defun comp-test-no-return-2 (x)
+  (cond
+   ((eql x '2) t)
+   ((error "bar") nil)))
+
+(defun comp-test-no-return-3 ())
+(defun comp-test-no-return-4 (x)
+  (when x
+    (error "foo")
+    (while (comp-test-no-return-3)
+      (comp-test-no-return-3))))
+
+(defun comp-test-=-nan (x)
+  (when (= x 0.0e+NaN)
+    x))
+
+(defun comp-test-=-infinity (x)
+  (when (= x 1.0e+INF)
+    x))
+
+(provide 'comp-test-funcs)
+
+;;; comp-test-funcs.el ends here
diff --git a/test/src/comp-resources/comp-test-pure.el 
b/test/src/comp-resources/comp-test-pure.el
new file mode 100644
index 0000000..5c1d2d1
--- /dev/null
+++ b/test/src/comp-resources/comp-test-pure.el
@@ -0,0 +1,40 @@
+;;; comp-test-pure.el --- compilation unit tested by comp-tests.el -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defun comp-tests-pure-callee-f (x)
+  (1+ x))
+
+(defun comp-tests-pure-caller-f ()
+  (comp-tests-pure-callee-f 3))
+
+(defun comp-tests-pure-fibn-f (a b count)
+  (if (= count 0)
+      b
+    (comp-tests-pure-fibn-f (+ a b) a (- count 1))))
+
+(defun comp-tests-pure-fibn-entry-f ()
+  (comp-tests-pure-fibn-f 1 0 20))
+
+;;; comp-test-pure.el ends here
diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el
new file mode 100644
index 0000000..a1893fd
--- /dev/null
+++ b/test/src/comp-tests.el
@@ -0,0 +1,1443 @@
+;;; comp-tests.el --- unit tests for src/comp.c      -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+;; Author: Andrea Corallo <akrl@sdf.org>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for src/comp.c.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'cl-lib)
+
+(defconst comp-test-src (ert-resource-file "comp-test-funcs.el"))
+
+(defconst comp-test-dyn-src (ert-resource-file "comp-test-funcs-dyn.el"))
+
+(when (featurep 'nativecomp)
+  (require 'comp)
+  (message "Compiling tests...")
+  (load (native-compile comp-test-src))
+  (load (native-compile comp-test-dyn-src)))
+
+(defmacro comp-deftest (name args &rest docstring-and-body)
+  "Define a test for the native compiler tagging it as :nativecomp."
+  (declare (indent defun)
+           (doc-string 3))
+  `(ert-deftest ,(intern (concat "comp-tests-" (symbol-name name))) ,args
+     :tags '(:nativecomp)
+     ,@docstring-and-body))
+
+
+
+(ert-deftest comp-tests-bootstrap ()
+  "Compile the compiler and load it to compile it-self.
+Check that the resulting binaries do not differ."
+  :tags '(:expensive-test :nativecomp)
+  (let* ((byte-native-for-bootstrap t) ; FIXME HACK
+         (comp-src (expand-file-name "../../../lisp/emacs-lisp/comp.el"
+                                     (ert-resource-directory)))
+         (comp1-src (make-temp-file "stage1-" nil ".el"))
+         (comp2-src (make-temp-file "stage2-" nil ".el"))
+         ;; Can't use debug symbols.
+         (comp-debug 0))
+    (copy-file comp-src comp1-src t)
+    (copy-file comp-src comp2-src t)
+    (let ((load-no-native t))
+      (load (concat comp-src "c") nil nil t t))
+    (should-not (subr-native-elisp-p (symbol-function #'native-compile)))
+    (message "Compiling stage1...")
+    (let* ((t0 (current-time))
+           (comp1-eln (native-compile comp1-src)))
+      (message "Done in %d secs" (float-time (time-since t0)))
+      (load comp1-eln nil nil t t)
+      (should (subr-native-elisp-p (symbol-function 'native-compile)))
+      (message "Compiling stage2...")
+      (let ((t0 (current-time))
+            (comp2-eln (native-compile comp2-src)))
+        (message "Done in %d secs" (float-time (time-since t0)))
+        (message "Comparing %s %s" comp1-eln comp2-eln)
+        (should (= (call-process "cmp" nil nil nil comp1-eln comp2-eln) 0))))))
+
+(comp-deftest provide ()
+  "Testing top level provide."
+  (should (featurep 'comp-test-funcs)))
+
+(comp-deftest varref ()
+  "Testing varref."
+  (should (= (comp-tests-varref-f) 3)))
+
+(comp-deftest list ()
+  "Testing cons car cdr."
+  (should (equal (comp-tests-list-f) '(1 2 3)))
+  (should (equal (comp-tests-list2-f 1 2 3) '(1 2 3)))
+  (should (= (comp-tests-car-f '(1 . 2)) 1))
+  (should (null (comp-tests-car-f nil)))
+  (should-error (comp-tests-car-f 3)
+                :type 'wrong-type-argument)
+  (should (= (comp-tests-cdr-f '(1 . 2)) 2))
+  (should (null (comp-tests-cdr-f nil)))
+  (should-error (comp-tests-cdr-f 3)
+                :type 'wrong-type-argument)
+  (should (= (comp-tests-car-safe-f '(1 . 2)) 1))
+  (should (null (comp-tests-car-safe-f 'a)))
+  (should (= (comp-tests-cdr-safe-f '(1 . 2)) 2))
+  (should (null (comp-tests-cdr-safe-f 'a))))
+
+(comp-deftest comp-tests-cons-car-cdr ()
+  "Testing cons car cdr."
+  (should (= (comp-tests-cons-car-f) 1))
+  (should (= (comp-tests-cons-cdr-f 3) 3)))
+
+(comp-deftest varset ()
+  "Testing varset."
+  (comp-tests-varset0-f)
+  (should (= comp-tests-var1 55))
+
+  (should (= (comp-tests-varset1-f) 4))
+  (should (= comp-tests-var1 66)))
+
+(comp-deftest length ()
+  "Testing length."
+  (should (= (comp-tests-length-f) 3)))
+
+(comp-deftest aref-aset ()
+  "Testing aref and aset."
+  (should (= (comp-tests-aref-aset-f) 100)))
+
+(comp-deftest symbol-value ()
+  "Testing aref and aset."
+  (should (= (comp-tests-symbol-value-f) 3)))
+
+(comp-deftest concat ()
+  "Testing concatX opcodes."
+  (should (string= (comp-tests-concat-f "bar") "abcdabcabfoobar")))
+
+(comp-deftest ffuncall ()
+  "Test calling conventions."
+
+  ;; (defun comp-tests-ffuncall-caller-f ()
+  ;;   (comp-tests-ffuncall-callee-f 1 2 3))
+
+  ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
+
+  ;; ;; After it gets compiled
+  ;; (native-compile #'comp-tests-ffuncall-callee-f)
+  ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
+
+  ;; ;; Recompiling the caller once with callee already compiled
+  ;; (defun comp-tests-ffuncall-caller-f ()
+  ;;   (comp-tests-ffuncall-callee-f 1 2 3))
+  ;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
+
+  (should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3 4)
+                 '(1 2 3 4)))
+  (should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3)
+                 '(1 2 3 nil)))
+  (should (equal (comp-tests-ffuncall-callee-optional-f 1 2)
+                 '(1 2 nil nil)))
+
+  (should (equal (comp-tests-ffuncall-callee-rest-f 1 2)
+                 '(1 2 nil)))
+  (should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3)
+                 '(1 2 (3))))
+  (should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3 4)
+                 '(1 2 (3 4))))
+
+  (should (equal (comp-tests-ffuncall-callee-more8-f 1 2 3 4 5 6 7 8 9 10)
+                 '(1 2 3 4 5 6 7 8 9 10)))
+
+  (should (equal (comp-tests-ffuncall-callee-more8-rest-f 1 2 3 4 5 6 7 8 9 10 
11)
+                 '(1 2 3 4 5 6 7 8 9 (10 11))))
+
+  (should (equal (comp-tests-ffuncall-native-f) [nil]))
+
+  (should (equal (comp-tests-ffuncall-native-rest-f) [1 2 3]))
+
+  (should (equal (comp-tests-ffuncall-apply-many-f '(1 2 3))
+                 '(1 2 3)))
+
+  (should (= (comp-tests-ffuncall-lambda-f 1) 2)))
+
+(comp-deftest jump-table ()
+  "Testing jump tables"
+  (should (eq (comp-tests-jump-table-1-f 'x) 'a))
+  (should (eq (comp-tests-jump-table-1-f 'y) 'b))
+  (should (eq (comp-tests-jump-table-1-f 'xxx) 'c))
+
+  ;; Jump table not with eq as test
+  (should (eq (comp-tests-jump-table-2-f "aaa") 'a))
+  (should (eq (comp-tests-jump-table-2-f "bbb") 'b)))
+
+(comp-deftest conditionals ()
+  "Testing conditionals."
+  (should (= (comp-tests-conditionals-1-f t) 1))
+  (should (= (comp-tests-conditionals-1-f nil) 2))
+  (should (= (comp-tests-conditionals-2-f t) 1340))
+  (should (eq (comp-tests-conditionals-2-f nil) nil)))
+
+(comp-deftest fixnum ()
+  "Testing some fixnum inline operation."
+  (should (= (comp-tests-fixnum-1-minus-f 10) 9))
+  (should (= (comp-tests-fixnum-1-minus-f most-negative-fixnum)
+             (1- most-negative-fixnum)))
+  (should-error (comp-tests-fixnum-1-minus-f 'a)
+                :type 'wrong-type-argument)
+  (should (= (comp-tests-fixnum-1-plus-f 10) 11))
+  (should (= (comp-tests-fixnum-1-plus-f most-positive-fixnum)
+             (1+ most-positive-fixnum)))
+  (should-error (comp-tests-fixnum-1-plus-f 'a)
+                :type 'wrong-type-argument)
+  (should (= (comp-tests-fixnum-minus-f 10) -10))
+  (should (= (comp-tests-fixnum-minus-f most-negative-fixnum)
+             (- most-negative-fixnum)))
+  (should-error (comp-tests-fixnum-minus-f 'a)
+                :type 'wrong-type-argument))
+
+(comp-deftest type-hints ()
+  "Just test compiler hints are transparent in this case."
+  ;; FIXME we should really check they are also effective.
+  (should (= (comp-tests-hint-fixnum-f 3) 4))
+  (should (= (comp-tests-hint-cons-f (cons 1 2)) 1)))
+
+(comp-deftest arith-comp ()
+  "Testing arithmetic comparisons."
+  (should (eq (comp-tests-eqlsign-f 4 3) nil))
+  (should (eq (comp-tests-eqlsign-f 3 3) t))
+  (should (eq (comp-tests-eqlsign-f 2 3) nil))
+  (should (eq (comp-tests-gtr-f 4 3) t))
+  (should (eq (comp-tests-gtr-f 3 3) nil))
+  (should (eq (comp-tests-gtr-f 2 3) nil))
+  (should (eq (comp-tests-lss-f 4 3) nil))
+  (should (eq (comp-tests-lss-f 3 3) nil))
+  (should (eq (comp-tests-lss-f 2 3) t))
+  (should (eq (comp-tests-les-f 4 3) nil))
+  (should (eq (comp-tests-les-f 3 3) t))
+  (should (eq (comp-tests-les-f 2 3) t))
+  (should (eq (comp-tests-geq-f 4 3) t))
+  (should (eq (comp-tests-geq-f 3 3) t))
+  (should (eq (comp-tests-geq-f 2 3) nil)))
+
+(comp-deftest setcarcdr ()
+  "Testing setcar setcdr."
+  (should (equal (comp-tests-setcar-f '(10 . 10) 3) '(3 . 10)))
+  (should (equal (comp-tests-setcdr-f '(10 . 10) 3) '(10 . 3)))
+  (should-error (comp-tests-setcar-f 3 10)
+                :type 'wrong-type-argument)
+  (should-error (comp-tests-setcdr-f 3 10)
+                :type 'wrong-type-argument))
+
+(comp-deftest bubble-sort ()
+  "Run bubble sort."
+  (let* ((list1 (mapcar #'random (make-list 1000 most-positive-fixnum)))
+         (list2 (copy-sequence list1)))
+    (should (equal (comp-bubble-sort-f list1)
+                   (sort list2 #'<)))))
+
+(comp-deftest apply ()
+  "Test some inlined list functions."
+  (should (eq (comp-tests-consp-f '(1)) t))
+  (should (eq (comp-tests-consp-f 1) nil))
+  (let ((x (cons 1 2)))
+    (should (= (comp-tests-setcar2-f x) 3))
+    (should (equal x '(3 . 2)))))
+
+(comp-deftest num-inline ()
+  "Test some inlined number functions."
+  (should (eq (comp-tests-integerp-f 1) t))
+  (should (eq (comp-tests-integerp-f '(1)) nil))
+  (should (eq (comp-tests-integerp-f 3.5) nil))
+  (should (eq (comp-tests-integerp-f (1+ most-negative-fixnum)) t))
+
+  (should (eq (comp-tests-numberp-f 1) t))
+  (should (eq (comp-tests-numberp-f 'a) nil))
+  (should (eq (comp-tests-numberp-f 3.5) t)))
+
+(comp-deftest stack ()
+  "Test some stack operation."
+  (should (= (comp-tests-discardn-f 10) 2))
+  (should (string= (with-temp-buffer
+                      (comp-tests-insertn-f "a" "b" "c" "d")
+                      (buffer-string))
+                   "abcd")))
+
+(comp-deftest non-locals ()
+  "Test non locals."
+  (should (string= (comp-tests-condition-case-0-f)
+                   "arith-error Arithmetic error catched"))
+  (should (string= (comp-tests-condition-case-1-f)
+                   "error foo catched"))
+  (should (= (comp-tests-catch-f
+              (lambda () (throw 'foo 3)))
+             3))
+  (should (= (catch 'foo
+               (comp-tests-throw-f 3)))))
+
+(comp-deftest gc ()
+  "Try to do some longer computation to let the gc kick in."
+  (dotimes (_ 100000)
+    (comp-tests-cons-cdr-f 3))
+  (should (= (comp-tests-cons-cdr-f 3) 3)))
+
+(comp-deftest buffer ()
+  (should (string= (comp-tests-buff0-f) "foo")))
+
+(comp-deftest lambda-return ()
+  (let ((f (comp-tests-lambda-return-f)))
+    (should (subr-native-elisp-p f))
+    (should (= (funcall f 3) 4))))
+
+(comp-deftest recursive ()
+  (should (= (comp-tests-fib-f 10) 55)))
+
+(comp-deftest macro ()
+  "Just check we can define macros"
+  (should (macrop (symbol-function 'comp-tests-macro-m))))
+
+(comp-deftest string-trim ()
+  (should (string= (comp-tests-string-trim-f "dsaf ") "dsaf")))
+
+(comp-deftest trampoline-removal ()
+  ;; This tests that we can can call primitives with no dedicated bytecode.
+  ;; At speed >= 2 the trampoline will not be used.
+  (should (hash-table-p (comp-tests-trampoline-removal-f))))
+
+(comp-deftest signal ()
+  (should (equal (condition-case err
+                     (comp-tests-signal-f)
+                   (t err))
+                 '(foo . t))))
+
+(comp-deftest func-call-removal ()
+  ;; See `comp-propagate-insn' `comp-function-call-remove'.
+  (should (= (comp-tests-func-call-removal-f) 1)))
+
+(comp-deftest doc ()
+  (should (string= (documentation #'comp-tests-doc-f)
+                   "A nice docstring"))
+  ;; Check a preloaded function, we can't use `comp-tests-doc-f' now
+  ;; as this is loaded manually with no .elc.
+  (should (string-match "\\.*.elc\\'" (symbol-file #'error))))
+
+(comp-deftest interactive-form ()
+  (should (equal (interactive-form #'comp-test-interactive-form0-f)
+                 '(interactive "D")))
+  (should (equal (interactive-form #'comp-test-interactive-form1-f)
+                 '(interactive '(1 2))))
+  (should (equal (interactive-form #'comp-test-interactive-form2-f)
+                 '(interactive nil)))
+  (should (cl-every #'commandp '(comp-test-interactive-form0-f
+                                 comp-test-interactive-form1-f
+                                 comp-test-interactive-form2-f)))
+  (should-not (commandp #'comp-tests-doc-f)))
+
+(comp-deftest free-fun ()
+  "Check we are able to compile a single function."
+  (eval '(defun comp-tests-free-fun-f ()
+           "Some doc."
+           (interactive)
+           3)
+        t)
+  (native-compile #'comp-tests-free-fun-f)
+
+  (should (subr-native-elisp-p (symbol-function #'comp-tests-free-fun-f)))
+  (should (= (comp-tests-free-fun-f) 3))
+  (should (string= (documentation #'comp-tests-free-fun-f)
+                   "Some doc."))
+  (should (commandp #'comp-tests-free-fun-f))
+  (should (equal (interactive-form #'comp-tests-free-fun-f)
+                 '(interactive))))
+
+(comp-deftest free-fun-silly-name ()
+  "Check we are able to compile a single function."
+  (eval '(defun comp-tests/free\fun-f ()) t)
+  (native-compile #'comp-tests/free\fun-f)
+  (should (subr-native-elisp-p (symbol-function #'comp-tests/free\fun-f))))
+
+(comp-deftest bug-40187 ()
+  "Check function name shadowing.
+https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-03/msg00914.html.";
+  (should (eq (comp-test-40187-1-f) 'foo))
+  (should (eq (comp-test-40187-2-f) 'bar)))
+
+(comp-deftest speed--1 ()
+  "Check that at speed -1 we do not native compile."
+  (should (= (comp-test-speed--1-f) 3))
+  (should-not (subr-native-elisp-p (symbol-function #'comp-test-speed--1-f))))
+
+(comp-deftest bug-42360 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-07/msg00418.html>."
+  (should (string= (comp-test-42360-f "Nel mezzo del " 18 0 32 "yyy" nil)
+                   "Nel mezzo del     yyy")))
+
+(comp-deftest bug-44968 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-11/msg02357.html>"
+  (comp-test-44968-f "/tmp/test/foo" "/tmp"))
+
+(comp-deftest bug-45342 ()
+  "Preserve multibyte immediate strings.
+<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-12/msg01771.html>"
+  (should (string= " ➊" (comp-test-45342-f 1))))
+
+(comp-deftest assume-double-neg ()
+  "In fwprop assumtions (not (not (member x))) /= (member x)."
+  (should-not (comp-test-assume-double-neg-f "bar" "foo")))
+
+(comp-deftest assume-in-loop-1 ()
+  "Broken call args assumptions lead to infinite loop."
+  (should (equal (comp-test-assume-in-loop-1-f "cd") '("cd"))))
+
+(comp-deftest bug-45376-1 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-12/msg01883.html>"
+  (should (equal (comp-test-45376-1-f) '(1 0))))
+
+(comp-deftest bug-45376-2 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-12/msg01883.html>"
+  (should (equal (comp-test-45376-2-f) '(0 2 1 0 1 0 1 0 0 0 0 0))))
+
+(defvar comp-test-primitive-advice)
+(comp-deftest primitive-advice ()
+  "Test effectiveness of primitive advicing."
+  (let (comp-test-primitive-advice
+        (f (lambda (&rest args)
+             (setq comp-test-primitive-advice args))))
+    (advice-add #'+ :before f)
+    (unwind-protect
+        (progn
+          (should (= (comp-test-primitive-advice-f 3 4) 7))
+          (should (equal comp-test-primitive-advice '(3 4))))
+      (advice-remove #'+ f))))
+
+(defvar comp-test-primitive-redefine-args)
+(comp-deftest primitive-redefine ()
+  "Test effectiveness of primitive redefinition."
+  (cl-letf ((comp-test-primitive-redefine-args nil)
+            ((symbol-function #'-)
+             (lambda (&rest args)
+              (setq comp-test-primitive-redefine-args args)
+               'xxx)))
+    (should (eq (comp-test-primitive-redefine-f 10 2) 'xxx))
+    (should (equal comp-test-primitive-redefine-args '(10 2)))))
+
+(comp-deftest compile-forms ()
+  "Verify lambda form native compilation."
+  (should-error (native-compile '(+ 1 foo)))
+  (let ((lexical-binding t)
+        (f (native-compile '(lambda (x) (1+ x)))))
+    (should (subr-native-elisp-p f))
+    (should (= (funcall f 2) 3)))
+  (let* ((lexical-binding nil)
+         (f (native-compile '(lambda (x) (1+ x)))))
+    (should (subr-native-elisp-p f))
+    (should (= (funcall f 2) 3))))
+
+(comp-deftest comp-test-defsubst ()
+  ;; Bug#42664, Bug#43280, Bug#44209.
+  (should-not (subr-native-elisp-p (symbol-function #'comp-test-defsubst-f))))
+
+(comp-deftest primitive-redefine-compile-44221 ()
+  "Test the compiler still works while primitives are redefined (bug#44221)."
+  (cl-letf (((symbol-function #'delete-region)
+             (lambda (_ _))))
+    (should (subr-native-elisp-p
+             (native-compile
+              '(lambda ()
+                 (delete-region (point-min) (point-max))))))))
+
+(comp-deftest and-3 ()
+  (should (= (comp-test-and-3-f t) 2))
+  (should (null (comp-test-and-3-f '(1 2)))))
+
+(comp-deftest copy-insn ()
+  (should (equal (comp-test-copy-insn-f '(1 2 3 (4 5 6)))
+                 '(1 2 3 (4 5 6))))
+  (should (null (comp-test-copy-insn-f nil))))
+
+(comp-deftest cond-rw-1 ()
+  "Check cond-rw does not break target blocks with multiple predecessor."
+  (should (null (comp-test-cond-rw-1-2-f))))
+
+(comp-deftest not-cons-1 ()
+  (should-not (comp-test-not-cons-f nil)))
+
+(comp-deftest 45576-1 ()
+  "Functionp satisfies also symbols.
+<https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-01/msg00029.html>."
+  (should (eq (comp-test-45576-f) 'eval)))
+
+(comp-deftest 45635-1 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-01/msg00158.html>."
+  (should (string= (comp-test-45635-f :height 180 :family "PragmataPro Liga")
+                   "PragmataPro Liga")))
+
+(comp-deftest 45603-1 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-12/msg01994.html>"
+  (load (native-compile (ert-resource-file "comp-test-45603.el")))
+  (should (fboundp #'comp-test-45603--file-local-name)))
+
+(comp-deftest 46670-1 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-02/msg01413.html>"
+  (should (string= (comp-test-46670-2-f "foo") "foo"))
+  (should (equal (subr-type (symbol-function #'comp-test-46670-2-f))
+                 '(function (t) t))))
+
+(comp-deftest 46824-1 ()
+  "<https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-02/msg01949.html>"
+  (should (equal (comp-test-46824-1-f) nil)))
+
+(comp-deftest comp-test-47868-1 ()
+  "Verify string hash consing strategy.
+
+<https://lists.gnu.org/archive/html/bug-gnu-emacs/2021-04/msg00921.html>"
+  (should-not (equal-including-properties (comp-test-47868-1-f)
+                                          (comp-test-47868-2-f)))
+  (should (eq (comp-test-47868-1-f) (comp-test-47868-3-f)))
+  (should (eq (comp-test-47868-2-f) (comp-test-47868-4-f))))
+
+
+;;;;;;;;;;;;;;;;;;;;;
+;; Tromey's tests. ;;
+;;;;;;;;;;;;;;;;;;;;;
+
+(comp-deftest consp ()
+  (should-not (comp-test-consp 23))
+  (should-not (comp-test-consp nil))
+  (should (comp-test-consp '(1 . 2))))
+
+(comp-deftest listp ()
+  (should-not (comp-test-listp 23))
+  (should (comp-test-listp nil))
+  (should (comp-test-listp '(1 . 2))))
+
+(comp-deftest stringp ()
+  (should-not (comp-test-stringp 23))
+  (should-not (comp-test-stringp nil))
+  (should (comp-test-stringp "hi")))
+
+(comp-deftest symbolp ()
+  (should-not (comp-test-symbolp 23))
+  (should-not (comp-test-symbolp "hi"))
+  (should (comp-test-symbolp 'whatever)))
+
+(comp-deftest integerp ()
+  (should (comp-test-integerp 23))
+  (should-not (comp-test-integerp 57.5))
+  (should-not (comp-test-integerp "hi"))
+  (should-not (comp-test-integerp 'whatever)))
+
+(comp-deftest numberp ()
+  (should (comp-test-numberp 23))
+  (should (comp-test-numberp 57.5))
+  (should-not (comp-test-numberp "hi"))
+  (should-not (comp-test-numberp 'whatever)))
+
+(comp-deftest add1 ()
+  (should (eq (comp-test-add1 23) 24))
+  (should (eq (comp-test-add1 -17) -16))
+  (should (eql (comp-test-add1 1.0) 2.0))
+  (should-error (comp-test-add1 nil)
+                :type 'wrong-type-argument))
+
+(comp-deftest sub1 ()
+  (should (eq (comp-test-sub1 23) 22))
+  (should (eq (comp-test-sub1 -17) -18))
+  (should (eql (comp-test-sub1 1.0) 0.0))
+  (should-error (comp-test-sub1 nil)
+                :type 'wrong-type-argument))
+
+(comp-deftest negate ()
+  (should (eq (comp-test-negate 23) -23))
+  (should (eq (comp-test-negate -17) 17))
+  (should (eql (comp-test-negate 1.0) -1.0))
+  (should-error (comp-test-negate nil)
+                :type 'wrong-type-argument))
+
+(comp-deftest not ()
+  (should (eq (comp-test-not 23) nil))
+  (should (eq (comp-test-not nil) t))
+  (should (eq (comp-test-not t) nil)))
+
+(comp-deftest bobp-and-eobp ()
+  (with-temp-buffer
+    (should (comp-test-bobp))
+    (should (comp-test-eobp))
+    (insert "hi")
+    (goto-char (point-min))
+    (should (eq (comp-test-point-min) (point-min)))
+    (should (eq (comp-test-point) (point-min)))
+    (should (comp-test-bobp))
+    (should-not (comp-test-eobp))
+    (goto-char (point-max))
+    (should (eq (comp-test-point-max) (point-max)))
+    (should (eq (comp-test-point) (point-max)))
+    (should-not (comp-test-bobp))
+    (should (comp-test-eobp))))
+
+(comp-deftest car-cdr ()
+  (let ((pair '(1 . b)))
+    (should (eq (comp-test-car pair) 1))
+    (should (eq (comp-test-car nil) nil))
+    (should-error (comp-test-car 23)
+                  :type 'wrong-type-argument)
+    (should (eq (comp-test-cdr pair) 'b))
+    (should (eq (comp-test-cdr nil) nil))
+    (should-error (comp-test-cdr 23)
+                  :type 'wrong-type-argument)))
+
+(comp-deftest car-cdr-safe ()
+  (let ((pair '(1 . b)))
+    (should (eq (comp-test-car-safe pair) 1))
+    (should (eq (comp-test-car-safe nil) nil))
+    (should (eq (comp-test-car-safe 23) nil))
+    (should (eq (comp-test-cdr-safe pair) 'b))
+    (should (eq (comp-test-cdr-safe nil) nil))
+    (should (eq (comp-test-cdr-safe 23) nil))))
+
+(comp-deftest eq ()
+  (should (comp-test-eq 'a 'a))
+  (should (comp-test-eq 5 5))
+  (should-not (comp-test-eq 'a 'b)))
+
+(comp-deftest if ()
+  (should (eq (comp-test-if 'a 'b) 'a))
+  (should (eq (comp-test-if 0 23) 0))
+  (should (eq (comp-test-if nil 'b) 'b)))
+
+(comp-deftest and ()
+  (should (eq (comp-test-and 'a 'b) 'b))
+  (should (eq (comp-test-and 0 23) 23))
+  (should (eq (comp-test-and nil 'b) nil)))
+
+(comp-deftest or ()
+  (should (eq (comp-test-or 'a 'b) 'a))
+  (should (eq (comp-test-or 0 23) 0))
+  (should (eq (comp-test-or nil 'b) 'b)))
+
+(comp-deftest save-excursion ()
+  (with-temp-buffer
+    (comp-test-save-excursion)
+    (should (eq (point) (point-min)))
+    (should (eq (comp-test-current-buffer) (current-buffer)))))
+
+(comp-deftest > ()
+  (should (eq (comp-test-> 0 23) nil))
+  (should (eq (comp-test-> 23 0) t)))
+
+(comp-deftest catch ()
+  (should (eq (comp-test-catch 0 1 2 3 4) nil))
+  (should (eq (comp-test-catch 20 21 22 23 24 25 26 27 28) 24)))
+
+(comp-deftest memq ()
+  (should (equal (comp-test-memq 0 '(5 4 3 2 1 0)) '(0)))
+  (should (eq (comp-test-memq 72 '(5 4 3 2 1 0)) nil)))
+
+(comp-deftest listN ()
+  (should (equal (comp-test-listN 57)
+                 '(57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57))))
+
+(comp-deftest concatN ()
+  (should (equal (comp-test-concatN "x") "xxxxxx")))
+
+(comp-deftest opt-rest ()
+  (should (equal (comp-test-opt-rest 1) '(1 nil nil)))
+  (should (equal (comp-test-opt-rest 1 2) '(1 2 nil)))
+  (should (equal (comp-test-opt-rest 1 2 3) '(1 2 (3))))
+  (should (equal (comp-test-opt-rest 1 2 56 57 58)
+                 '(1 2 (56 57 58)))))
+
+(comp-deftest opt ()
+  (should (equal (comp-test-opt 23) '(23)))
+  (should (equal (comp-test-opt 23 24) '(23 . 24)))
+  (should-error (comp-test-opt)
+                :type 'wrong-number-of-arguments)
+  (should-error (comp-test-opt nil 24 97)
+                :type 'wrong-number-of-arguments))
+
+(comp-deftest unwind-protect ()
+  (comp-test-unwind-protect 'ignore)
+  (should (eq comp-test-up-val 999))
+  (condition-case nil
+      (comp-test-unwind-protect (lambda () (error "HI")))
+    (error
+     nil))
+  (should (eq comp-test-up-val 999)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests for dynamic scope. ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(comp-deftest dynamic-ffuncall ()
+  "Test calling convention for dynamic binding."
+
+  (should (equal (comp-tests-ffuncall-callee-dyn-f 1 2)
+                 '(1 2)))
+
+  (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3 4)
+                 '(1 2 3 4)))
+  (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3)
+                 '(1 2 3 nil)))
+  (should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2)
+                 '(1 2 nil nil)))
+
+  (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2)
+                 '(1 2 nil)))
+  (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3)
+                 '(1 2 (3))))
+  (should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3 4)
+                 '(1 2 (3 4))))
+
+  (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2)
+                 '(1 2 nil nil)))
+  (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3)
+                 '(1 2 3 nil)))
+  (should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3 4)
+                 '(1 2 3 (4)))))
+
+(comp-deftest dynamic-arity ()
+  "Test func-arity on dynamic scope functions."
+  (should (equal '(2 . 2)
+                 (func-arity #'comp-tests-ffuncall-callee-dyn-f)))
+  (should (equal '(2 . 4)
+                 (func-arity #'comp-tests-ffuncall-callee-opt-dyn-f)))
+  (should (equal '(2 . many)
+                 (func-arity #'comp-tests-ffuncall-callee-rest-dyn-f)))
+  (should (equal '(2 . many)
+                 (func-arity #'comp-tests-ffuncall-callee-opt-rest-dyn-f))))
+
+(comp-deftest dynamic-help-arglist ()
+  "Test `help-function-arglist' works on lisp/d (bug#42572)."
+  (should (equal (help-function-arglist
+                  (symbol-function #'comp-tests-ffuncall-callee-opt-rest-dyn-f)
+                  t)
+                 '(a b &optional c &rest d))))
+
+(comp-deftest cl-macro-exp ()
+  "Verify CL macro expansion (bug#42088)."
+  (should (equal (comp-tests-cl-macro-exp-f) '(a b))))
+
+(comp-deftest cl-uninterned-arg-parse-f ()
+  "Verify the parsing of a lambda list with uninterned symbols (bug#42120)."
+  (should (equal (comp-tests-cl-uninterned-arg-parse-f 1 2)
+                 '(1 2))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Middle-end specific tests. ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun comp-tests-mentioned-p-1 (x insn)
+  (cl-loop for y in insn
+           when (cond
+                 ((consp y) (comp-tests-mentioned-p x y))
+                 ((and (comp-mvar-p y) (comp-cstr-imm-vld-p y))
+                  (equal (comp-cstr-imm y) x))
+                 (t (equal x y)))
+             return t))
+
+(defun comp-tests-mentioned-p (x insn)
+  "Check if X is actively mentioned in INSN."
+  (unless (eq (car-safe insn)
+              'comment)
+    (comp-tests-mentioned-p-1 x insn)))
+
+(defun comp-tests-map-checker (func-name checker)
+  "Apply CHECKER to each insn of FUNC-NAME.
+Return a list of results."
+  (cl-loop
+    with func-c-name = (comp-c-func-name (or func-name 'anonymous-lambda) "F" 
t)
+    with f = (gethash func-c-name (comp-ctxt-funcs-h comp-ctxt))
+    for bb being each hash-value of (comp-func-blocks f)
+    nconc
+    (cl-loop
+     for insn in (comp-block-insns bb)
+     collect (funcall checker insn))))
+
+(defun comp-tests-tco-checker (_)
+  "Check that inside `comp-tests-tco-f' we have no recursion."
+  (should
+   (cl-notany
+    #'identity
+    (comp-tests-map-checker
+     'comp-tests-tco-f
+     (lambda (insn)
+       (or (comp-tests-mentioned-p 'comp-tests-tco-f insn)
+           (comp-tests-mentioned-p (comp-c-func-name 'comp-tests-tco-f "F" t)
+                                   insn)))))))
+
+(comp-deftest tco ()
+  "Check for tail recursion elimination."
+  (let ((comp-speed 3)
+        ;; Disable ipa-pure otherwise `comp-tests-tco-f' gets
+        ;; optimized-out.
+        (comp-disabled-passes '(comp-ipa-pure))
+        (comp-post-pass-hooks '((comp-tco comp-tests-tco-checker)
+                                (comp-final comp-tests-tco-checker))))
+    (eval '(defun comp-tests-tco-f (a b count)
+             (if (= count 0)
+                 b
+               (comp-tests-tco-f (+ a b) a (- count 1))))
+          t)
+    (native-compile #'comp-tests-tco-f)
+    (should (subr-native-elisp-p (symbol-function #'comp-tests-tco-f)))
+    (should (= (comp-tests-tco-f 1 0 10) 55))))
+
+(defun comp-tests-fw-prop-checker-1 (_)
+  "Check that inside `comp-tests-fw-prop-f' `concat' and `length' are folded."
+  (should
+   (cl-notany
+    #'identity
+    (comp-tests-map-checker
+     'comp-tests-fw-prop-1-f
+     (lambda (insn)
+       (or (comp-tests-mentioned-p 'concat insn)
+           (comp-tests-mentioned-p 'length insn)))))))
+
+(comp-deftest fw-prop-1 ()
+  "Some tests for forward propagation."
+  (let ((comp-speed 2)
+        (comp-post-pass-hooks '((comp-final comp-tests-fw-prop-checker-1))))
+    (eval '(defun comp-tests-fw-prop-1-f ()
+             (let* ((a "xxx")
+                   (b "yyy")
+                   (c (concat a b))) ; <= has to optimize
+               (length c))) ; <= has to optimize
+          t)
+    (native-compile #'comp-tests-fw-prop-1-f)
+    (should (subr-native-elisp-p (symbol-function #'comp-tests-fw-prop-1-f)))
+    (should (= (comp-tests-fw-prop-1-f) 6))))
+
+(defun comp-tests-check-ret-type-spec (func-form ret-type)
+  (let ((lexical-binding t)
+        (comp-speed 2)
+        (f-name (cl-second func-form)))
+    (eval func-form t)
+    (native-compile f-name)
+    (should (equal (cl-third (subr-type (symbol-function f-name)))
+                   ret-type))))
+
+(cl-eval-when (compile eval load)
+  (defconst comp-tests-type-spec-tests
+    `(
+      ;; 1
+      ((defun comp-tests-ret-type-spec-f (x)
+         x)
+       t)
+
+      ;; 2
+      ((defun comp-tests-ret-type-spec-f ()
+         1)
+       (integer 1 1))
+
+      ;; 3
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if x 1 3))
+       (or (integer 1 1) (integer 3 3)))
+
+      ;; 4
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let (y)
+           (if x
+               (setf y 1)
+             (setf y 2))
+           y))
+       (integer 1 2))
+
+      ;; 5
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let (y)
+           (if x
+               (setf y 1)
+             (setf y 3))
+           y))
+       (or (integer 1 1) (integer 3 3)))
+
+      ;; 6
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if x
+             (list x)
+           3))
+       (or cons (integer 3 3)))
+
+      ;; 7
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if x
+             'foo
+           3))
+       (or (member foo) (integer 3 3)))
+
+      ;; 8
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (eq x 3)
+             x
+           'foo))
+       (or (member foo) (integer 3 3)))
+
+      ;; 9
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (eq 3 x)
+             x
+           'foo))
+       (or (member foo) (integer 3 3)))
+
+      ;; 10
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (eql x 3)
+             x
+           'foo))
+       (or (member foo) (integer 3 3)))
+
+      ;; 11
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (eql 3 x)
+             x
+           'foo))
+       (or (member foo) (integer 3 3)))
+
+      ;; 12
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (eql x 3)
+             'foo
+           x))
+       (not (integer 3 3)))
+
+      ;; 13
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (if (= x y)
+             x
+           'foo))
+       (or (member foo) marker number))
+
+      ;; 14
+      ((defun comp-tests-ret-type-spec-f (x)
+         (comp-hint-fixnum x))
+       (integer ,most-negative-fixnum ,most-positive-fixnum))
+
+      ;; 15
+      ((defun comp-tests-ret-type-spec-f (x)
+         (comp-hint-cons x))
+       cons)
+
+      ;; 16
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let (y)
+           (when x
+             (setf y 4))
+           y))
+       (or null (integer 4 4)))
+
+      ;; 17
+      ((defun comp-tests-ret-type-spec-f ()
+         (let (x
+               (y 3))
+           (setf x y)
+           y))
+       (integer 3 3))
+
+      ;; 18
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let ((y 3))
+           (when x
+             (setf y x))
+           y))
+       t)
+
+      ;; 19
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (eq x y))
+       boolean)
+
+      ;; 20
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when x
+           'foo))
+       (or (member foo) null))
+
+      ;; 21
+      ((defun comp-tests-ret-type-spec-f (x)
+         (unless x
+           'foo))
+       (or (member foo) null))
+
+      ;; 22
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (> x 3)
+          x))
+       (or null float (integer 4 *)))
+
+      ;; 23
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (>= x 3)
+          x))
+       (or null float (integer 3 *)))
+
+      ;; 24
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (< x 3)
+          x))
+       (or null float (integer * 2)))
+
+      ;; 25
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (<= x 3)
+          x))
+       (or null float (integer * 3)))
+
+      ;; 26
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (> 3 x)
+          x))
+       (or null float (integer * 2)))
+
+      ;; 27
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (>= 3 x)
+          x))
+       (or null float (integer * 3)))
+
+      ;; 28
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (< 3 x)
+          x))
+       (or null float (integer 4 *)))
+
+      ;; 29
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (<= 3 x)
+          x))
+       (or null float (integer 3 *)))
+
+      ;; 30
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let ((y 3))
+          (when (> x y)
+            x)))
+       (or null float (integer 4 *)))
+
+      ;; 31
+      ((defun comp-tests-ret-type-spec-f (x)
+         (let ((y 3))
+          (when (> y x)
+            x)))
+       (or null float (integer * 2)))
+
+      ;; 32
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (and (> x 3)
+                   (< x 10))
+          x))
+       (or null float (integer 4 9)))
+
+      ;; 33
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (or (> x 3)
+                   (< x 10))
+          x))
+       (or null float integer))
+
+      ;; 34
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (or (< x 3)
+                   (> x 10))
+          x))
+       (or null float (integer * 2) (integer 11 *)))
+
+      ;; 35 No float range support.
+      ((defun comp-tests-ret-type-spec-f (x)
+        (when (> x 1.0)
+          x))
+       (or null marker number))
+
+      ;; 36
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (> x 3)
+                    (> y 2))
+           (+ x y)))
+       (or null float (integer 7 *)))
+
+      ;; 37
+      ;; SBCL: (OR REAL NULL)
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (<= x 3)
+                    (<= y 2))
+           (+ x y)))
+       (or null float (integer * 5)))
+
+      ;; 38
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (< 1 x 5)
+                   (< 1 y 5))
+           (+ x y)))
+       (or null float (integer 4 8)))
+
+      ;; 39
+      ;; SBCL gives: (OR REAL NULL)
+      ((defun comp-tests-ret-type-spec-f (x y)
+        (when (and (<= 1 x 10)
+                   (<= 2 y 3))
+          (+ x y)))
+       (or null float (integer 3 13)))
+
+      ;; 40
+      ;; SBCL: (OR REAL NULL)
+      ((defun comp-tests-ret-type-spec-f (x y)
+        (when (and (<= 1 x 10)
+                   (<= 2 y 3))
+          (- x y)))
+       (or null float (integer -2 8)))
+
+      ;; 41
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (<= 1 x)
+                    (<= 2 y 3))
+           (- x y)))
+       (or null float (integer -2 *)))
+
+      ;; 42
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (<= 1 x 10)
+                    (<= 2 y))
+           (- x y)))
+       (or null float (integer * 8)))
+
+      ;; 43
+      ((defun comp-tests-ret-type-spec-f (x y)
+        (when (and (<= x 10)
+                   (<= 2 y))
+          (- x y)))
+       (or null float (integer * 8)))
+
+      ;; 44
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (<= x 10)
+                    (<= y 3))
+           (- x y)))
+       (or null float integer))
+
+      ;; 45
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (when (and (<= 2 x)
+                    (<= 3 y))
+           (- x y)))
+       (or null float integer))
+
+      ;; 46
+      ;; SBCL: (OR (RATIONAL (6) (30)) (SINGLE-FLOAT 6.0 30.0)
+      ;;           (DOUBLE-FLOAT 6.0d0 30.0d0) NULL)
+      ((defun comp-tests-ret-type-spec-f (x y z i j k)
+         (when (and (< 1 x 5)
+                   (< 1 y 5)
+                   (< 1 z 5)
+                   (< 1 i 5)
+                   (< 1 j 5)
+                   (< 1 k 5))
+           (+ x y z i j k)))
+       (or null float (integer 12 24)))
+
+      ;; 47
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (<= 1 x 5)
+           (1+ x)))
+       (or null float (integer 2 6)))
+
+      ;;48
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (<= 1 x 5)
+           (1- x)))
+       (or null float (integer 0 4)))
+
+      ;; 49
+      ((defun comp-tests-ret-type-spec-f ()
+         (error "foo"))
+       nil)
+
+      ;; 50
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (stringp x)
+            x
+           'bar))
+       (or (member bar) string))
+
+      ;; 51
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (stringp x)
+             'bar
+           x))
+       (not string))
+
+      ;; 52
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (integerp x)
+             x
+           'bar))
+       (or (member bar) integer))
+
+      ;; 53
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (integerp x)
+           x))
+       (or null integer))
+
+      ;; 54
+      ((defun comp-tests-ret-type-spec-f (x)
+         (unless (symbolp x)
+           x))
+       t)
+
+      ;; 55
+      ((defun comp-tests-ret-type-spec-f (x)
+         (unless (integerp x)
+           x))
+       (not integer))
+
+      ;; 56
+      ((defun comp-tests-ret-type-spec-f (x)
+         (cl-ecase x
+           (1 (message "one"))
+           (5 (message "five")))
+         x)
+       t
+       ;; FIXME improve `comp-cond-cstrs-target-mvar' to cross block
+       ;; boundary if necessary as this should return:
+       ;; (or (integer 1 1) (integer 5 5))
+       )
+
+      ;; 57
+      ((defun comp-tests-ret-type-spec-f (x)
+         (unless (or (eq x 'foo)
+                    (eql x 3))
+           (error "Not foo or 3"))
+         x)
+       (or (member foo) (integer 3 3)))
+
+      ;;58
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (if (and (natnump x)
+                  (natnump y)
+                  (<= x y))
+             x
+           (error "")))
+       (integer 0 *))
+
+      ;; 59
+      ((defun comp-tests-ret-type-spec-f (x y)
+         (if (and (>= x 3)
+                  (<= y 10)
+                  (<= x y))
+             x
+           (error "")))
+       (or float (integer 3 10)))
+
+      ;; 60
+      ((defun comp-tests-ret-type-spec-f (x y)
+                   (if (and (<= x 10)
+                            (>= y 3)
+                            (>= x y))
+             x
+           (error "")))
+       (or float (integer 3 10)))
+
+      ;; 61
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (= x 1.0)
+             x
+           (error "")))
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 62
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (= x 1.0)
+             x
+           (error "")))
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 63
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (= x 1.1)
+             x
+           (error "")))
+       (member 1.1))
+
+      ;; 64
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (= x 1)
+             x
+           (error "")))
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 65
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (= x 1)
+             x
+           (error "")))
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 66
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (eql x 0.0)
+            x
+          (error "")))
+       float)
+
+      ;; 67
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (equal x '(1 2 3))
+            x
+          (error "")))
+       cons)
+
+      ;; 68
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (and (floatp x)
+                 (= x 1))
+             x
+           (error "")))
+       ;; Conservative (see cstr relax in `comp-cstr-=').
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 69
+      ((defun comp-tests-ret-type-spec-f (x)
+        (if (and (integer x)
+                 (= x 1))
+             x
+           (error "")))
+       ;; Conservative (see cstr relax in `comp-cstr-=').
+       (or (member 1.0) (integer 1 1)))
+
+      ;; 70
+      ((defun comp-tests-ret-type-spec-f (x y)
+        (if (and (floatp x)
+                 (integerp y)
+                 (= x y))
+             x
+           (error "")))
+       (or float integer))
+
+      ;; 71
+      ((defun comp-tests-ret-type-spec-f (x)
+         (if (= x 0.0)
+             x
+           (error "")))
+       (or (member -0.0 0.0) (integer 0 0)))
+
+      ;; 72
+      ((defun comp-tests-ret-type-spec-f (x)
+         (unless (= x 0.0)
+           (error ""))
+         (unless (eql x -0.0)
+           (error ""))
+         x)
+       float)
+
+      ;; 73
+      ((defun comp-tests-ret-type-spec-f (x)
+         (when (eql x 1.0)
+          (error ""))
+         x)
+       t)))
+
+  (defun comp-tests-define-type-spec-test (number x)
+    `(comp-deftest ,(intern (format "ret-type-spec-%d" number)) ()
+                   ,(format "Type specifier test number %d." number)
+                   (let ((comp-ctxt (make-comp-cstr-ctxt)))
+                     (comp-tests-check-ret-type-spec ',(car x) ',(cadr x))))))
+
+(defmacro comp-tests-define-type-spec-tests ()
+  "Define all type specifier tests."
+  `(progn
+     ,@(cl-loop
+        for test in comp-tests-type-spec-tests
+        for n from 1
+        collect (comp-tests-define-type-spec-test n test))))
+
+(comp-tests-define-type-spec-tests)
+
+(defun comp-tests-pure-checker-1 (_)
+  "Check that inside `comp-tests-pure-caller-f' `comp-tests-pure-callee-f' is
+ folded."
+  (should
+   (cl-notany
+    #'identity
+    (comp-tests-map-checker
+     'comp-tests-pure-caller-f
+     (lambda (insn)
+       (or (comp-tests-mentioned-p 'comp-tests-pure-callee-f insn)
+           (comp-tests-mentioned-p (comp-c-func-name
+                                    'comp-tests-pure-callee-f "F" t)
+                                   insn)))))))
+
+(defun comp-tests-pure-checker-2 (_)
+  "Check that `comp-tests-pure-fibn-f' is folded."
+  (should
+   (cl-notany
+    #'identity
+    (comp-tests-map-checker
+     'comp-tests-pure-fibn-entry-f
+     (lambda (insn)
+       (or (comp-tests-mentioned-p 'comp-tests-pure-fibn-f insn)
+           (comp-tests-mentioned-p (comp-c-func-name 'comp-tests-pure-fibn-f 
"F" t)
+                                   insn)))))))
+
+(comp-deftest pure ()
+  "Some tests for pure functions optimization."
+  (let ((comp-speed 3)
+        (comp-post-pass-hooks '((comp-final comp-tests-pure-checker-1
+                                            comp-tests-pure-checker-2))))
+    (load (native-compile (ert-resource-file "comp-test-pure.el")))
+
+    (should (subr-native-elisp-p (symbol-function #'comp-tests-pure-caller-f)))
+    (should (= (comp-tests-pure-caller-f) 4))
+
+    (should (subr-native-elisp-p (symbol-function 
#'comp-tests-pure-fibn-entry-f)))
+    (should (= (comp-tests-pure-fibn-entry-f) 6765))))
+
+(defvar comp-tests-cond-rw-checked-function nil
+  "Function to be checked.")
+(defun comp-tests-cond-rw-checker-val (_)
+  "Check we manage to propagate the correct return value."
+  (should
+   (cl-some
+    #'identity
+    (comp-tests-map-checker
+     comp-tests-cond-rw-checked-function
+     (lambda (insn)
+       (pcase insn
+         (`(return ,mvar)
+          (and (comp-cstr-imm-vld-p mvar)
+               (eql (comp-cstr-imm mvar) 123)))))))))
+
+(defvar comp-tests-cond-rw-expected-type nil
+  "Type to expect in `comp-tests-cond-rw-checker-type'.")
+(defun comp-tests-cond-rw-checker-type (_)
+  "Check we manage to propagate the correct return type."
+  (should
+   (cl-some
+    #'identity
+    (comp-tests-map-checker
+     comp-tests-cond-rw-checked-function
+     (lambda (insn)
+       (pcase insn
+         (`(return ,mvar)
+          (equal (comp-mvar-typeset mvar)
+                 comp-tests-cond-rw-expected-type))))))))
+
+;;; comp-tests.el ends here
diff --git a/test/src/emacs-tests.el b/test/src/emacs-tests.el
index 87c3e84..ee5586f 100644
--- a/test/src/emacs-tests.el
+++ b/test/src/emacs-tests.el
@@ -1,6 +1,6 @@
 ;;; emacs-tests.el --- unit tests for emacs.c -*- lexical-binding: t; -*-
 
-;; Copyright (C) 2020  Free Software Foundation, Inc.
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
 
 ;; This file is part of GNU Emacs.
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]