emacs-diffs
[Top][All Lists]
Advanced

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

master 289000e: Merge branch 'feature/native-comp' into into trunk


From: Andrea Corallo
Subject: master 289000e: Merge branch 'feature/native-comp' into into trunk
Date: Sun, 25 Apr 2021 14:25:07 -0400 (EDT)

branch: master
commit 289000eee729689b0cf362a21baa40ac7f9506f6
Merge: 8f63f00 fa65c04
Author: Andrea Corallo <akrl@sdf.org>
Commit: Andrea Corallo <akrl@sdf.org>

    Merge branch 'feature/native-comp' into into trunk
---
 .gitignore                              |    1 +
 Makefile.in                             |   31 +-
 admin/MAINTAINERS                       |    7 +
 configure.ac                            |  133 +-
 etc/NEWS                                |   18 +
 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            | 1187 +++++++
 lisp/emacs-lisp/comp.el                 | 4210 ++++++++++++++++++++++++
 lisp/emacs-lisp/disass.el               |   29 +-
 lisp/emacs-lisp/find-func.el            |    9 +-
 lisp/emacs-lisp/nadvice.el              |   18 +
 lisp/emacs-lisp/package.el              |   33 +-
 lisp/faces.el                           |    3 +-
 lisp/files.el                           |    7 +-
 lisp/gnus/gnus.el                       |    5 +-
 lisp/help-fns.el                        |    2 +
 lisp/help.el                            |    6 +
 lisp/international/mule.el              |    5 +-
 lisp/loadup.el                          |   46 +-
 lisp/mail/emacsbug.el                   |    2 +-
 lisp/progmodes/cc-bytecomp.el           |   11 +-
 lisp/progmodes/cc-langs.el              |    3 +-
 lisp/progmodes/elisp-mode.el            |   26 +-
 lisp/startup.el                         |   25 +
 lisp/subr.el                            |    7 +-
 lisp/term/w32-win.el                    |    3 +-
 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                              |  113 +
 src/data.c                              |   95 +-
 src/decompress.c                        |  102 +
 src/doc.c                               |   12 +-
 src/dynlib.c                            |    4 -
 src/emacs.c                             |  178 +-
 src/epaths.in                           |    4 +
 src/eval.c                              |  121 +-
 src/fns.c                               |   43 +-
 src/image.c                             |    4 +-
 src/lisp.h                              |   76 +-
 src/lread.c                             |  229 +-
 src/pdumper.c                           |  331 +-
 src/pdumper.h                           |   15 +-
 src/print.c                             |   13 +-
 src/process.c                           |    2 +-
 src/sound.c                             |    5 +-
 src/verbose.mk.in                       |    8 +
 src/w32.c                               |   28 +-
 src/w32.h                               |    3 +
 src/w32common.h                         |    8 +
 src/w32proc.c                           |   13 +-
 src/window.c                            |    2 +-
 test/Makefile.in                        |   10 +
 test/infra/gitlab-ci.yml                |   32 +
 test/lisp/auth-source-tests.el          |    3 +-
 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-test-45603.el             |   28 +
 test/src/comp-test-funcs-dyn.el         |   50 +
 test/src/comp-test-funcs.el             |  710 ++++
 test/src/comp-test-pure.el              |   40 +
 test/src/comp-tests.el                  | 1446 +++++++++
 77 files changed, 15420 insertions(+), 256 deletions(-)

diff --git a/.gitignore b/.gitignore
index c262f39..fcbc9cd 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 f04ba0c..8d52cb5 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.
@@ -206,6 +208,10 @@ iconsrcdir=$(srcdir)/etc/images/icons
 # 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.
@@ -315,6 +321,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
+
 all: ${SUBDIR} info
 
 .PHONY: all ${SUBDIR} blessmail epaths-force epaths-force-w32 etc-emacsver
@@ -349,6 +363,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'            \
@@ -379,6 +394,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") &&                \
@@ -406,7 +422,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
@@ -446,14 +463,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: all install-arch-indep install-etcdoc install-arch-dep 
install-$(NTDIR) blessmail install-eln
        @true
 
 ## Ensure that $subdir contains a subdirs.el file.
@@ -733,6 +750,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
@@ -842,6 +866,7 @@ clean: $(clean_dirs:=_clean)
        [ ! -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/configure.ac b/configure.ac
index a1629d7..3df4359 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'
@@ -483,6 +484,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)])],
@@ -1889,6 +1891,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
@@ -3660,6 +3663,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" ;;
@@ -3695,7 +3699,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])
@@ -3722,6 +3727,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=
@@ -5258,6 +5381,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)
@@ -5701,8 +5825,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 PNG RSVG SECCOMP SOUND \
- THREADS TIFF \
+ M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PNG RSVG SECCOMP \
+ SOUND THREADS TIFF \
  TOOLKIT_SCROLL_BARS UNEXEC X11 XAW3D XDBE XFT XIM XPM XWIDGETS X_TOOLKIT \
  ZLIB; do
 
@@ -5778,6 +5902,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/etc/NEWS b/etc/NEWS
index 63de46a..7e30941 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,11 @@ 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, configure Emacs with the '--with-native-compilation' option
+to the 'configure' script.  This requires to have the libgccjit
+library to be installed and functional.
+
 ---
 ** Support for building with Motif has been removed.
 
@@ -1282,6 +1287,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 +2648,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..5b189e7
--- /dev/null
+++ b/lisp/emacs-lisp/comp-cstr.el
@@ -0,0 +1,1187 @@
+;;; comp-cstr.el --- native compiler constraint library -*- lexical-binding: t 
-*-
+
+;; Author: Andrea Corallo <akrl@sdf.com>
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; 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)))
+
+(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..ab5a06e
--- /dev/null
+++ b/lisp/emacs-lisp/comp.el
@@ -0,0 +1,4210 @@
+;;; comp.el --- compilation of Lisp code into native code -*- lexical-binding: 
t -*-
+
+;; Author: Andrea Corallo <akrl@sdf.com>
+
+;; Copyright (C) 2019-2021 Free Software Foundation, Inc.
+
+;; 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 is excluded from native compilation."
+  :type 'list
+  :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 is excluded from native compilation
+during bootstrap."
+  :type 'list
+  :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 'list
+  :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")
+
+;; FIXME: This an abnormal hook, and should be renamed to something
+;; like `comp-async-cu-done-function'.
+(defcustom comp-async-cu-done-hook nil
+  "Hook run after asynchronously compiling a single 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 'list
+  :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 'list
+  :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) integer))
+    (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 correspondig 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 comparision 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' intitializer."
+  (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 funciton."
+  (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 asyncronous 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-hook
+                                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..a0d859b 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)
@@ -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/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 f2e83d3..1ce2940 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
@@ -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
@@ -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/faces.el b/lisp/faces.el
index 42f4cdd..68bfbba 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2244,7 +2244,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/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.el b/lisp/help.el
index e143501..63f9974 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -1870,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
@@ -1885,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 d6cfcd6..c82d081 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
@@ -448,6 +449,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)
@@ -483,6 +521,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)
@@ -539,6 +582,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/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-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/startup.el b/lisp/startup.el
index c126727..89d4c8a 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -536,6 +536,21 @@ 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'.
+      (defvar 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/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 f3c545d..b8bad73 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@
@@ -326,6 +326,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.
@@ -392,7 +397,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) \
@@ -495,6 +500,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.
@@ -510,7 +516,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(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,
@@ -560,7 +566,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
 
@@ -791,7 +798,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 00c1fd3..76d8c7d 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.  */
@@ -6725,6 +6745,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:
@@ -6869,7 +6898,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:
@@ -7629,14 +7660,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..03d22df
--- /dev/null
+++ b/src/comp.h
@@ -0,0 +1,113 @@
+/* Elisp native compiler definitions
+Copyright (C) 2019-2020 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/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/emacs.c b/src/emacs.c
index 362e4a2..9157cd84 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 */
 
@@ -1809,6 +1845,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);
 
@@ -1980,7 +2019,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);
@@ -2160,6 +2200,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
     {
@@ -2598,6 +2643,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
@@ -3248,7 +3297,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/image.c b/src/image.c
index ff4ef01..f2fb69a 100644
--- a/src/image.c
+++ b/src/image.c
@@ -511,7 +511,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;
 
@@ -3152,7 +3152,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/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/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/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 b98bc29..84e301a 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/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/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/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/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..b740f43 100644
--- a/test/infra/gitlab-ci.yml
+++ b/test/infra/gitlab-ci.yml
@@ -243,6 +243,38 @@ test-filenotify-gio:
     target: emacs-filenotify-gio
     make_params: "-k -C test autorevert-tests.log filenotify-tests.log"
 
+test-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 sceduled.
+  # 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 --without-makeinfo --with-nativecomp
+    - make bootstrap NATIVE_FULL_AOT=1 BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq 
comp-speed 0)"' -j2
+  timeout: 8 hours
+
+test-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 --without-makeinfo --with-nativecomp
+    - make bootstrap BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq comp-speed 1)"'
+  timeout: 8 hours
+
+test-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 --without-makeinfo --with-nativecomp
+    - make bootstrap
+  timeout: 8 hours
+
 test-gnustep:
   # This tests the GNUstep build process
   stage: platforms
diff --git a/test/lisp/auth-source-tests.el b/test/lisp/auth-source-tests.el
index 4f0d994..19216d3 100644
--- a/test/lisp/auth-source-tests.el
+++ b/test/lisp/auth-source-tests.el
@@ -320,7 +320,8 @@
     ;; 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 _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..c2492b9
--- /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 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..8fa3917 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 _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-test-45603.el b/test/src/comp-test-45603.el
new file mode 100644
index 0000000..f1c0daf
--- /dev/null
+++ b/test/src/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-test-funcs-dyn.el b/test/src/comp-test-funcs-dyn.el
new file mode 100644
index 0000000..67db758
--- /dev/null
+++ b/test/src/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 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-test-funcs.el b/test/src/comp-test-funcs.el
new file mode 100644
index 0000000..f2a2463
--- /dev/null
+++ b/test/src/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-test-pure.el b/test/src/comp-test-pure.el
new file mode 100644
index 0000000..f606a44
--- /dev/null
+++ b/test/src/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 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..a1e91ec
--- /dev/null
+++ b/test/src/comp-tests.el
@@ -0,0 +1,1446 @@
+;;; 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 'cl-lib)
+(require 'comp)
+
+(defconst comp-test-directory (file-name-directory (or load-file-name
+                                                       buffer-file-name)))
+(defconst comp-test-src
+  (concat comp-test-directory "comp-test-funcs.el"))
+
+(defconst comp-test-dyn-src
+  (concat comp-test-directory "comp-test-funcs-dyn.el"))
+
+(when (featurep 'nativecomp)
+  (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 (concat comp-test-directory
+                              "../../lisp/emacs-lisp/comp.el"))
+         (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 (concat comp-test-directory "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 (concat comp-test-directory "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



reply via email to

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