From 0ce0b064b6fe09e57bfdfaa362c13bd7650f22b1 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 4 Jun 2020 14:13:14 -0400
Subject: [PATCH] FIXME: WIP on extended-asm support (v4)

Changes in v4:
* gcc_jit_context_add_top_level_asm:
  * document it
  * rename param from asm_insns to asm_stmts
  * updated test coverage to create a simple fn as per comment #29
* C++ support
* tweaks to documentation
* WIP on write_reproducer

Changes in v3:
* added gcc_jit_context_add_top_level_asm
* drop redundant gcc_jit_extended_asm_add_goto_label
* added gcc_jit_extended_asm_as_object
* started adding documentation
* added comments to libgccjit.h
* consolidated test-asm.c, grouping create/verify pairs
* add test coverage for "volatile"
* initial implementation of make_debug_string
* added some error-checking

Changes in v2:
* initial implementation of "asm goto"
---
 gcc/jit/docs/cp/topics/asm.rst        | 303 +++++++++++++++
 gcc/jit/docs/cp/topics/index.rst      |   1 +
 gcc/jit/docs/topics/asm.rst           | 306 +++++++++++++++
 gcc/jit/docs/topics/compatibility.rst |   2 +
 gcc/jit/docs/topics/functions.rst     |   2 +
 gcc/jit/docs/topics/index.rst         |   1 +
 gcc/jit/docs/topics/objects.rst       |   1 +
 gcc/jit/jit-common.h                  |   2 +
 gcc/jit/jit-playback.c                | 120 +++++-
 gcc/jit/jit-playback.h                |  27 ++
 gcc/jit/jit-recording.c               | 482 +++++++++++++++++++++++
 gcc/jit/jit-recording.h               | 208 ++++++++++
 gcc/jit/libgccjit++.h                 | 167 ++++++++
 gcc/jit/libgccjit.c                   | 162 +++++++-
 gcc/jit/libgccjit.h                   |  96 +++++
 gcc/jit/libgccjit.map                 |  15 +-
 gcc/testsuite/jit.dg/test-asm.c       | 528 ++++++++++++++++++++++++++
 gcc/testsuite/jit.dg/test-asm.cc      | 475 +++++++++++++++++++++++
 18 files changed, 2894 insertions(+), 4 deletions(-)
 create mode 100644 gcc/jit/docs/cp/topics/asm.rst
 create mode 100644 gcc/jit/docs/topics/asm.rst
 create mode 100644 gcc/testsuite/jit.dg/test-asm.c
 create mode 100644 gcc/testsuite/jit.dg/test-asm.cc

diff --git a/gcc/jit/docs/cp/topics/asm.rst b/gcc/jit/docs/cp/topics/asm.rst
new file mode 100644
index 00000000000..d7a4de32677
--- /dev/null
+++ b/gcc/jit/docs/cp/topics/asm.rst
@@ -0,0 +1,303 @@
+.. Copyright (C) 2020 Free Software Foundation, Inc.
+   Originally contributed by David Malcolm <dmalcolm@redhat.com>
+
+   This 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.
+
+   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
+   <http://www.gnu.org/licenses/>.
+
+.. default-domain:: cpp
+
+Using Assembly Language with libgccjit++
+========================================
+
+.. TODO: which ABI version for all of these entrypoints
+
+libgccjit has some support for directly embedding assembler instructions.
+This is based on GCC's support for inline ``asm`` in C code, and the
+following assumes a familiarity with that functionality.  See
+`How to Use Inline Assembly Language in C Code <https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html>`_
+in GCC's documentation, the "Extended Asm" section in particular.
+
+
+Adding assembler instructions within a function
+***********************************************
+
+.. class:: gccjit::extended_asm
+
+   A `gccjit::extended_asm` represents an extended ``asm`` statement: a
+   series of low-level instructions inside a function that convert inputs
+   to outputs.
+
+   :class:`gccjit::extended_asm` is a subclass of :class:`gccjit::object`.
+   It is a thin wrapper around the C API's :c:type:`gcc_jit_extended_asm *`.
+
+   To avoid having an API entrypoint with a very large number of
+   parameters, an extended ``asm`` statement is made in stages:
+   an initial call to create the :type:`gccjit::extended_asm`,
+   followed by calls to add operands and set other properties of the
+   statement.
+
+   There are two API entrypoints for creating a :type:`gccjit::extended_asm`:
+
+   * :func:`gccjit::block::add_extended_asm` for an ``asm`` statement with
+     no control flow, and
+
+   * :func:`gccjit::block::end_with_extended_asm_goto` for an ``asm goto``.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: // Quote from here in docs/cp/topics/asm.rst: example 1: C
+      :end-before: // Quote up to here in docs/cp/topics/asm.rst: example 1: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: /* Quote from here in docs/cp/topics/asm.rst: example 1: jit.  */
+      :end-before: /* Quote up to here in docs/cp/topics/asm.rst: example 1: jit.  */
+      :language: c
+
+   .. warning::  When considering the numbering of operands within an
+		 extended ``asm`` statement (e.g. the ``%0`` and ``%1``
+		 above), the equivalent to the C syntax is followed i.e. all
+		 output operands, then all input operands, regardless of
+		 what order the calls to
+		 :func:`gccjit::extended_asm::add_output_operand` and
+		 :func:`gccjit::extended_asm::add_input_operand` were made in.
+
+   As in the C syntax, operands can be given symbolic names to avoid having
+   to number them.  For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: // Quote from here in docs/cp/topics/asm.rst: example 2: C
+      :end-before: // Quote up to here in docs/cp/topics/asm.rst: example 2: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: /* Quote from here in docs/cp/topics/asm.rst: example 2: jit.  */
+      :end-before: /* Quote up to here in docs/cp/topics/asm.rst: example 2: jit.  */
+      :language: c
+
+.. function:: extended_asm \
+	      gccjit::block::add_extended_asm (const std::string &asm_template,\
+                                               gccjit::location loc = location ())
+
+   Create a :type:`gccjit::extended_asm` for an extended ``asm`` statement
+   with no control flow (i.e. without the ``goto`` qualifier).
+
+   The parameter ``asm_template`` corresponds to the `AssemblerTemplate`
+   within C's extended ``asm`` syntax.  It must be non-NULL.  The call takes
+   a copy of the underlying string, so it is valid to pass in a pointer to
+   an on-stack buffer.
+
+.. function:: extended_asm\
+              gccjit::block::end_with_extended_asm_goto (const std::string &asm_template,\
+                                                         std::vector<block> goto_blocks,\
+                                                         block *fallthrough_block,\
+                                                         location loc = location ())
+
+   Create a :type:`gccjit::extended_asm` for an extended ``asm`` statement
+   that may perform jumps, and use it to terminate the given block.
+   This is equivalent to the ``goto`` qualifier in C's extended ``asm``
+   syntax.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: // Quote from here in docs/cp/topics/asm.rst: example 3b: C
+      :end-before: // Quote up to here in docs/cp/topics/asm.rst: example 3b: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: /* Quote from here in docs/cp/topics/asm.rst: example 3: jit.  */
+      :end-before: /* Quote up to here in docs/cp/topics/asm.rst: example 3: jit.  */
+      :language: c
+
+   here referencing a :type:`gcc_jit_block` named "carry".
+
+   ``num_goto_blocks`` corresponds to the ``GotoLabels`` parameter within C's
+   extended ``asm`` syntax.  The block names can be referenced within the
+   assembler template.
+
+   ``fallthrough_block`` can be NULL.  If non-NULL, it specifies the block
+   to fall through to after the statement.
+
+   .. note:: This is needed since each :type:`gccjit::block` must have a
+	     single exit point, as a basic block: you can't jump from the
+	     middle of a block.  A "goto" is implicitly added after the
+	     asm to handle the fallthrough case, which is equivalent to what
+	     would have happened in the C case.
+
+.. function:: gccjit::extended_asm &\
+	      gccjit::extended_asm::set_volatile_flag (bool flag)
+
+   Set whether the :type:`gccjit::extended_asm` has side-effects, equivalent to the
+   `volatile <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile>`_
+   qualifier in C's extended asm syntax.
+
+   For example, to create the equivalent of:
+
+   .. code-block:: c
+
+      asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (msr)
+                     :
+                     : "rdx");
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: /* Quote from here in docs/cp/topics/asm.rst: example 4: jit.  */
+      :end-before: /* Quote up to here in docs/cp/topics/asm.rst: example 4: jit.  */
+      :language: c
+
+   where the :type:`gccjit::extended_asm` is flagged as volatile.
+
+.. function::  gccjit::extended_asm &\
+	       gccjit::extended_asm::set_inline_flag (bool flag)
+
+   Set the equivalent of the
+   `inline <https://gcc.gnu.org/onlinedocs/gcc/Size-of-an-asm.html#Size-of-an-asm>`_
+   qualifier in C's extended ``asm`` syntax.
+
+.. function:: gccjit::extended_asm&\
+	      gccjit::extended_asm::add_output_operand (const std::string &asm_symbolic_name,\
+                                                        const std::string &constraint,\
+                                                        gccjit::lvalue dest)
+
+   Add an output operand to the extended ``asm`` statement.  See the
+   `Output Operands <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#OutputOperands>`_
+   section of the documentation of the C syntax.
+
+   ``asm_symbolic_name`` corresponds to the ``asmSymbolicName`` component of
+   C's extended ``asm`` syntax, and specifies the symbolic name for the operand.
+   See the overload below for an alternative that does not supply a symbolic
+   name.
+
+   ``constraint`` corresponds to the ``constraint`` component of C's extended
+   ``asm`` syntax.
+
+   ``dest`` corresponds to the ``cvariablename`` component of C's extended
+   ``asm`` syntax.
+
+   .. code-block:: c++
+
+      // Example with a symbolic name ("aIndex"), the equivalent of:
+      //   : [aIndex] "=r" (index)
+      ext_asm.add_output_operand ("aIndex", "=r", index);
+
+   This function can't be called on an ``asm goto`` as such instructions can't
+   have outputs; see the
+   `Goto Labels <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#GotoLabels>`_
+   section of GCC's "Extended Asm" documentation.
+
+.. function:: gccjit::extended_asm&\
+              gccjit::extended_asm::add_output_operand (const std::string &constraint,\
+                                                        gccjit::lvalue dest)
+
+   As above, but don't supply a symbolic name for the operand.
+
+   .. code-block:: c++
+
+      // Example without a symbolic name, the equivalent of:
+      //   : "=r" (dst)
+      ext_asm.add_output_operand ("=r", dst);
+
+.. function:: gccjit::extended_asm&\
+              gccjit::extended_asm::add_input_operand (const std::string &asm_symbolic_name, \
+                                                       const std::string &constraint, \
+                                                       gccjit::rvalue src)
+
+   Add an input operand to the extended ``asm`` statement.  See the
+   `Input Operands <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#InputOperands>`_
+   section of the documentation of the C syntax.
+
+   ``asm_symbolic_name`` corresponds to the ``asmSymbolicName`` component
+   of C's extended ``asm`` syntax.  See the overload below for an alternative
+   that does not supply a symbolic name.
+
+   ``constraint`` corresponds to the ``constraint`` component of C's extended
+   ``asm`` syntax.
+
+   ``src`` corresponds to the ``cexpression`` component of C's extended
+   ``asm`` syntax.
+
+   .. code-block:: c++
+
+      // Example with a symbolic name ("aMask"), the equivalent of:
+      //   : [aMask] "r" (Mask)
+      ext_asm.add_input_operand ("aMask", "r", mask);
+
+.. function:: gccjit::extended_asm&\
+              gccjit::extended_asm::add_input_operand (const std::string &constraint,\
+                                                       gccjit::rvalue src)
+
+   As above, but don't supply a symbolic name for the operand.
+
+   .. code-block:: c++
+
+      // Example without a symbolic name, the equivalent of:
+      //   : "r" (src)
+      ext_asm.add_input_operand ("r", src);
+
+.. function:: gccjit::extended_asm&\
+              gccjit::extended_asm::add_clobber (const std::string &victim)
+
+   Add `victim` to the list of registers clobbered by the extended ``asm``
+   statement.  See the
+   `Clobbers and Scratch Registers <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers#>`_
+   section of the documentation of the C syntax.
+
+   Statements with multiple clobbers will require multiple calls, one per
+   clobber.
+
+   For example:
+
+   .. code-block:: c++
+
+     ext_asm.add_clobber ("r0").add_clobber ("cc").add_clobber ("memory");
+
+
+Adding top-level assembler statements
+*************************************
+
+In addition to creating extended ``asm`` instructions within a function,
+there is support for creating "top-level" assembler statements, outside
+of any function.
+
+.. function:: void\
+              gccjit::context::add_top_level_asm (const char *asm_stmts)
+
+   Create a set of top-level asm statements, analogous to those created
+   by GCC's "basic" ``asm`` syntax in C at file scope.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: // Quote from here in docs/cp/topics/asm.rst: example 5: C
+      :end-before: // Quote up to here in docs/cp/topics/asm.rst: example 5: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../../testsuite/jit.dg/test-asm.cc
+      :start-after: /* Quote from here in docs/cp/topics/asm.rst: example 5: jit.  */
+      :end-before: /* Quote up to here in docs/cp/topics/asm.rst: example 5: jit.  */
+      :language: c
diff --git a/gcc/jit/docs/cp/topics/index.rst b/gcc/jit/docs/cp/topics/index.rst
index 187a20d03b3..721e70cfa1b 100644
--- a/gcc/jit/docs/cp/topics/index.rst
+++ b/gcc/jit/docs/cp/topics/index.rst
@@ -28,3 +28,4 @@ Topic Reference
    functions.rst
    locations.rst
    compilation.rst
+   asm.rst
diff --git a/gcc/jit/docs/topics/asm.rst b/gcc/jit/docs/topics/asm.rst
new file mode 100644
index 00000000000..edcfa32ef48
--- /dev/null
+++ b/gcc/jit/docs/topics/asm.rst
@@ -0,0 +1,306 @@
+.. Copyright (C) 2020 Free Software Foundation, Inc.
+   Originally contributed by David Malcolm <dmalcolm@redhat.com>
+
+   This 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.
+
+   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
+   <http://www.gnu.org/licenses/>.
+
+.. default-domain:: c
+
+Using Assembly Language with libgccjit
+======================================
+
+.. TODO: which ABI version for all of these entrypoints
+
+libgccjit has some support for directly embedding assembler instructions.
+This is based on GCC's support for inline ``asm`` in C code, and the
+following assumes a familiarity with that functionality.  See
+`How to Use Inline Assembly Language in C Code <https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html>`_
+in GCC's documentation, the "Extended Asm" section in particular.
+
+
+Adding assembler instructions within a function
+***********************************************
+
+.. type:: gcc_jit_extended_asm
+
+   A `gcc_jit_extended_asm` represents an extended ``asm`` statement: a
+   series of low-level instructions inside a function that convert inputs
+   to outputs.
+
+   To avoid having an API entrypoint with a very large number of
+   parameters, an extended ``asm`` statement is made in stages:
+   an initial call to create the :type:`gcc_jit_extended_asm`,
+   followed by calls to add operands and set other properties of the
+   statement.
+
+   There are two API entrypoints for creating a :type:`gcc_jit_extended_asm`:
+
+   * :func:`gcc_jit_block_add_extended_asm` for an ``asm`` statement with
+     no control flow, and
+
+   * :func:`gcc_jit_block_end_with_extended_asm_goto` for an ``asm goto``.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: // Quote from here in docs/topics/asm.rst: example 1: C
+      :end-before: // Quote up to here in docs/topics/asm.rst: example 1: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: /* Quote from here in docs/topics/asm.rst: example 1: jit.  */
+      :end-before: /* Quote up to here in docs/topics/asm.rst: example 1: jit.  */
+      :language: c
+
+   .. warning::  When considering the numbering of operands within an
+		 extended ``asm`` statement (e.g. the ``%0`` and ``%1``
+		 above), the equivalent to the C syntax is followed i.e. all
+		 output operands, then all input operands, regardless of
+		 what order the calls to
+		 :func:`gcc_jit_extended_asm_add_output_operand` and
+		 :func:`gcc_jit_extended_asm_add_input_operand` were made in.
+
+   As in the C syntax, operands can be given symbolic names to avoid having
+   to number them.  For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: // Quote from here in docs/topics/asm.rst: example 2: C
+      :end-before: // Quote up to here in docs/topics/asm.rst: example 2: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: /* Quote from here in docs/topics/asm.rst: example 2: jit.  */
+      :end-before: /* Quote up to here in docs/topics/asm.rst: example 2: jit.  */
+      :language: c
+
+.. function:: gcc_jit_extended_asm *\
+              gcc_jit_block_add_extended_asm (gcc_jit_block *block,\
+                                              gcc_jit_location *loc,\
+                                              const char *asm_template)
+
+   Create a :type:`gcc_jit_extended_asm` for an extended ``asm`` statement
+   with no control flow (i.e. without the ``goto`` qualifier).
+
+   The parameter ``asm_template`` corresponds to the `AssemblerTemplate`
+   within C's extended ``asm`` syntax.  It must be non-NULL.  The call takes
+   a copy of the underlying string, so it is valid to pass in a pointer to
+   an on-stack buffer.
+
+.. function:: gcc_jit_extended_asm *\
+              gcc_jit_block_end_with_extended_asm_goto (gcc_jit_block *block,\
+                                                        gcc_jit_location *loc,\
+                                                        const char *asm_template,\
+                                                        int num_goto_blocks,\
+                                                        gcc_jit_block **goto_blocks,\
+                                                        gcc_jit_block *fallthrough_block)
+
+   Create a :type:`gcc_jit_extended_asm` for an extended ``asm`` statement
+   that may perform jumps, and use it to terminate the given block.
+   This is equivalent to the ``goto`` qualifier in C's extended ``asm``
+   syntax.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: // Quote from here in docs/topics/asm.rst: example 3b: C
+      :end-before: // Quote up to here in docs/topics/asm.rst: example 3b: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: /* Quote from here in docs/topics/asm.rst: example 3: jit.  */
+      :end-before: /* Quote up to here in docs/topics/asm.rst: example 3: jit.  */
+      :language: c
+
+   here referencing a :type:`gcc_jit_block` named "carry".
+
+   ``num_goto_blocks`` must be >= 0.
+
+   ``goto_blocks`` must be non-NULL.  This corresponds to the ``GotoLabels``
+   parameter within C's extended ``asm`` syntax.  The block names can be
+   referenced within the assembler template.
+
+   ``fallthrough_block`` can be NULL.  If non-NULL, it specifies the block
+   to fall through to after the statement.
+
+   .. note:: This is needed since each :type:`gcc_jit_block` must have a
+	     single exit point, as a basic block: you can't jump from the
+	     middle of a block.  A "goto" is implicitly added after the
+	     asm to handle the fallthrough case, which is equivalent to what
+	     would have happened in the C case.
+
+.. function:: void\
+              gcc_jit_extended_asm_set_volatile_flag (gcc_jit_extended_asm *ext_asm,\
+                                                      int flag)
+
+   Set whether the :type:`gcc_jit_extended_asm` has side-effects, equivalent to the
+   `volatile <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile>`_
+   qualifier in C's extended asm syntax.
+
+   For example, to create the equivalent of:
+
+   .. code-block:: c
+
+      asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (msr)
+                     :
+                     : "rdx");
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: /* Quote from here in docs/topics/asm.rst: example 4: jit.  */
+      :end-before: /* Quote up to here in docs/topics/asm.rst: example 4: jit.  */
+      :language: c
+
+   where the :type:`gcc_jit_extended_asm` is flagged as volatile.
+
+.. function:: void\
+              gcc_jit_extended_asm_set_inline_flag (gcc_jit_extended_asm *ext_asm,\
+                                                    int flag)
+
+   Set the equivalent of the
+   `inline <https://gcc.gnu.org/onlinedocs/gcc/Size-of-an-asm.html#Size-of-an-asm>`_
+   qualifier in C's extended ``asm`` syntax.
+
+.. function:: void\
+              gcc_jit_extended_asm_add_output_operand (gcc_jit_extended_asm *ext_asm,\
+                                                       const char *asm_symbolic_name,\
+                                                       const char *constraint,\
+                                                       gcc_jit_lvalue *dest)
+
+   Add an output operand to the extended ``asm`` statement.  See the
+   `Output Operands <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#OutputOperands>`_
+   section of the documentation of the C syntax.
+
+   ``asm_symbolic_name`` corresponds to the ``asmSymbolicName`` component of C's
+   extended ``asm`` syntax.  It can be NULL.  If non-NULL it specifies the
+   symbolic name for the operand.
+
+   ``constraint`` corresponds to the ``constraint`` component of C's extended
+   ``asm`` syntax.  It must be non-NULL.
+
+   ``dest`` corresponds to the ``cvariablename`` component of C's extended
+   ``asm`` syntax.  It must be non-NULL.
+
+   .. code-block:: c
+
+      // Example with a NULL symbolic name, the equivalent of:
+      //   : "=r" (dst)
+      gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=r", dst);
+
+      // Example with a symbolic name ("aIndex"), the equivalent of:
+      //   : [aIndex] "=r" (index)
+      gcc_jit_extended_asm_add_output_operand (ext_asm, "aIndex", "=r", index);
+
+   This function can't be called on an ``asm goto`` as such instructions can't
+   have outputs; see the
+   `Goto Labels <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#GotoLabels>`_
+   section of GCC's "Extended Asm" documentation.
+
+.. function:: void\
+              gcc_jit_extended_asm_add_input_operand (gcc_jit_extended_asm *ext_asm,\
+                                                      const char *asm_symbolic_name,\
+                                                      const char *constraint,\
+                                                      gcc_jit_rvalue *src)
+
+   Add an input operand to the extended ``asm`` statement.  See the
+   `Input Operands <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#InputOperands>`_
+   section of the documentation of the C syntax.
+
+   ``asm_symbolic_name`` corresponds to the ``asmSymbolicName`` component of C's
+   extended ``asm`` syntax.  It can be NULL.  If non-NULL it specifies the
+   symbolic name for the operand.
+
+   ``constraint`` corresponds to the ``constraint`` component of C's extended
+   ``asm`` syntax.  It must be non-NULL.
+
+   ``src`` corresponds to the ``cexpression`` component of C's extended
+   ``asm`` syntax.  It must be non-NULL.
+
+   .. code-block:: c
+
+      // Example with a NULL symbolic name, the equivalent of:
+      //   : "r" (src)
+      gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r",
+                                              gcc_jit_lvalue_as_rvalue (src));
+
+      // Example with a symbolic name ("aMask"), the equivalent of:
+      //   : [aMask] "r" (Mask)
+      gcc_jit_extended_asm_add_input_operand (ext_asm, "aMask", "r",
+                                              gcc_jit_lvalue_as_rvalue (mask));
+
+.. function:: void\
+              gcc_jit_extended_asm_add_clobber (gcc_jit_extended_asm *ext_asm,\
+                                                const char *victim)
+
+   Add `victim` to the list of registers clobbered by the extended ``asm``
+   statement.  It must be non-NULL.  See the
+   `Clobbers and Scratch Registers <https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers#>`_
+   section of the documentation of the C syntax.
+
+   Statements with multiple clobbers will require multiple calls, one per
+   clobber.
+
+   For example:
+
+   .. code-block:: c
+
+     gcc_jit_extended_asm_add_clobber (ext_asm, "r0");
+     gcc_jit_extended_asm_add_clobber (ext_asm, "cc");
+     gcc_jit_extended_asm_add_clobber (ext_asm, "memory");
+
+A :type:`gcc_jit_extended_asm` is a :type:`gcc_jit_object` "owned" by
+the block's context.  The following upcast is available:
+
+.. function:: gcc_jit_object *\
+              gcc_jit_extended_asm_as_object (gcc_jit_extended_asm *ext_asm)
+
+   Upcast from extended ``asm`` to object.
+
+
+Adding top-level assembler statements
+*************************************
+
+In addition to creating extended ``asm`` instructions within a function,
+there is support for creating "top-level" assembler statements, outside
+of any function.
+
+.. function:: void \
+              gcc_jit_context_add_top_level_asm (gcc_jit_context *ctxt,\
+                                                 const char *asm_stmts)
+
+   Create a set of top-level asm statements, analogous to those created
+   by GCC's "basic" ``asm`` syntax in C at file scope.
+
+   For example, to create the equivalent of:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: // Quote from here in docs/topics/asm.rst: example 5: C
+      :end-before: // Quote up to here in docs/topics/asm.rst: example 5: C
+      :language: c
+
+   the following API calls could be used:
+
+   .. literalinclude:: ../../../testsuite/jit.dg/test-asm.c
+      :start-after: /* Quote from here in docs/topics/asm.rst: example 5: jit.  */
+      :end-before: /* Quote up to here in docs/topics/asm.rst: example 5: jit.  */
+      :language: c
diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index bb3387fa583..ca49eb61fe3 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -219,3 +219,5 @@ entrypoints:
   * :func:`gcc_jit_version_minor`
 
   * :func:`gcc_jit_version_patchlevel`
+
+.. TODO: notes on new assembler-related entrypoints
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index eb40d64010e..c4412d94a3a 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -458,3 +458,5 @@ Statements
        :start-after: /* Quote from here in docs/topics/functions.rst.  */
        :end-before: /* Quote up to here in docs/topics/functions.rst.  */
        :language: c
+
+.. TODO: link to docs for inline assembler stmts
diff --git a/gcc/jit/docs/topics/index.rst b/gcc/jit/docs/topics/index.rst
index 8352ca25818..d7cb86aa0d8 100644
--- a/gcc/jit/docs/topics/index.rst
+++ b/gcc/jit/docs/topics/index.rst
@@ -31,3 +31,4 @@ Topic Reference
    compilation.rst
    compatibility.rst
    performance.rst
+   asm.rst
diff --git a/gcc/jit/docs/topics/objects.rst b/gcc/jit/docs/topics/objects.rst
index 12d3c9f7987..cdee2c0af6a 100644
--- a/gcc/jit/docs/topics/objects.rst
+++ b/gcc/jit/docs/topics/objects.rst
@@ -48,6 +48,7 @@ looks like this::
           +- gcc_jit_lvalue
              +- gcc_jit_param
       +- gcc_jit_case
+      +- gcc_jit_extended_asm
 
 There are casting methods for upcasting from subclasses to parent classes.
 For example, :c:func:`gcc_jit_type_as_object`:
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 4570bd2d717..b8c3685c073 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -131,7 +131,9 @@ namespace recording {
       class base_call;
       class function_pointer;
     class statement;
+      class extended_asm;
     class case_;
+  class top_level_asm;
 
   /* End of recording types. */
 }
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 0fddf04da87..ce57a0249f7 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "opt-suggestions.h"
 #include "gcc.h"
 #include "diagnostic.h"
+#include "stmt.h"
 
 #include <pthread.h>
 
@@ -86,6 +87,17 @@ namespace jit {
  Playback.
  **********************************************************************/
 
+/* FIXME.  */
+
+static tree
+build_string (const char *str)
+{
+  if (str)
+    return ::build_string (strlen (str), str);
+  else
+    return NULL_TREE;
+}
+
 /* The constructor for gcc::jit::playback::context.  */
 
 playback::context::context (recording::context *ctxt)
@@ -679,7 +691,7 @@ new_string_literal (const char *value)
   tree a_type = build_array_type (char_type_node, i_type);
   /* build_string len parameter must include NUL terminator when
      building C strings.  */
-  tree t_str = build_string (len + 1, value);
+  tree t_str = ::build_string (len + 1, value);
   TREE_TYPE (t_str) = a_type;
 
   /* Convert to (const char*), loosely based on
@@ -726,6 +738,16 @@ as_truth_value (tree expr, location *loc)
   return expr;
 }
 
+/* FIXME.  */
+
+void
+playback::context::add_top_level_asm (const char *asm_stmts)
+{
+  /* Compare with c_parser_asm_definition.  */
+  tree asm_str = build_string (asm_stmts);
+  symtab->finalize_toplevel_asm (asm_str);
+}
+
 /* Construct a playback::rvalue instance (wrapping a tree) for a
    unary op.  */
 
@@ -1802,6 +1824,102 @@ add_switch (location *loc,
   add_stmt (switch_stmt);
 }
 
+/* FIXME.  */
+/* Compare with c_parser_asm_operands.  */
+
+static tree
+build_operand_chain (const auto_vec <playback::asm_operand> *operands)
+{
+  tree result = NULL_TREE;
+  unsigned i;
+  playback::asm_operand *asm_op;
+  FOR_EACH_VEC_ELT (*operands, i, asm_op)
+    {
+      tree name = build_string (asm_op->m_asm_symbolic_name);
+      tree str = build_string (asm_op->m_constraint);
+      tree value = asm_op->m_expr;
+      result = chainon (result,
+			build_tree_list (build_tree_list (name, str),
+					 value));
+    }
+  return result;
+}
+
+/* FIXME.  */
+/* Compare with c_parser_asm_clobbers.  */
+
+static tree
+build_clobbers (const auto_vec <const char *> *clobbers)
+{
+  tree list = NULL_TREE;
+  unsigned i;
+  const char *clobber;
+  FOR_EACH_VEC_ELT (*clobbers, i, clobber)
+    {
+      tree str = build_string (clobber);
+      list = tree_cons (NULL_TREE, str, list);
+    }
+  return list;
+}
+
+/* FIXME.  */
+/* Compare with c_parser_asm_goto_operands.  */
+
+static tree
+build_goto_operands (const auto_vec <playback::block *> *blocks)
+{
+  tree list = NULL_TREE;
+  unsigned i;
+  playback::block *b;
+  FOR_EACH_VEC_ELT (*blocks, i, b)
+    {
+      tree label = b->as_label_decl ();
+      tree name = build_string (IDENTIFIER_POINTER (DECL_NAME (label)));
+      TREE_USED (label) = 1;
+      list = tree_cons (name, label, list);
+    }
+  return nreverse (list);
+}
+
+/* FIXME. */
+
+void
+playback::block::add_extended_asm (location *loc,
+				   const char *asm_template,
+				   bool is_volatile,
+				   bool is_inline,
+				   const auto_vec <asm_operand> *outputs,
+				   const auto_vec <asm_operand> *inputs,
+				   const auto_vec <const char *> *clobbers,
+				   const auto_vec <block *> *goto_blocks)
+{
+  /* See c/c-parser.c c_parser_asm_statement.  */
+  /* c/c-typeck.c has build_asm_expr.  */
+  tree t_string = build_string (asm_template);
+  tree t_outputs = build_operand_chain (outputs);
+  tree t_inputs = build_operand_chain (inputs);
+  tree t_clobbers = build_clobbers (clobbers);
+  tree t_labels = build_goto_operands (goto_blocks);
+  t_string
+    = resolve_asm_operand_names (t_string, t_outputs, t_inputs, t_labels);
+  tree asm_stmt
+    = build5 (ASM_EXPR, void_type_node,
+	      t_string, t_outputs, t_inputs, t_clobbers, t_labels);
+
+  /* asm statements without outputs, including simple ones, are treated
+     as volatile.  */
+#if 0
+  ASM_INPUT_P (asm_stmt) = simple;
+#endif
+  ASM_VOLATILE_P (asm_stmt) = (outputs->length () == 0);
+  ASM_INLINE_P (asm_stmt) = is_inline;
+  if (is_volatile)
+    ASM_VOLATILE_P (asm_stmt) = 1;
+  if (loc)
+    set_tree_location (asm_stmt, loc);
+  add_stmt (asm_stmt);
+}
+
 /* Constructor for gcc::jit::playback::block.  */
 
 playback::block::
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index f9b3e675368..82772590ed3 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -243,6 +243,8 @@ public:
 
   timer *get_timer () const { return m_recording_ctxt->get_timer (); }
 
+  void add_top_level_asm (const char *asm_stmts);
+
 private:
   void dump_generated_code ();
 
@@ -497,6 +499,21 @@ struct case_
   block *m_dest_block;
 };
 
+struct asm_operand
+{
+  asm_operand (const char *asm_symbolic_name,
+	       const char *constraint,
+	       tree expr)
+  : m_asm_symbolic_name (asm_symbolic_name),
+    m_constraint (constraint),
+    m_expr (expr)
+  {}
+
+  const char *m_asm_symbolic_name;
+  const char *m_constraint;
+  tree m_expr;
+};
+
 class block : public wrapper
 {
 public:
@@ -546,6 +563,16 @@ public:
 	      block *default_block,
 	      const auto_vec <case_> *cases);
 
+  void
+  add_extended_asm (location *loc,
+		    const char *asm_template,
+		    bool is_volatile,
+		    bool is_inline,
+		    const auto_vec <asm_operand> *outputs,
+		    const auto_vec <asm_operand> *inputs,
+		    const auto_vec <const char *> *clobbers,
+		    const auto_vec <block *> *goto_blocks);
+
 private:
   void
   set_tree_location (tree t, location *loc)
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index b73cd76a0a0..7655e976163 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -1904,6 +1904,17 @@ recording::context::get_all_requested_dumps (vec <recording::requested_dump> *ou
   out->splice (m_requested_dumps);
 }
 
+/* FIXME.  */
+
+void
+recording::context::add_top_level_asm (const char *asm_stmts)
+{
+  recording::top_level_asm *asm_obj
+    = new recording::top_level_asm (this, new_string (asm_stmts));
+  record (asm_obj);
+  m_top_level_asms.safe_push (asm_obj);
+}
+
 /* This is a pre-compilation check for the context (and any parents).
 
    Detect errors within the context, adding errors if any are found.  */
@@ -4125,6 +4136,19 @@ recording::block::add_comment (recording::location *loc,
   return result;
 }
 
+/* FIXME.  */
+
+recording::extended_asm *
+recording::block::add_extended_asm (location *loc,
+				    const char *asm_template)
+{
+  extended_asm *result
+    = new extended_asm_simple (this, loc, new_string (asm_template));
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  return result;
+}
+
 /* Create a recording::end_with_conditional instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
@@ -4207,6 +4231,25 @@ recording::block::end_with_switch (recording::location *loc,
   return result;
 }
 
+/* FIXME.  */
+
+recording::extended_asm *
+recording::block::end_with_extended_asm_goto (location *loc,
+					      const char *asm_template,
+					      int num_goto_blocks,
+					      block **goto_blocks,
+					      block *fallthrough_block)
+{
+  extended_asm *result
+    = new extended_asm_goto (this, loc, new_string (asm_template),
+			     num_goto_blocks, goto_blocks,
+			     fallthrough_block);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+  return result;
+}
+
 /* Override the default implementation of
    recording::memento::write_to_dump for blocks by writing
    an unindented block name as a label, followed by the indented
@@ -6358,6 +6401,445 @@ recording::switch_::write_reproducer (reproducer &r)
 	     cases_id);
 }
 
+/* class asm_operand : public memento.  */
+
+recording::asm_operand::asm_operand (extended_asm *ext_asm,
+				     string *asm_symbolic_name,
+				     string *constraint)
+: memento (ext_asm->get_context ()),
+  m_ext_asm (ext_asm),
+  m_asm_symbolic_name (asm_symbolic_name),
+  m_constraint (constraint)
+{
+}
+
+void
+recording::asm_operand::print (pretty_printer *pp) const
+{
+  if (m_asm_symbolic_name)
+    {
+      pp_character (pp, '[');
+      pp_string (pp, m_asm_symbolic_name->c_str ());
+      pp_character (pp, ']');
+      pp_space (pp);
+    }
+  pp_string (pp, m_constraint->get_debug_string ());
+  /* Subclass will add lvalue/rvalue.  */
+}
+
+/* class output_asm_operand : public asm_operand.  */
+
+recording::string *
+recording::output_asm_operand::make_debug_string ()
+{
+  gcc_unreachable (); // TODO
+}
+
+void
+recording::output_asm_operand::write_reproducer (reproducer &r)
+{
+  const char *fmt =
+    "  gcc_jit_extended_asm_add_output_operand (%s, /* gcc_jit_extended_asm *ext_asm */\n"
+    "                                           %s, /* const char *asm_symbolic_name */\n"
+    "                                           %s, /* const char *constraint */\n"
+    "                                           %s); /* gcc_jit_lvalue *dest */\n";
+  r.write (fmt,
+	   r.get_identifier (m_ext_asm),
+	   (m_asm_symbolic_name
+	    ? m_asm_symbolic_name->get_debug_string () : "NULL"),
+	   m_constraint->get_debug_string (),
+	   r.get_identifier (m_dest));
+}
+
+void
+recording::output_asm_operand::print (pretty_printer *pp) const
+{
+  asm_operand::print (pp);
+  pp_string (pp, " (");
+  pp_string (pp, m_dest->get_debug_string ());
+  pp_string (pp, ")");
+}
+
+/* class input_asm_operand : public asm_operand.  */
+
+recording::string *
+recording::input_asm_operand::make_debug_string ()
+{
+  gcc_unreachable (); // TODO
+}
+
+void
+recording::input_asm_operand::write_reproducer (reproducer &r)
+{
+  const char *fmt =
+    "  gcc_jit_extended_asm_add_input_operand (%s, /* gcc_jit_extended_asm *ext_asm */\n"
+    "                                          %s, /* const char *asm_symbolic_name */\n"
+    "                                          %s, /* const char *constraint */\n"
+    "                                          %s); /* gcc_jit_rvalue *src */\n";
+  r.write (fmt,
+	   r.get_identifier (m_ext_asm),
+	   (m_asm_symbolic_name
+	    ? m_asm_symbolic_name->get_debug_string () : "NULL"),
+	   m_constraint->get_debug_string (),
+	   r.get_identifier_as_rvalue (m_src));
+}
+
+void
+recording::input_asm_operand::print (pretty_printer *pp) const
+{
+  asm_operand::print (pp);
+  pp_string (pp, " (");
+  pp_string (pp, m_src->get_debug_string ());
+  pp_string (pp, ")");
+}
+
+/* The implementation of class gcc::jit::recording::extended_asm.  */
+
+/* FIXME.  */
+
+void
+recording::extended_asm::add_output_operand (const char *asm_symbolic_name,
+					     const char *constraint,
+					     lvalue *dest)
+{
+  output_asm_operand *op
+    = new output_asm_operand (this,
+			      new_string (asm_symbolic_name),
+			      new_string (constraint),
+			      dest);
+  m_ctxt->record (op);
+  m_output_ops.safe_push (op);
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm::add_input_operand (const char *asm_symbolic_name,
+					    const char *constraint,
+					    rvalue *src)
+{
+  input_asm_operand *op
+    = new input_asm_operand (this,
+			     new_string (asm_symbolic_name),
+			     new_string (constraint),
+			     src);
+  m_ctxt->record (op);
+  m_input_ops.safe_push (op);
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm::add_clobber (const char *victim)
+{
+  m_clobbers.safe_push (new_string (victim));
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::extended_asm.  */
+
+void
+recording::extended_asm::replay_into (replayer *r)
+{
+  auto_vec<playback::asm_operand> playback_output_ops;
+  auto_vec<playback::asm_operand> playback_input_ops;
+  auto_vec<const char *> playback_clobbers;
+  auto_vec<playback::block *> playback_goto_blocks;
+
+  /* Populate outputs.  */
+  {
+    output_asm_operand *rec_asm_op;
+    unsigned i;
+    FOR_EACH_VEC_ELT (m_output_ops, i, rec_asm_op)
+      {
+	playback::asm_operand playback_asm_op
+	  (rec_asm_op->get_symbolic_name (),
+	   rec_asm_op->get_constraint (),
+	   rec_asm_op->get_lvalue ()->playback_lvalue ()->as_tree ());
+	playback_output_ops.safe_push (playback_asm_op);
+      }
+  }
+
+  /* Populate inputs.  */
+  {
+    input_asm_operand *rec_asm_op;
+    unsigned i;
+    FOR_EACH_VEC_ELT (m_input_ops, i, rec_asm_op)
+      {
+	playback::asm_operand playback_asm_op
+	  (rec_asm_op->get_symbolic_name (),
+	   rec_asm_op->get_constraint (),
+	   rec_asm_op->get_rvalue ()->playback_rvalue ()->as_tree ());
+	playback_input_ops.safe_push (playback_asm_op);
+      }
+  }
+
+  /* Populate clobbers.  */
+  {
+    string *rec_clobber;
+    unsigned i;
+    FOR_EACH_VEC_ELT (m_clobbers, i, rec_clobber)
+      playback_clobbers.safe_push (rec_clobber->c_str ());
+  }
+
+  /* Populate playback blocks if an "asm goto".  */
+  maybe_populate_playback_blocks (&playback_goto_blocks);
+
+  playback_block (get_block ())
+    ->add_extended_asm (playback_location (r),
+			m_asm_template->c_str (),
+			m_is_volatile, m_is_inline,
+			&playback_output_ops,
+			&playback_input_ops,
+			&playback_clobbers,
+			&playback_goto_blocks);
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   an extended_asm "statement".  */
+
+recording::string *
+recording::extended_asm::make_debug_string ()
+{
+  pretty_printer pp;
+  pp_string (&pp, "asm ");
+  if (m_is_volatile)
+    pp_string (&pp, "volatile ");
+  if (m_is_inline)
+    pp_string (&pp, "inline ");
+  if (is_goto ())
+    pp_string (&pp, "goto ");
+  pp_character (&pp, '(');
+  pp_character (&pp, '"');
+  // TODO: escape this:
+  pp_string (&pp, m_asm_template->c_str ());
+  pp_string (&pp, "\" : ");
+  unsigned i;
+  {
+    output_asm_operand *asm_op;
+    FOR_EACH_VEC_ELT (m_output_ops, i, asm_op)
+      {
+	if (i > 0)
+	  pp_string (&pp, ", ");
+	asm_op->print (&pp);
+      }
+  }
+  pp_string (&pp, " : ");
+  {
+    input_asm_operand *asm_op;
+    FOR_EACH_VEC_ELT (m_input_ops, i, asm_op)
+      {
+	if (i > 0)
+	  pp_string (&pp, ", ");
+	asm_op->print (&pp);
+      }
+  }
+  pp_string (&pp, " : ");
+  string *rec_clobber;
+  FOR_EACH_VEC_ELT (m_clobbers, i, rec_clobber)
+      {
+	if (i > 0)
+	  pp_string (&pp, ", ");
+	pp_string (&pp, rec_clobber->get_debug_string ());
+      }
+  maybe_print_gotos (&pp);
+  pp_character (&pp, ')');
+  return new_string (pp_formatted_text (&pp));
+}
+
+void
+recording::extended_asm::write_flags (reproducer &r)
+{
+  if (m_is_volatile)
+    r.write ("  gcc_jit_extended_asm_set_volatile_flag (%s, 1);\n",
+	     r.get_identifier (this));
+  if (m_is_inline)
+    r.write ("  gcc_jit_extended_asm_set_inline_flag (%s, 1);\n",
+	     r.get_identifier (this));
+}
+
+void
+recording::extended_asm::write_clobbers (reproducer &r)
+{
+  string *clobber;
+  unsigned i;
+  FOR_EACH_VEC_ELT (m_clobbers, i, clobber)
+    r.write ("  gcc_jit_extended_asm_add_clobber (%s, %s);\n",
+	     r.get_identifier (this),
+	     clobber->get_debug_string ()); // FIXME: escape?
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   extended_asm_simple.  */
+
+void
+recording::extended_asm_simple::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "extended_asm");
+  r.write ("  gcc_jit_extended_asm *%s =\n"
+	   "    gcc_jit_block_add_extended_asm (%s, /*gcc_jit_block *block */\n"
+	   "                                    %s, /* gcc_jit_location *loc */\n"
+	   "                                    %s); /* const char *asm_template */\n",
+	   id,
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   // FIXME: need to escape this
+	   "\"FIXME\""); //m_asm_template->get_debug_string ());
+  write_flags (r);
+  write_clobbers (r);
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm::
+maybe_populate_playback_blocks (auto_vec <playback::block *> *)
+{
+  /* Do nothing; not an "asm goto".  */
+}
+
+/* The implementation of class gcc::jit::recording::extended_asm_goto.  */
+
+/* FIXME.  */
+
+recording::extended_asm_goto::extended_asm_goto (block *b,
+						 location *loc,
+						 string *asm_template,
+						 int num_goto_blocks,
+						 block **goto_blocks,
+						 block *fallthrough_block)
+: extended_asm (b, loc, asm_template),
+  m_goto_blocks (num_goto_blocks),
+  m_fallthrough_block (fallthrough_block)
+{
+  for (int i = 0; i < num_goto_blocks; i++)
+    m_goto_blocks.quick_push (goto_blocks[i]);
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm_goto::replay_into (replayer *r)
+{
+  recording::extended_asm::replay_into (r);
+  if (m_fallthrough_block)
+    playback_block (get_block ())
+      ->add_jump (playback_location (r),
+		  m_fallthrough_block->playback_block ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   extended_asm_goto.  */
+
+void
+recording::extended_asm_goto::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "extended_asm");
+  const char *blocks_id = r.make_tmp_identifier ("blocks_for", this);
+  r.write ("  gcc_jit_block *%s[%i] = {\n",
+	   blocks_id,
+	   m_goto_blocks.length ());
+  int i;
+  block *b;
+  FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
+    r.write ("    %s,\n", r.get_identifier (b));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_extended_asm *%s =\n"
+	   "    gcc_jit_block_end_with_extended_asm_goto (%s, /*gcc_jit_block *block */\n"
+	   "                                              %s, /* gcc_jit_location *loc */\n"
+	   "                                              %s, /* const char *asm_template */\n"
+	   "                                              %i, /* int num_goto_blocks */\n"
+	   "                                              %s, /* gcc_jit_block **goto_blocks */\n"
+	   "                                              %s); /* gcc_jit_block *fallthrough_block */\n",
+	   id,
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   // FIXME: need to escape this
+	   "\"FIXME\"", //m_asm_template->get_debug_string ()
+	   m_goto_blocks.length (),
+	   blocks_id,
+	   (m_fallthrough_block
+	    ? r.get_identifier (m_fallthrough_block)
+	    : "NULL"));
+  write_flags (r);
+  write_clobbers (r);
+}
+
+/* FIXME.  */
+
+vec <recording::block *>
+recording::extended_asm_goto::get_successor_blocks () const
+{
+  vec <block *> result;
+  result.create (m_goto_blocks.length () + 1);
+  if (m_fallthrough_block)
+    result.quick_push (m_fallthrough_block);
+  result.splice (m_goto_blocks);
+  return result;
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm_goto::maybe_print_gotos (pretty_printer *pp) const
+{
+  pp_string (pp, " : ");
+  unsigned i;
+  block *b;
+  FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      pp_string (pp, b->get_debug_string ());
+    }
+  /* Non-C syntax here.  */
+  if (m_fallthrough_block)
+    pp_printf (pp, " [fallthrough: %s]",
+	       m_fallthrough_block->get_debug_string ());
+}
+
+/* FIXME.  */
+
+void
+recording::extended_asm_goto::
+maybe_populate_playback_blocks (auto_vec <playback::block *> *out)
+{
+  unsigned i;
+  block *b;
+  FOR_EACH_VEC_ELT (m_goto_blocks, i, b)
+    out->safe_push (b->playback_block ());
+}
+
+/* class top_level_asm : public memento.  */
+
+recording::top_level_asm::top_level_asm (context *ctxt,
+					 string *asm_stmts)
+: memento (ctxt),
+  m_asm_stmts (asm_stmts)
+{
+}
+
+void
+recording::top_level_asm::replay_into (replayer *r)
+{
+  r->add_top_level_asm (m_asm_stmts->c_str ());
+}
+
+recording::string *
+recording::top_level_asm::make_debug_string ()
+{
+  gcc_unreachable (); // TODO
+}
+
+void
+recording::top_level_asm::write_reproducer (reproducer &r)
+{
+  r.write ("  gcc_jit_context_add_top_level_asm (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                     %s); /* const char *asm_stmts */\n",
+	   r.get_identifier (get_context ()),
+	   "\"FIXME\"");  // TODO: need to escape this
+}
+
 } // namespace gcc::jit
 
 } // namespace gcc
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 726b9c4b837..1e22b66c01b 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -301,6 +301,8 @@ public:
   void set_timer (timer *t) { m_timer = t; }
   timer *get_timer () const { return m_timer; }
 
+  void add_top_level_asm (const char *asm_stmts);
+
 private:
   void log_all_options () const;
   void log_str_option (enum gcc_jit_str_option opt) const;
@@ -344,6 +346,7 @@ private:
   auto_vec<compound_type *> m_compound_types;
   auto_vec<global *> m_globals;
   auto_vec<function *> m_functions;
+  auto_vec<top_level_asm *> m_top_level_asms;
 
   type *m_basic_types[NUM_GCC_JIT_TYPES];
   type *m_FILE_type;
@@ -1258,6 +1261,10 @@ public:
   add_comment (location *loc,
 	       const char *text);
 
+  extended_asm *
+  add_extended_asm (location *loc,
+		    const char *asm_template);
+
   statement *
   end_with_conditional (location *loc,
 			rvalue *boolval,
@@ -1279,6 +1286,13 @@ public:
 		   int num_cases,
 		   case_ **cases);
 
+  extended_asm *
+  end_with_extended_asm_goto (location *loc,
+			      const char *asm_template,
+			      int num_goto_blocks,
+			      block **goto_blocks,
+			      block *fallthrough_block);
+
   playback::block *
   playback_block () const
   {
@@ -2071,6 +2085,200 @@ private:
   auto_vec <case_ *> m_cases;
 };
 
+class asm_operand : public memento
+{
+public:
+  asm_operand (extended_asm *ext_asm,
+	       string *asm_symbolic_name,
+	       string *constraint);
+
+  const char *get_symbolic_name () const
+  {
+    if (m_asm_symbolic_name)
+      return m_asm_symbolic_name->c_str ();
+    else
+      return NULL;
+  }
+
+  const char *get_constraint () const
+  {
+    return m_constraint->c_str ();
+  }
+
+  virtual void print (pretty_printer *pp) const;
+
+protected:
+  extended_asm *m_ext_asm;
+  string *m_asm_symbolic_name;
+  string *m_constraint;
+};
+
+class output_asm_operand : public asm_operand
+{
+public:
+  output_asm_operand (extended_asm *ext_asm,
+		      string *asm_symbolic_name,
+		      string *constraint,
+		      lvalue *dest)
+  : asm_operand (ext_asm, asm_symbolic_name, constraint),
+    m_dest (dest)
+  {}
+
+  lvalue *get_lvalue () const { return m_dest; }
+
+  void replay_into (replayer *) FINAL OVERRIDE {}
+
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  string * make_debug_string () FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  lvalue *m_dest;
+};
+
+class input_asm_operand : public asm_operand
+{
+public:
+  input_asm_operand (extended_asm *ext_asm,
+		     string *asm_symbolic_name,
+		     string *constraint,
+		     rvalue *src)
+  : asm_operand (ext_asm, asm_symbolic_name, constraint),
+    m_src (src)
+  {}
+
+  rvalue *get_rvalue () const { return m_src; }
+
+  void replay_into (replayer *) FINAL OVERRIDE {}
+
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  string * make_debug_string () FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  rvalue *m_src;
+};
+
+class extended_asm : public statement
+{
+public:
+  extended_asm (block *b,
+		location *loc,
+		string *asm_template)
+  : statement (b, loc),
+    m_asm_template (asm_template),
+    m_is_volatile (false),
+    m_is_inline (false)
+  {}
+
+  void set_volatile_flag (bool flag) { m_is_volatile = flag; }
+  void set_inline_flag (bool flag) { m_is_inline = flag; }
+
+  void add_output_operand (const char *asm_symbolic_name,
+			   const char *constraint,
+			   lvalue *dest);
+  void add_input_operand (const char *asm_symbolic_name,
+			  const char *constraint,
+			  rvalue *src);
+  void add_clobber (const char *victim);
+
+  void replay_into (replayer *r) OVERRIDE;
+
+  string *get_asm_template () const { return m_asm_template; }
+
+  virtual bool is_goto () const = 0;
+  virtual void maybe_print_gotos (pretty_printer *) const = 0;
+
+protected:
+  void write_flags (reproducer &r);
+  void write_clobbers (reproducer &r);
+
+private:
+  string * make_debug_string () FINAL OVERRIDE;
+  virtual void maybe_populate_playback_blocks
+    (auto_vec <playback::block *> *out) = 0;
+
+protected:
+  string *m_asm_template;
+  bool m_is_volatile;
+  bool m_is_inline;
+  auto_vec<output_asm_operand *> m_output_ops;
+  auto_vec<input_asm_operand *> m_input_ops;
+  auto_vec<string *> m_clobbers;
+};
+
+/* An extended_asm that's not a goto, as created by
+   gcc_jit_block_add_extended_asm. */
+
+class extended_asm_simple : public extended_asm
+{
+public:
+  extended_asm_simple (block *b,
+		       location *loc,
+		       string *asm_template)
+  : extended_asm (b, loc, asm_template)
+  {}
+
+  void write_reproducer (reproducer &r) OVERRIDE;
+  bool is_goto () const FINAL OVERRIDE { return false; }
+  void maybe_print_gotos (pretty_printer *) const FINAL OVERRIDE {}
+
+private:
+  void maybe_populate_playback_blocks
+    (auto_vec <playback::block *> *) FINAL OVERRIDE
+  {}
+};
+
+/* An extended_asm that's a asm goto, as created by
+   gcc_jit_block_end_with_extended_asm_goto.  */
+
+class extended_asm_goto : public extended_asm
+{
+public:
+  extended_asm_goto (block *b,
+		     location *loc,
+		     string *asm_template,
+		     int num_goto_blocks,
+		     block **goto_blocks,
+		     block *fallthrough_block);
+
+  void replay_into (replayer *r) FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) OVERRIDE;
+
+  vec <block *> get_successor_blocks () const FINAL OVERRIDE;
+
+  bool is_goto () const FINAL OVERRIDE { return true; }
+  void maybe_print_gotos (pretty_printer *) const FINAL OVERRIDE;
+
+private:
+  void maybe_populate_playback_blocks
+    (auto_vec <playback::block *> *out) FINAL OVERRIDE;
+
+private:
+  auto_vec <block *> m_goto_blocks;
+  block *m_fallthrough_block;
+};
+
+/* FIXME.  */
+
+class top_level_asm : public memento
+{
+public:
+  top_level_asm (context *ctxt, string *asm_stmts);
+
+private:
+  void replay_into (replayer *r) FINAL OVERRIDE;
+  string * make_debug_string () FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  string *m_asm_stmts;
+};
+
 } // namespace gcc::jit::recording
 
 /* Create a recording::memento_of_new_rvalue_from_const instance and add
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 69e67766640..31256628cf4 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -46,6 +46,7 @@ namespace gccjit
      class lvalue;
        class param;
     class case_;
+    class extended_asm;
   class timer;
   class auto_time;
 
@@ -316,6 +317,8 @@ namespace gccjit
 		    rvalue max_value,
 		    block dest_block);
 
+    void add_top_level_asm (const char *asm_stmts);
+
   private:
     gcc_jit_context *m_inner_ctxt;
   };
@@ -449,6 +452,13 @@ namespace gccjit
 			  block default_block,
 			  std::vector <case_> cases,
 			  location loc = location ());
+
+    extended_asm add_extended_asm (const std::string &asm_template,
+				   location loc = location ());
+    extended_asm end_with_extended_asm_goto (const std::string &asm_template,
+					     std::vector<block> goto_blocks,
+					     block *fallthrough_block,
+					     location loc = location ());
   };
 
   class rvalue : public object
@@ -508,6 +518,40 @@ namespace gccjit
     gcc_jit_case *get_inner_case () const;
   };
 
+  class extended_asm : public object
+  {
+  public:
+    extended_asm ();
+    extended_asm (gcc_jit_extended_asm *inner);
+
+    extended_asm &
+    set_volatile_flag (bool flag);
+
+    extended_asm &
+    set_inline_flag (bool flag);
+
+    extended_asm&
+    add_output_operand (const std::string &asm_symbolic_name,
+			const std::string &constraint,
+			gccjit::lvalue dest);
+    extended_asm&
+    add_output_operand (const std::string &constraint,
+			gccjit::lvalue dest);
+
+    extended_asm&
+    add_input_operand (const std::string &asm_symbolic_name,
+		       const std::string &constraint,
+		       gccjit::rvalue src);
+    extended_asm&
+    add_input_operand (const std::string &constraint,
+		       gccjit::rvalue src);
+
+    extended_asm&
+    add_clobber (const std::string &victim);
+
+    gcc_jit_extended_asm *get_inner_extended_asm () const;
+  };
+
   /* Overloaded operators, for those who want the most terse API
      (at the possible risk of being a little too magical).
 
@@ -1258,6 +1302,12 @@ context::new_case (rvalue min_value,
 					  dest_block.get_inner_block ()));
 }
 
+inline void
+context::add_top_level_asm (const char *asm_stmts)
+{
+  gcc_jit_context_add_top_level_asm (m_inner_ctxt, asm_stmts);
+}
+
 // class object
 inline context
 object::get_context () const
@@ -1553,6 +1603,37 @@ block::end_with_switch (rvalue expr,
 				 as_array_of_ptrs);
 }
 
+inline extended_asm
+block::add_extended_asm (const std::string &asm_template,
+			 location loc)
+{
+  return gcc_jit_block_add_extended_asm (get_inner_block (),
+					 loc.get_inner_location (),
+					 asm_template.c_str ());
+}
+
+inline extended_asm
+block::end_with_extended_asm_goto (const std::string &asm_template,
+				   std::vector<block> goto_blocks,
+				   block *fallthrough_block,
+				   location loc)
+{
+  /* Treat std::vector as an array, relying on it not being resized: */
+  block *as_array_of_wrappers = &goto_blocks[0];
+
+  /* Treat the array as being of the underlying pointers, relying on
+     the wrapper type being such a pointer internally.  */
+  gcc_jit_block **as_array_of_ptrs =
+    reinterpret_cast<gcc_jit_block **> (as_array_of_wrappers);
+  return gcc_jit_block_end_with_extended_asm_goto
+    (get_inner_block (),
+     loc.get_inner_location (),
+     asm_template.c_str (),
+     goto_blocks.size (),
+     as_array_of_ptrs,
+     fallthrough_block ? fallthrough_block->get_inner_block () : NULL);
+}
+
 inline rvalue
 block::add_call (function other,
 		 location loc)
@@ -1757,6 +1838,92 @@ case_::get_inner_case () const
   return reinterpret_cast<gcc_jit_case *> (get_inner_object ());
 }
 
+// class extended_asm : public object
+inline extended_asm::extended_asm () : object () {}
+inline extended_asm::extended_asm (gcc_jit_extended_asm *inner)
+  : object (gcc_jit_extended_asm_as_object (inner))
+{
+}
+
+inline extended_asm&
+extended_asm::set_volatile_flag (bool flag)
+{
+  gcc_jit_extended_asm_set_volatile_flag (get_inner_extended_asm (), flag);
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::set_inline_flag (bool flag)
+{
+  gcc_jit_extended_asm_set_inline_flag (get_inner_extended_asm (), flag);
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::add_output_operand (const std::string &asm_symbolic_name,
+				  const std::string &constraint,
+				  gccjit::lvalue dest)
+{
+  gcc_jit_extended_asm_add_output_operand
+    (get_inner_extended_asm (),
+     asm_symbolic_name.c_str (),
+     constraint.c_str (),
+     dest.get_inner_lvalue ());
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::add_output_operand (const std::string &constraint,
+				  gccjit::lvalue dest)
+{
+  gcc_jit_extended_asm_add_output_operand
+    (get_inner_extended_asm (),
+     NULL, /* asm_symbolic_name */
+     constraint.c_str (),
+     dest.get_inner_lvalue ());
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::add_input_operand (const std::string &asm_symbolic_name,
+				 const std::string &constraint,
+				 gccjit::rvalue src)
+{
+  gcc_jit_extended_asm_add_input_operand
+    (get_inner_extended_asm (),
+     asm_symbolic_name.c_str (),
+     constraint.c_str (),
+     src.get_inner_rvalue ());
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::add_input_operand (const std::string &constraint,
+				 gccjit::rvalue src)
+{
+  gcc_jit_extended_asm_add_input_operand
+    (get_inner_extended_asm (),
+     NULL, /* asm_symbolic_name */
+     constraint.c_str (),
+     src.get_inner_rvalue ());
+  return *this;
+}
+
+inline extended_asm&
+extended_asm::add_clobber (const std::string &victim)
+{
+  gcc_jit_extended_asm_add_clobber (get_inner_extended_asm (),
+				    victim.c_str ());
+  return *this;
+}
+
+inline gcc_jit_extended_asm *
+extended_asm::get_inner_extended_asm () const
+{
+  /* Manual downcast: */
+  return reinterpret_cast<gcc_jit_extended_asm *> (get_inner_object ());
+}
+
 /* Overloaded operators.  */
 // Unary operators
 inline rvalue operator- (rvalue a)
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 3d04f6db3af..37bd1e5e2b3 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -96,6 +96,11 @@ struct gcc_jit_timer : public timer
 {
 };
 
+struct gcc_jit_extended_asm : public gcc::jit::recording::extended_asm
+{
+};
+
+
 /**********************************************************************
  Error-handling.
 
@@ -300,13 +305,13 @@ struct gcc_jit_timer : public timer
 
 static void
 jit_error (gcc::jit::recording::context *ctxt,
-	   gcc_jit_location *loc,
+	   gcc::jit::recording::location *loc,
 	   const char *fmt, ...)
   GNU_PRINTF(3, 4);
 
 static void
 jit_error (gcc::jit::recording::context *ctxt,
-	   gcc_jit_location *loc,
+	   gcc::jit::recording::location *loc,
 	   const char *fmt, ...)
 {
   va_list ap;
@@ -3247,3 +3252,156 @@ gcc_jit_version_patchlevel (void)
   version_info vi;
   return vi.patchlevel;
 }
+
+/**********************************************************************
+ Asm support.
+ **********************************************************************/
+
+/* FIXME.  */
+
+gcc_jit_extended_asm *
+gcc_jit_block_add_extended_asm (gcc_jit_block *block,
+				gcc_jit_location *loc,
+				const char *asm_template)
+{
+  RETURN_NULL_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_NULL_IF_FAIL (asm_template, ctxt, loc, "NULL asm_template");
+
+  return (gcc_jit_extended_asm *)block->add_extended_asm (loc, asm_template);
+}
+
+/* FIXME.  */
+
+gcc_jit_extended_asm *
+gcc_jit_block_end_with_extended_asm_goto (gcc_jit_block *block,
+					  gcc_jit_location *loc,
+					  const char *asm_template,
+					  int num_goto_blocks,
+					  gcc_jit_block **goto_blocks,
+					  gcc_jit_block *fallthrough_block)
+{
+  RETURN_NULL_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_NULL_IF_FAIL (asm_template, ctxt, loc, "NULL asm_template");
+  RETURN_NULL_IF_FAIL (num_goto_blocks >= 0, ctxt, loc, "num_goto_blocks < 0");
+  for (int i = 0; i < num_goto_blocks; i++)
+    RETURN_NULL_IF_FAIL_PRINTF1 (goto_blocks[i],
+				 ctxt, loc,
+				 "NULL goto_blocks[%i]", i);
+  /* fallthrough_block can be NULL.  */
+  return (gcc_jit_extended_asm *)block->end_with_extended_asm_goto
+    (loc, asm_template,
+     num_goto_blocks, (gcc::jit::recording::block **)goto_blocks,
+     fallthrough_block);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, this calls the trivial
+   gcc::jit::recording::memento::as_object method (an extended_asm is a
+   memento), in jit-recording.h.  */
+
+gcc_jit_object *
+gcc_jit_extended_asm_as_object (gcc_jit_extended_asm *ext_asm)
+{
+  RETURN_NULL_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+
+  return static_cast <gcc_jit_object *> (ext_asm->as_object ());
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+void
+gcc_jit_extended_asm_set_volatile_flag (gcc_jit_extended_asm *ext_asm,
+					int flag)
+{
+  RETURN_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+  ext_asm->set_volatile_flag (flag);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+void
+gcc_jit_extended_asm_set_inline_flag (gcc_jit_extended_asm *ext_asm,
+				      int flag)
+{
+  RETURN_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+  ext_asm->set_inline_flag (flag);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+void
+gcc_jit_extended_asm_add_output_operand (gcc_jit_extended_asm *ext_asm,
+					 const char *asm_symbolic_name,
+					 const char *constraint,
+					 gcc_jit_lvalue *dest)
+{
+  RETURN_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+  gcc::jit::recording::context *ctxt = ext_asm->get_context ();
+  gcc::jit::recording::location *loc = ext_asm->get_loc ();
+  /* asm_symbolic_name can be NULL.  */
+  RETURN_IF_FAIL (constraint, ctxt, loc, "NULL constraint");
+  RETURN_IF_FAIL (dest, ctxt, loc, "NULL dest");
+  RETURN_IF_FAIL (!ext_asm->is_goto (), ctxt, loc,
+		  "cannot add output operand to asm goto");
+  ext_asm->add_output_operand (asm_symbolic_name, constraint, dest);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+extern void
+gcc_jit_extended_asm_add_input_operand (gcc_jit_extended_asm *ext_asm,
+					const char *asm_symbolic_name,
+					const char *constraint,
+					gcc_jit_rvalue *src)
+{
+  RETURN_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+  gcc::jit::recording::context *ctxt = ext_asm->get_context ();
+  gcc::jit::recording::location *loc = ext_asm->get_loc ();
+  /* asm_symbolic_name can be NULL.  */
+  RETURN_IF_FAIL (constraint, ctxt, loc, "NULL constraint");
+  RETURN_IF_FAIL (src, ctxt, loc, "NULL src");
+  ext_asm->add_input_operand (asm_symbolic_name, constraint, src);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+void
+gcc_jit_extended_asm_add_clobber (gcc_jit_extended_asm *ext_asm,
+				  const char *victim)
+{
+  RETURN_IF_FAIL (ext_asm, NULL, NULL, "NULL ext_asm");
+  gcc::jit::recording::context *ctxt = ext_asm->get_context ();
+  gcc::jit::recording::location *loc = ext_asm->get_loc ();
+  RETURN_IF_FAIL (victim, ctxt, loc, "NULL victim");
+  ext_asm->add_clobber (victim);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   FIXME.  */
+
+void
+gcc_jit_context_add_top_level_asm (gcc_jit_context *ctxt,
+				   const char *asm_stmts)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
+  RETURN_IF_FAIL (asm_stmts, ctxt, NULL, "NULL asm_stmts");
+  ctxt->add_top_level_asm (asm_stmts);
+}
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 1c5a12e9c01..f118ed31368 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -68,6 +68,7 @@ typedef struct gcc_jit_result gcc_jit_result;
 	     +- gcc_jit_lvalue
 		 +- gcc_jit_param
 	 +- gcc_jit_case
+	 +- gcc_jit_extended_asm
 */
 typedef struct gcc_jit_object gcc_jit_object;
 
@@ -138,6 +139,9 @@ typedef struct gcc_jit_param gcc_jit_param;
    destination block.  */
 typedef struct gcc_jit_case gcc_jit_case;
 
+/* FIXME.  */
+typedef struct gcc_jit_extended_asm gcc_jit_extended_asm;
+
 /* Acquire a JIT-compilation context.  */
 extern gcc_jit_context *
 gcc_jit_context_acquire (void);
@@ -1503,6 +1507,98 @@ gcc_jit_version_minor (void);
 extern int
 gcc_jit_version_patchlevel (void);
 
+/**********************************************************************
+ Asm support.
+ **********************************************************************/
+
+/* Functions for adding inline assembler code, analogous to GCC's
+   "extended asm" syntax.
+
+   See https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html
+
+   FIXME: note about API entrypoints and version.
+   FIXME.  */
+
+#define LIBGCCJIT_HAVE_gcc_jit_block_add_extended_asm
+
+/* Create a gcc_jit_extended_asm for an extended asm statement
+   with no control flow (i.e. without the goto qualifier).
+
+   The asm_template parameter  corresponds to the AssemblerTemplate
+   within C's extended asm syntax.  It must be non-NULL.  */
+
+extern gcc_jit_extended_asm *
+gcc_jit_block_add_extended_asm (gcc_jit_block *block,
+				gcc_jit_location *loc,
+				const char *asm_template);
+
+/* Create a gcc_jit_extended_asm for an extended asm statement
+   that may perform jumps, and use it to terminate the given block.
+   This is equivalent to the "goto" qualifier in C's extended asm
+   syntax.  */
+
+extern gcc_jit_extended_asm *
+gcc_jit_block_end_with_extended_asm_goto (gcc_jit_block *block,
+					  gcc_jit_location *loc,
+					  const char *asm_template,
+					  int num_goto_blocks,
+					  gcc_jit_block **goto_blocks,
+					  gcc_jit_block *fallthrough_block);
+
+/* Upcasting from extended asm to object.  */
+
+extern gcc_jit_object *
+gcc_jit_extended_asm_as_object (gcc_jit_extended_asm *ext_asm);
+
+/* Set whether the gcc_jit_extended_asm has side-effects, equivalent to
+   the "volatile" qualifier in C's extended asm syntax.  */
+
+extern void
+gcc_jit_extended_asm_set_volatile_flag (gcc_jit_extended_asm *ext_asm,
+					int flag);
+
+/* Set the equivalent of the "inline" qualifier in C's extended asm
+   syntax.  */
+
+extern void
+gcc_jit_extended_asm_set_inline_flag (gcc_jit_extended_asm *ext_asm,
+				      int flag);
+
+/* Add an output operand to the extended asm statement.
+   "asm_symbolic_name" can be NULL.
+   "constraint" and "dest" must be non-NULL.
+   This function can't be called on an "asm goto" as such instructions
+   can't have outputs  */
+
+extern void
+gcc_jit_extended_asm_add_output_operand (gcc_jit_extended_asm *ext_asm,
+					 const char *asm_symbolic_name,
+					 const char *constraint,
+					 gcc_jit_lvalue *dest);
+
+/* Add an input operand to the extended asm statement.
+   "asm_symbolic_name" can be NULL.
+   "constraint" and "src" must be non-NULL.  */
+
+extern void
+gcc_jit_extended_asm_add_input_operand (gcc_jit_extended_asm *ext_asm,
+					const char *asm_symbolic_name,
+					const char *constraint,
+					gcc_jit_rvalue *src);
+
+/* Add "victim" to the list of registers clobbered by the extended
+   asm statement.  It must be non-NULL.  */
+
+extern void
+gcc_jit_extended_asm_add_clobber (gcc_jit_extended_asm *ext_asm,
+				  const char *victim);
+
+/* FIXME.  */
+
+extern void
+gcc_jit_context_add_top_level_asm (gcc_jit_context *ctxt,
+				   const char *asm_stmts);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 6137dd4b4b0..f59bb7c12c5 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -186,4 +186,17 @@ LIBGCCJIT_ABI_13 {
     gcc_jit_version_major;
     gcc_jit_version_minor;
     gcc_jit_version_patchlevel;
-} LIBGCCJIT_ABI_12;
\ No newline at end of file
+} LIBGCCJIT_ABI_12;
+
+LIBGCCJIT_ABI_FIXME {
+  global:
+    gcc_jit_block_add_extended_asm;
+    gcc_jit_block_end_with_extended_asm_goto;
+    gcc_jit_extended_asm_as_object;
+    gcc_jit_extended_asm_set_volatile_flag;
+    gcc_jit_extended_asm_set_inline_flag;
+    gcc_jit_extended_asm_add_output_operand;
+    gcc_jit_extended_asm_add_input_operand;
+    gcc_jit_extended_asm_add_clobber;
+    gcc_jit_context_add_top_level_asm;
+} LIBGCCJIT_ABI_13;
diff --git a/gcc/testsuite/jit.dg/test-asm.c b/gcc/testsuite/jit.dg/test-asm.c
new file mode 100644
index 00000000000..28a0f64b5a3
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-asm.c
@@ -0,0 +1,528 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+/* DONE:
+   - example with outputs and inputs
+   - example with asm_symbolic_name
+   - example with clobber
+   - example with goto labels
+   - example with using a goto label by name, rather than by number
+   - example with volatile
+   - example of gcc_jit_extended_asm_as_object (and getting the debug obj)
+
+   TODO:
+   - make the tests only run on x86
+   - example with inline
+   - error-checking.  */
+
+/**********************************************************************
+ Support fns for creating code.
+ **********************************************************************/
+
+/* FIXME.  */
+
+static gcc_jit_block *
+make_single_block_func (gcc_jit_context *ctxt, const char *func_name)
+{
+  gcc_jit_type *void_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_function *func
+    = gcc_jit_context_new_function (ctxt, NULL,
+				    GCC_JIT_FUNCTION_EXPORTED,
+				    void_type,
+				    func_name,
+				    0, NULL, 0);
+  return gcc_jit_function_new_block (func, "initial");
+}
+
+static const char *
+get_desc (gcc_jit_extended_asm *ext_asm)
+{
+  return gcc_jit_object_get_debug_string
+    (gcc_jit_extended_asm_as_object (ext_asm));
+}
+
+/**********************************************************************
+ Support fns for verifying code.
+ **********************************************************************/
+
+typedef void (*void_void_fn) (void);
+
+static void_void_fn
+get_test_fn (gcc_jit_result *result, const char *func_name)
+{
+  return (void_void_fn)gcc_jit_result_get_code (result, func_name);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_1: simple example of asm
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+     int src;
+     int dst;
+
+     void test_i386_basic_asm_1 (void)
+     {
+       // Quote from here in docs/topics/asm.rst: example 1: C
+       asm ("mov %1, %0\n\t"
+            "add $1, %0"
+            : "=r" (dst)
+            : "r" (src));
+       // Quote up to here in docs/topics/asm.rst: example 1: C
+     }
+
+     i.e. copy src to dst and add 1 to dst.  */
+
+static void
+create_test_i386_basic_asm_1 (gcc_jit_context *ctxt)
+{
+  gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_lvalue *dst
+    = gcc_jit_context_new_global (ctxt, NULL,
+				  GCC_JIT_GLOBAL_EXPORTED,
+				  int_type, "dst");
+  gcc_jit_lvalue *src
+    = gcc_jit_context_new_global (ctxt, NULL,
+				  GCC_JIT_GLOBAL_EXPORTED,
+				  int_type, "src");
+
+  gcc_jit_block *block
+    = make_single_block_func (ctxt, "test_i386_basic_asm_1");
+
+  /* Quote from here in docs/topics/asm.rst: example 1: jit.  */
+  gcc_jit_extended_asm *ext_asm
+    = gcc_jit_block_add_extended_asm (block, NULL,
+				      "mov %1, %0\n\t"
+				      "add $1, %0");
+  gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=r", dst);
+  gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r",
+					  gcc_jit_lvalue_as_rvalue (src));
+  /* Quote up to here in docs/topics/asm.rst: example 1: jit.  */
+
+  const char *desc = get_desc (ext_asm);
+  CHECK_STRING_VALUE
+    (desc,
+     "asm (\"mov %1, %0\n\tadd $1, %0\" : \"=r\" (dst) : \"r\" (src) : )");
+
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+static void
+verify_code_1 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  void_void_fn test_i386_basic_asm_1
+    = get_test_fn (result, "test_i386_basic_asm_1");
+  CHECK_NON_NULL (test_i386_basic_asm_1);
+
+  int *dst_ptr = (int *)gcc_jit_result_get_global (result, "dst");
+  CHECK_NON_NULL (dst_ptr);
+  int *src_ptr = (int *)gcc_jit_result_get_global (result, "src");
+  CHECK_NON_NULL (src_ptr);
+
+  *src_ptr = 42;
+  *dst_ptr = 0;
+  test_i386_basic_asm_1 ();
+  CHECK_VALUE (*src_ptr, 42);
+  CHECK_VALUE (*dst_ptr, 43);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_2: test of symbolic names and clobbers
+ **********************************************************************/
+
+/* Create the equivalent of:
+     uint32_t test_i386_basic_asm_2 (uint32_t Mask)
+     {
+       uint32_t Index;
+       // Quote from here in docs/topics/asm.rst: example 2: C
+       asm ("bsfl %[aMask], %[aIndex]"
+            : [aIndex] "=r" (Index)
+            : [aMask] "r" (Mask)
+            : "cc");
+       // Quote up to here in docs/topics/asm.rst: example 2: C
+       return Index;
+     }
+   i.e. return the first bit set in "Mask"
+
+   This exercises symbolic names and clobbers.  */
+
+static void
+create_test_i386_basic_asm_2 (gcc_jit_context *ctxt)
+{
+  gcc_jit_type *uint32_type = gcc_jit_context_get_int_type (ctxt, 4, 0);
+  gcc_jit_param *mask
+    = gcc_jit_context_new_param (ctxt, NULL,
+				 uint32_type, "Mask");
+  gcc_jit_function *func
+    = gcc_jit_context_new_function (ctxt, NULL,
+				    GCC_JIT_FUNCTION_EXPORTED,
+				    uint32_type,
+				    "test_i386_basic_asm_2",
+				    1, &mask, 0);
+  gcc_jit_lvalue *index
+    = gcc_jit_function_new_local (func, NULL,
+				  uint32_type, "Index");
+  gcc_jit_block *block = gcc_jit_function_new_block (func, "initial");
+
+  /* Quote from here in docs/topics/asm.rst: example 2: jit.  */
+  gcc_jit_extended_asm *ext_asm
+    = gcc_jit_block_add_extended_asm (block, NULL,
+				      "bsfl %[aMask], %[aIndex]");
+  gcc_jit_extended_asm_add_output_operand (ext_asm, "aIndex", "=r", index);
+  gcc_jit_extended_asm_add_input_operand (ext_asm, "aMask", "r",
+					  gcc_jit_param_as_rvalue (mask));
+  gcc_jit_extended_asm_add_clobber (ext_asm, "cc");
+  /* Quote up to here in docs/topics/asm.rst: example 2: jit.  */
+
+  const char *desc = get_desc (ext_asm);
+  CHECK_STRING_VALUE
+    (desc,
+     "asm (\"bsfl %[aMask], %[aIndex]\""
+     " : [aIndex] \"=r\" (Index) : [aMask] \"r\" (Mask) : \"cc\")");
+
+  gcc_jit_block_end_with_return (block, NULL,
+				 gcc_jit_lvalue_as_rvalue (index));
+}
+
+static void
+verify_code_2 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef uint32_t (*fntype) (uint32_t);
+  fntype test_i386_basic_asm_2
+    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_2");
+  CHECK_NON_NULL (test_i386_basic_asm_2);
+
+  CHECK_VALUE (test_i386_basic_asm_2 (1), 0);
+  CHECK_VALUE (test_i386_basic_asm_2 (2), 1);
+  CHECK_VALUE (test_i386_basic_asm_2 (4), 2);
+  CHECK_VALUE (test_i386_basic_asm_2 (8), 3);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_3a/b: test of control flow: "asm goto"
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+     int test_i386_basic_asm_3a (int p1, int p2)
+     {
+       asm goto ("btl %1, %0\n\t"
+		 "jc %l2"
+		 : // No outputs
+		 : "r" (p1), "r" (p2)
+		 : "cc"
+		 : carry);
+
+       return 0;
+
+      carry:
+       return 1;
+     }
+
+    or (the "_3b" variant) using a name rather than a number for the goto
+    label:
+
+       // Quote from here in docs/topics/asm.rst: example 3b: C
+       asm goto ("btl %1, %0\n\t"
+                 "jc %l[carry]"
+                 : // No outputs
+                 : "r" (p1), "r" (p2)
+                 : "cc"
+                 : carry);
+       // Quote up to here in docs/topics/asm.rst: example 3b: C
+
+    This exercises control flow with an asm.  */
+
+static void
+create_test_i386_basic_asm_3 (gcc_jit_context *ctxt,
+			      const char *funcname,
+			      int use_name)
+{
+  gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_param *p1 = gcc_jit_context_new_param (ctxt, NULL, int_type, "p1");
+  gcc_jit_param *p2 = gcc_jit_context_new_param (ctxt, NULL, int_type, "p2");
+  gcc_jit_param *params[2] = {p1, p2};
+  gcc_jit_function *func
+    = gcc_jit_context_new_function (ctxt, NULL,
+				    GCC_JIT_FUNCTION_EXPORTED,
+				    int_type,
+				    funcname,
+				    2, params, 0);
+  gcc_jit_block *b_start = gcc_jit_function_new_block (func, "start");
+  gcc_jit_block *b_fallthru = gcc_jit_function_new_block (func, "fallthru");
+  gcc_jit_block *b_carry = gcc_jit_function_new_block (func, "carry");
+
+  gcc_jit_rvalue *zero
+    = gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0);
+  gcc_jit_rvalue *one
+    = gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1);
+
+  /* Quote from here in docs/topics/asm.rst: example 3: jit.  */
+  const char *asm_template =
+    (use_name
+     ? /* Label referred to by name: "%l[carry]".  */
+       ("btl %1, %0\n\t"
+        "jc %l[carry]")
+     : /* Label referred to numerically: "%l2".  */
+       ("btl %1, %0\n\t"
+        "jc %l2"));
+
+  gcc_jit_extended_asm *ext_asm
+    = gcc_jit_block_end_with_extended_asm_goto (b_start, NULL,
+						asm_template,
+						1, &b_carry,
+						b_fallthru);
+  gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r",
+					  gcc_jit_param_as_rvalue (p1));
+  gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r",
+					  gcc_jit_param_as_rvalue (p2));
+  gcc_jit_extended_asm_add_clobber (ext_asm, "cc");
+  /* Quote up to here in docs/topics/asm.rst: example 3: jit.  */
+
+  const char *desc = get_desc (ext_asm);
+  CHECK_STRING_VALUE
+    (desc,
+     (use_name
+      ? ("asm goto (\"btl %1, %0\n\tjc %l[carry]\" "
+	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
+	 ": carry [fallthrough: fallthru])")
+      : ("asm goto (\"btl %1, %0\n\tjc %l2\" "
+	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
+	 ": carry [fallthrough: fallthru])")));
+
+  gcc_jit_block_end_with_return (b_fallthru, NULL, zero);
+  gcc_jit_block_end_with_return (b_carry, NULL, one);
+}
+
+static void
+verify_code_3 (gcc_jit_context *ctxt, gcc_jit_result *result,
+	       const char *funcname)
+{
+  typedef int (*test_i386_basic_asm_3_type) (int, int);
+
+  test_i386_basic_asm_3_type test_i386_basic_asm_3
+    = (test_i386_basic_asm_3_type) gcc_jit_result_get_code (result, funcname);
+  CHECK_NON_NULL (test_i386_basic_asm_3);
+
+  /* The fn should test bits, returning 0 or 1.  */
+  /* Bit 0.  */
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 0), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 0), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 0), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 0), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 0), 0);
+  /* Bit 1.  */
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 1), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 1), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 1), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 1), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 1), 0);
+
+  for (int i = 0; i < 15; i++)
+    {
+      CHECK_VALUE (test_i386_basic_asm_3 (0x0000, i), 0);
+      CHECK_VALUE (test_i386_basic_asm_3 (0xffff, i), 1);
+    }
+}
+
+/**********************************************************************
+ test_i386_basic_asm_4: test of "volatile"
+ **********************************************************************/
+
+/* Create the equivalent of:
+     uint64_t test_i386_basic_asm_4 (void)
+     {
+       uint64_t start_time, end_time;
+
+       // Get start time
+       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (start_time)
+                     :
+                     : "rdx");
+
+       // could do other work here
+
+       // Get end time
+       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (start_time)
+                     :
+                     : "rdx");
+
+       // Get elapsed time
+       return end_time - start_time;
+     }
+
+   This exercises "volatile"; without it, the optimizer can assume that
+   both asm generate the same value and thus the time difference is zero.  */
+
+static void
+add_rdtsc (gcc_jit_block *block, gcc_jit_lvalue *msr)
+{
+  /* Quote from here in docs/topics/asm.rst: example 4: jit.  */
+  gcc_jit_extended_asm *ext_asm
+    = gcc_jit_block_add_extended_asm
+	(block, NULL,
+	 "rdtsc\n\t"  /* Returns the time in EDX:EAX.  */
+	 "shl $32, %%rdx\n\t"  /* Shift the upper bits left.  */
+	 "or %%rdx, %0");  /* 'Or' in the lower bits.  */
+  gcc_jit_extended_asm_set_volatile_flag (ext_asm, 1);
+  gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=a", msr);
+  gcc_jit_extended_asm_add_clobber (ext_asm, "rdx");
+  /* Quote up to here in docs/topics/asm.rst: example 4: jit.  */
+
+  const char *desc = get_desc (ext_asm);
+  CHECK_STRING_STARTS_WITH (desc, "asm volatile (");
+}
+
+static void
+create_test_i386_basic_asm_4 (gcc_jit_context *ctxt)
+{
+  gcc_jit_type *uint64_type = gcc_jit_context_get_int_type (ctxt, 8, 0);
+  gcc_jit_function *func
+    = gcc_jit_context_new_function (ctxt, NULL,
+				    GCC_JIT_FUNCTION_EXPORTED,
+				    uint64_type,
+				    "test_i386_basic_asm_4",
+				    0, NULL, 0);
+  gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+  gcc_jit_lvalue *start_time
+    = gcc_jit_function_new_local (func, NULL, uint64_type, "start_time");
+  add_rdtsc (block, start_time);
+
+  gcc_jit_block_add_comment (block, NULL, "other work here");
+
+  gcc_jit_lvalue *end_time
+    = gcc_jit_function_new_local (func, NULL, uint64_type, "end_time");
+  add_rdtsc (block, end_time);
+
+  gcc_jit_rvalue *elapsed
+    = gcc_jit_context_new_binary_op (ctxt, NULL, GCC_JIT_BINARY_OP_MINUS,
+				     uint64_type,
+				     gcc_jit_lvalue_as_rvalue (end_time),
+				     gcc_jit_lvalue_as_rvalue (start_time));
+  gcc_jit_block_end_with_return (block, NULL, elapsed);
+}
+
+static void
+verify_code_4 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef uint64_t (*fntype) (void);
+  fntype test_i386_basic_asm_4
+    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_4");
+
+  CHECK_NON_NULL (test_i386_basic_asm_4);
+
+  test_i386_basic_asm_4 ();
+  // TODO: verify behavior
+}
+
+/**********************************************************************
+ test_i386_basic_asm_5: test of top-level asm
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+   // Quote from here in docs/topics/asm.rst: example 5: C
+     asm ("\t.pushsection .text\n"
+          "\t.globl add_asm\n"
+          "\t.type add_asm, @function\n"
+          "add_asm:\n"
+          "\tmovq %rdi, %rax\n"
+          "\tadd %rsi, %rax\n"
+          "\tret\n"
+          "\t.popsection\n");
+   // Quote up to here in docs/topics/asm.rst: example 5: C
+
+   to add a simple function ("add_asm") directly in assembly language.  */
+
+static void
+create_test_i386_basic_asm_5 (gcc_jit_context *ctxt)
+{
+  /* Quote from here in docs/topics/asm.rst: example 5: jit.  */
+  gcc_jit_context_add_top_level_asm (ctxt,
+                                     "\t.pushsection .text\n"
+                                     "\t.globl add_asm\n"
+                                     "\t.type add_asm, @function\n"
+                                     "add_asm:\n"
+                                     "\tmovq %rdi, %rax\n"
+                                     "\tadd %rsi, %rax\n"
+                                     "\tret\n"
+                                     "\t# some asm here\n"
+                                     "\t.popsection\n");
+  /* Quote up to here in docs/topics/asm.rst: example 5: jit.  */
+}
+
+static void
+verify_code_5 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*test_i386_basic_asm_5_type) (int, int);
+  test_i386_basic_asm_5_type test_i386_basic_asm_5
+    = (test_i386_basic_asm_5_type) gcc_jit_result_get_code (result, "add_asm");
+  CHECK_NON_NULL (test_i386_basic_asm_5);
+
+  CHECK_VALUE (test_i386_basic_asm_5 (2, 2), 4);
+  CHECK_VALUE (test_i386_basic_asm_5 (20, 7), 27);
+}
+
+/**********************************************************************
+ Code for harness
+ **********************************************************************/
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  // TODO:
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
+    1);
+
+  /* TODO: make the tests only run on x86
+     How to check target config?  Can we get at a triple?
+     DejaGnu has "global target_triplet" which is set up via args to
+     runtest.exp.
+     Elements of a triple?  */
+
+  create_test_i386_basic_asm_1 (ctxt);
+  create_test_i386_basic_asm_2 (ctxt);
+  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3a", 0);
+  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3b", 1);
+  create_test_i386_basic_asm_4 (ctxt);
+  create_test_i386_basic_asm_5 (ctxt);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_NON_NULL (result);
+  verify_code_1 (ctxt, result);
+  verify_code_2 (ctxt, result);
+  verify_code_3 (ctxt, result, "test_i386_basic_asm_3a");
+  verify_code_3 (ctxt, result, "test_i386_basic_asm_3b");
+  verify_code_4 (ctxt, result);
+  verify_code_5 (ctxt, result);
+}
diff --git a/gcc/testsuite/jit.dg/test-asm.cc b/gcc/testsuite/jit.dg/test-asm.cc
new file mode 100644
index 00000000000..1932cb01c09
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-asm.cc
@@ -0,0 +1,475 @@
+#include "libgccjit++.h"
+
+#include "harness.h"
+
+/**********************************************************************
+ Support fns for creating code.
+ **********************************************************************/
+
+/* FIXME.  */
+
+static gccjit::block
+make_single_block_func (gccjit::context ctxt, const char *func_name)
+{
+  gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID);
+  std::vector<gccjit::param> params;
+  gccjit::function func
+    = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+			 void_type,
+			 func_name, params, 0);
+  return func.new_block ("initial");
+}
+
+/**********************************************************************
+ Support fns for verifying code.
+ **********************************************************************/
+
+typedef void (*void_void_fn) (void);
+
+static void_void_fn
+get_test_fn (gcc_jit_result *result, const char *func_name)
+{
+  return (void_void_fn)gcc_jit_result_get_code (result, func_name);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_1: simple example of asm
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+     int src;
+     int dst;
+
+     void test_i386_basic_asm_1 (void)
+     {
+       // Quote from here in docs/cp/topics/asm.rst: example 1: C
+       asm ("mov %1, %0\n\t"
+            "add $1, %0"
+            : "=r" (dst)
+            : "r" (src));
+       // Quote up to here in docs/cp/topics/asm.rst: example 1: C
+     }
+
+     i.e. copy src to dst and add 1 to dst.  */
+
+static void
+create_test_i386_basic_asm_1 (gcc_jit_context *c_ctxt)
+{
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
+  gccjit::lvalue dst
+    = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "dst");
+  gccjit::lvalue src
+    = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "src");
+
+  gccjit::block block
+    = make_single_block_func (ctxt, "test_i386_basic_asm_1");
+
+  gccjit::extended_asm ext_asm =
+  /* Quote from here in docs/cp/topics/asm.rst: example 1: jit.  */
+    block.add_extended_asm ("mov %1, %0\n\t"
+                            "add $1, %0")
+    .add_output_operand ("=r", dst)
+    .add_input_operand ("r", src);
+  /* Quote up to here in docs/cp/topics/asm.rst: example 1: jit.  */
+
+  std::string desc = ext_asm.get_debug_string ();
+  CHECK_STRING_VALUE
+    (desc.c_str (),
+     "asm (\"mov %1, %0\n\tadd $1, %0\" : \"=r\" (dst) : \"r\" (src) : )");
+
+  block.end_with_return ();
+}
+
+static void
+verify_code_1 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  void_void_fn test_i386_basic_asm_1
+    = get_test_fn (result, "test_i386_basic_asm_1");
+  CHECK_NON_NULL (test_i386_basic_asm_1);
+
+  int *dst_ptr = (int *)gcc_jit_result_get_global (result, "dst");
+  CHECK_NON_NULL (dst_ptr);
+  int *src_ptr = (int *)gcc_jit_result_get_global (result, "src");
+  CHECK_NON_NULL (src_ptr);
+
+  *src_ptr = 42;
+  *dst_ptr = 0;
+  test_i386_basic_asm_1 ();
+  CHECK_VALUE (*src_ptr, 42);
+  CHECK_VALUE (*dst_ptr, 43);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_2: test of symbolic names and clobbers
+ **********************************************************************/
+
+/* Create the equivalent of:
+     uint32_t test_i386_basic_asm_2 (uint32_t Mask)
+     {
+       uint32_t Index;
+       // Quote from here in docs/cp/topics/asm.rst: example 2: C
+       asm ("bsfl %[aMask], %[aIndex]"
+            : [aIndex] "=r" (Index)
+            : [aMask] "r" (Mask)
+            : "cc");
+       // Quote up to here in docs/cp/topics/asm.rst: example 2: C
+       return Index;
+     }
+   i.e. return the first bit set in "Mask"
+
+   This exercises symbolic names and clobbers.  */
+
+static void
+create_test_i386_basic_asm_2 (gcc_jit_context *c_ctxt)
+{
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type uint32_type = ctxt.get_int_type (4, 0);
+  gccjit::param mask = ctxt.new_param (uint32_type, "Mask");
+  std::vector<gccjit::param> params {mask};
+  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+					     uint32_type,
+					     "test_i386_basic_asm_2",
+					     params, 0);
+  gccjit::lvalue index = func.new_local (uint32_type, "Index");
+  gccjit::block block = func.new_block ("initial");
+  gccjit::extended_asm ext_asm =
+  /* Quote from here in docs/cp/topics/asm.rst: example 2: jit.  */
+    block.add_extended_asm ("bsfl %[aMask], %[aIndex]")
+    .add_output_operand ("aIndex", "=r", index)
+    .add_input_operand ("aMask", "r", mask)
+    .add_clobber ("cc");
+  /* Quote up to here in docs/cp/topics/asm.rst: example 2: jit.  */
+
+  std::string desc = ext_asm.get_debug_string ();
+  CHECK_STRING_VALUE
+    (desc.c_str (),
+     "asm (\"bsfl %[aMask], %[aIndex]\""
+     " : [aIndex] \"=r\" (Index) : [aMask] \"r\" (Mask) : \"cc\")");
+
+  block.end_with_return (index);
+}
+
+static void
+verify_code_2 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef uint32_t (*fntype) (uint32_t);
+  fntype test_i386_basic_asm_2
+    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_2");
+  CHECK_NON_NULL (test_i386_basic_asm_2);
+
+  CHECK_VALUE (test_i386_basic_asm_2 (1), 0);
+  CHECK_VALUE (test_i386_basic_asm_2 (2), 1);
+  CHECK_VALUE (test_i386_basic_asm_2 (4), 2);
+  CHECK_VALUE (test_i386_basic_asm_2 (8), 3);
+}
+
+/**********************************************************************
+ test_i386_basic_asm_3a/b: test of control flow: "asm goto"
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+     int test_i386_basic_asm_3a (int p1, int p2)
+     {
+       asm goto ("btl %1, %0\n\t"
+		 "jc %l2"
+		 : // No outputs
+		 : "r" (p1), "r" (p2)
+		 : "cc"
+		 : carry);
+
+       return 0;
+
+      carry:
+       return 1;
+     }
+
+    or (the "_3b" variant) using a name rather than a number for the goto
+    label:
+
+       // Quote from here in docs/cp/topics/asm.rst: example 3b: C
+       asm goto ("btl %1, %0\n\t"
+                 "jc %l[carry]"
+                 : // No outputs
+                 : "r" (p1), "r" (p2)
+                 : "cc"
+                 : carry);
+       // Quote up to here in docs/cp/topics/asm.rst: example 3b: C
+
+    This exercises control flow with an asm.  */
+
+static void
+create_test_i386_basic_asm_3 (gcc_jit_context *c_ctxt,
+			      const char *funcname,
+			      int use_name)
+{
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
+  gccjit::param p1 = ctxt.new_param (int_type, "p1");
+  gccjit::param p2 = ctxt.new_param (int_type, "p2");
+  std::vector<gccjit::param> params ({p1, p2});
+  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+					     int_type,
+					     funcname,
+					     params, 0);
+  gccjit::block b_start = func.new_block ("start");
+  gccjit::block b_fallthru = func.new_block ("fallthru");
+  gccjit::block b_carry = func.new_block ("carry");
+
+  gccjit::rvalue zero = ctxt.new_rvalue (int_type, 0);
+  gccjit::rvalue one = ctxt.new_rvalue (int_type, 1);
+
+  /* Quote from here in docs/cp/topics/asm.rst: example 3: jit.  */
+  const char *asm_template =
+    (use_name
+     ? /* Label referred to by name: "%l[carry]".  */
+       ("btl %1, %0\n\t"
+        "jc %l[carry]")
+     : /* Label referred to numerically: "%l2".  */
+       ("btl %1, %0\n\t"
+        "jc %l2"));
+
+  std::vector<gccjit::block> goto_blocks ({b_carry});
+  gccjit::extended_asm ext_asm
+    = (b_start.end_with_extended_asm_goto (asm_template,
+					  goto_blocks,
+					  &b_fallthru)
+       .add_input_operand ("r", p1)
+       .add_input_operand ("r", p2)
+       .add_clobber ("cc"));
+  /* Quote up to here in docs/cp/topics/asm.rst: example 3: jit.  */
+
+  std::string desc = ext_asm.get_debug_string ();
+  CHECK_STRING_VALUE
+    (desc.c_str (),
+     (use_name
+      ? ("asm goto (\"btl %1, %0\n\tjc %l[carry]\" "
+	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
+	 ": carry [fallthrough: fallthru])")
+      : ("asm goto (\"btl %1, %0\n\tjc %l2\" "
+	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
+	 ": carry [fallthrough: fallthru])")));
+
+  b_fallthru.end_with_return (zero);
+  b_carry.end_with_return (one);
+}
+
+static void
+verify_code_3 (gcc_jit_context *ctxt, gcc_jit_result *result,
+	       const char *funcname)
+{
+  typedef int (*test_i386_basic_asm_3_type) (int, int);
+
+  test_i386_basic_asm_3_type test_i386_basic_asm_3
+    = (test_i386_basic_asm_3_type) gcc_jit_result_get_code (result, funcname);
+  CHECK_NON_NULL (test_i386_basic_asm_3);
+
+  /* The fn should test bits, returning 0 or 1.  */
+  /* Bit 0.  */
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 0), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 0), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 0), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 0), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 0), 0);
+  /* Bit 1.  */
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 1), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 1), 0);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 1), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 1), 1);
+  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 1), 0);
+
+  for (int i = 0; i < 15; i++)
+    {
+      CHECK_VALUE (test_i386_basic_asm_3 (0x0000, i), 0);
+      CHECK_VALUE (test_i386_basic_asm_3 (0xffff, i), 1);
+    }
+}
+
+/**********************************************************************
+ test_i386_basic_asm_4: test of "volatile"
+ **********************************************************************/
+
+/* Create the equivalent of:
+     uint64_t test_i386_basic_asm_4 (void)
+     {
+       uint64_t start_time, end_time;
+
+       // Get start time
+       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (start_time)
+                     :
+                     : "rdx");
+
+       // could do other work here
+
+       // Get end time
+       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
+                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
+                     "or %%rdx, %0"        // 'Or' in the lower bits.
+                     : "=a" (start_time)
+                     :
+                     : "rdx");
+
+       // Get elapsed time
+       return end_time - start_time;
+     }
+
+   This exercises "volatile"; without it, the optimizer can assume that
+   both asm generate the same value and thus the time difference is zero.  */
+
+static void
+add_rdtsc (gccjit::block block, gccjit::lvalue msr)
+{
+  /* Quote from here in docs/cp/topics/asm.rst: example 4: jit.  */
+  gccjit::extended_asm ext_asm
+    = block.add_extended_asm
+	("rdtsc\n\t"  /* Returns the time in EDX:EAX.  */
+	 "shl $32, %%rdx\n\t"  /* Shift the upper bits left.  */
+	 "or %%rdx, %0")  /* 'Or' in the lower bits.  */
+    .set_volatile_flag (true)
+    .add_output_operand ("=a", msr)
+    .add_clobber ("rdx");
+  /* Quote up to here in docs/cp/topics/asm.rst: example 4: jit.  */
+
+  std::string desc = ext_asm.get_debug_string ();
+  CHECK_STRING_STARTS_WITH (desc.c_str (), "asm volatile (");
+}
+
+static void
+create_test_i386_basic_asm_4 (gcc_jit_context *c_ctxt)
+{
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type uint64_type = ctxt.get_int_type (8, 0);
+  std::vector<gccjit::param> params;
+  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+					      uint64_type,
+					      "test_i386_basic_asm_4",
+					     params, 0);
+  gccjit::block block = func.new_block ();
+
+  gccjit::lvalue start_time = func.new_local (uint64_type, "start_time");
+  add_rdtsc (block, start_time);
+
+  block.add_comment ("other work here");
+
+  gccjit::lvalue end_time = func.new_local (uint64_type, "end_time");
+  add_rdtsc (block, end_time);
+
+  block.end_with_return (end_time - start_time);
+}
+
+static void
+verify_code_4 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef uint64_t (*fntype) (void);
+  fntype test_i386_basic_asm_4
+    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_4");
+
+  CHECK_NON_NULL (test_i386_basic_asm_4);
+
+  test_i386_basic_asm_4 ();
+  // TODO: verify behavior
+}
+
+/**********************************************************************
+ test_i386_basic_asm_5: test of top-level asm
+ **********************************************************************/
+
+/* Create the equivalent of:
+
+   // Quote from here in docs/cp/topics/asm.rst: example 5: C
+     asm ("\t.pushsection .text\n"
+          "\t.globl add_asm\n"
+          "\t.type add_asm, @function\n"
+          "add_asm:\n"
+          "\tmovq %rdi, %rax\n"
+          "\tadd %rsi, %rax\n"
+          "\tret\n"
+          "\t.popsection\n");
+   // Quote up to here in docs/cp/topics/asm.rst: example 5: C
+
+   to add a simple function ("add_asm") directly in assembly language.  */
+
+static void
+create_test_i386_basic_asm_5 (gcc_jit_context *c_ctxt)
+{
+  gccjit::context ctxt (c_ctxt);
+  /* Quote from here in docs/cp/topics/asm.rst: example 5: jit.  */
+  ctxt.add_top_level_asm ("\t.pushsection .text\n"
+                          "\t.globl add_asm\n"
+                          "\t.type add_asm, @function\n"
+                          "add_asm:\n"
+                          "\tmovq %rdi, %rax\n"
+                          "\tadd %rsi, %rax\n"
+                          "\tret\n"
+                          "\t# some asm here\n"
+                          "\t.popsection\n");
+  /* Quote up to here in docs/cp/topics/asm.rst: example 5: jit.  */
+}
+
+static void
+verify_code_5 (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*test_i386_basic_asm_5_type) (int, int);
+  test_i386_basic_asm_5_type test_i386_basic_asm_5
+    = (test_i386_basic_asm_5_type) gcc_jit_result_get_code (result, "add_asm");
+  CHECK_NON_NULL (test_i386_basic_asm_5);
+
+  CHECK_VALUE (test_i386_basic_asm_5 (2, 2), 4);
+  CHECK_VALUE (test_i386_basic_asm_5 (20, 7), 27);
+}
+
+/**********************************************************************
+ Code for harness
+ **********************************************************************/
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  // TODO:
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
+    1);
+  gcc_jit_context_set_bool_option (
+    ctxt,
+    GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING,
+    1);
+
+  /* TODO: make the tests only run on x86
+     How to check target config?  Can we get at a triple?
+     DejaGnu has "global target_triplet" which is set up via args to
+     runtest.exp.
+     Elements of a triple?  */
+
+  create_test_i386_basic_asm_1 (ctxt);
+  create_test_i386_basic_asm_2 (ctxt);
+  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3a", 0);
+  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3b", 1);
+  create_test_i386_basic_asm_4 (ctxt);
+  create_test_i386_basic_asm_5 (ctxt);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_NON_NULL (result);
+  verify_code_1 (ctxt, result);
+  verify_code_2 (ctxt, result);
+  verify_code_3 (ctxt, result, "test_i386_basic_asm_3a");
+  verify_code_3 (ctxt, result, "test_i386_basic_asm_3b");
+  verify_code_4 (ctxt, result);
+  verify_code_5 (ctxt, result);
+}
-- 
2.26.2

