public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed] jit: add support for inline asm [PR87291]
@ 2020-11-12 22:35 David Malcolm
  0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2020-11-12 22:35 UTC (permalink / raw)
  To: gcc-patches, jit

This patch adds various entrypoints to libgccjit for directly embedding
asm statements into a compile, analogous to inline asm in the C frontend:
  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

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to master as 421d0d0f54294a7bf2872b3b2ac521ce0fa9869e.

gcc/jit/ChangeLog:
	PR jit/87291
	* docs/cp/topics/asm.rst: New file.
	* docs/cp/topics/index.rst (Topic Reference): Add it.
	* docs/topics/asm.rst: New file.
	* docs/topics/compatibility.rst (LIBGCCJIT_ABI_15): New.
	* docs/topics/functions.rst (Statements): Add link to extended
	asm.
	* docs/topics/index.rst (Topic Reference): Add asm.rst.
	* docs/topics/objects.rst: Add gcc_jit_extended_asm to ASCII art.
	* jit-common.h (gcc::jit::recording::extended_asm): New forward
	decl.
	(gcc::jit::recording::top_level_asm): Likewise.
	* jit-playback.c: Include "stmt.h".
	(build_string): New.
	(gcc::jit::playback::context::new_string_literal): Disambiguate
	build_string call.
	(gcc::jit::playback::context::add_top_level_asm): New.
	(build_operand_chain): New.
	(build_clobbers): New.
	(build_goto_operands): New.
	(gcc::jit::playback::block::add_extended_asm): New.
	* jit-playback.h (gcc::jit::playback::context::add_top_level_asm):
	New decl.
	(struct gcc::jit::playback::asm_operand): New struct.
	(gcc::jit::playback::block::add_extended_asm): New decl.
	* jit-recording.c (gcc::jit::recording::context::dump_to_file):
	Dump top-level asms.
	(gcc::jit::recording::context::add_top_level_asm): New.
	(gcc::jit::recording::block::add_extended_asm): New.
	(gcc::jit::recording::block::end_with_extended_asm_goto): New.
	(gcc::jit::recording::asm_operand::asm_operand): New.
	(gcc::jit::recording::asm_operand::print): New.
	(gcc::jit::recording::asm_operand::make_debug_string): New.
	(gcc::jit::recording::output_asm_operand::write_reproducer): New.
	(gcc::jit::recording::output_asm_operand::print): New.
	(gcc::jit::recording::input_asm_operand::write_reproducer): New.
	(gcc::jit::recording::input_asm_operand::print): New.
	(gcc::jit::recording::extended_asm::add_output_operand): New.
	(gcc::jit::recording::extended_asm::add_input_operand): New.
	(gcc::jit::recording::extended_asm::add_clobber): New.
	(gcc::jit::recording::extended_asm::replay_into): New.
	(gcc::jit::recording::extended_asm::make_debug_string): New.
	(gcc::jit::recording::extended_asm::write_flags): New.
	(gcc::jit::recording::extended_asm::write_clobbers): New.
	(gcc::jit::recording::extended_asm_simple::write_reproducer): New.
	(gcc::jit::recording::extended_asm::maybe_populate_playback_blocks):
	New.
	(gcc::jit::recording::extended_asm_goto::extended_asm_goto): New.
	(gcc::jit::recording::extended_asm_goto::replay_into): New.
	(gcc::jit::recording::extended_asm_goto::write_reproducer): New.
	(gcc::jit::recording::extended_asm_goto::get_successor_blocks):
	New.
	(gcc::jit::recording::extended_asm_goto::maybe_print_gotos): New.
	(gcc::jit::recording::extended_asm_goto::maybe_populate_playback_blocks):
	New.
	(gcc::jit::recording::top_level_asm::top_level_asm): New.
	(gcc::jit::recording::top_level_asm::replay_into): New.
	(gcc::jit::recording::top_level_asm::make_debug_string): New.
	(gcc::jit::recording::top_level_asm::write_to_dump): New.
	(gcc::jit::recording::top_level_asm::write_reproducer): New.
	* jit-recording.h
	(gcc::jit::recording::context::add_top_level_asm): New decl.
	(gcc::jit::recording::context::m_top_level_asms): New field.
	(gcc::jit::recording::block::add_extended_asm): New decl.
	(gcc::jit::recording::block::end_with_extended_asm_goto): New
	decl.
	(gcc::jit::recording::asm_operand): New class.
	(gcc::jit::recording::output_asm_operand): New class.
	(gcc::jit::recording::input_asm_operand): New class.
	(gcc::jit::recording::extended_asm): New class.
	(gcc::jit::recording::extended_asm_simple): New class.
	(gcc::jit::recording::extended_asm_goto): New class.
	(gcc::jit::recording::top_level_asm): New class.
	* libgccjit++.h (gccjit::extended_asm): New forward decl.
	(gccjit::context::add_top_level_asm): New.
	(gccjit::block::add_extended_asm): New.
	(gccjit::block::end_with_extended_asm_goto): New.
	(gccjit::extended_asm): New class.
	(gccjit::extended_asm::extended_asm): New ctors.
	(gccjit::extended_asm::set_volatile_flag): New.
	(gccjit::extended_asm::set_inline_flag): New.
	(gccjit::extended_asm::add_output_operand): New.
	(gccjit::extended_asm::add_input_operand): New.
	(gccjit::extended_asm::add_clobber): New.
	(gccjit::extended_asm::get_inner_extended_asm): New.
	* libgccjit.c (struct gcc_jit_extended_asm): New.
	(jit_error): Make "loc" param take a gcc::jit::recording::location *
	rather than a gcc_jit_location *.
	(gcc_jit_block_add_extended_asm): New entrypoint.
	(gcc_jit_block_end_with_extended_asm_goto): New entrypoint.
	(gcc_jit_extended_asm_as_object): New entrypoint.
	(gcc_jit_extended_asm_set_volatile_flag): New entrypoint.
	(gcc_jit_extended_asm_set_inline_flag): New entrypoint.
	(gcc_jit_extended_asm_add_output_operand): New entrypoint.
	(gcc_jit_extended_asm_add_clobber): New entrypoint.
	(gcc_jit_context_add_top_level_asm): New entrypoint.
	* libgccjit.h: Add gcc_jit_extended_asm to ASCII art.
	(gcc_jit_extended_asm): New typedef.
	(LIBGCCJIT_HAVE_ASM_STATEMENTS): New define.
	(gcc_jit_block_add_extended_asm): New entrypoint.
	(gcc_jit_block_end_with_extended_asm_goto): New entrypoint.
	(gcc_jit_extended_asm_as_object): New entrypoint.
	(gcc_jit_extended_asm_set_volatile_flag): New entrypoint.
	(gcc_jit_extended_asm_set_inline_flag): New entrypoint.
	(gcc_jit_extended_asm_add_output_operand): New entrypoint.
	(gcc_jit_extended_asm_add_input_operand): New entrypoint.
	(gcc_jit_extended_asm_add_clobber): New entrypoint.
	(gcc_jit_context_add_top_level_asm): New entrypoint.
	* libgccjit.map (LIBGCCJIT_ABI_15): New.

gcc/testsuite/ChangeLog:
	PR jit/87291
	* jit.dg/jit.exp: Load target-supports-dg.exp.
	Set dg-do-what-default.
	(jit-dg-test): Set dg-do-what and call dg-get-options, skipping
	the test if it's not supported on the given target.
	* jit.dg/test-asm.c: New test.
	* jit.dg/test-asm.cc: New test.
---
 gcc/jit/docs/cp/topics/asm.rst        | 308 +++++++++++++++
 gcc/jit/docs/cp/topics/index.rst      |   1 +
 gcc/jit/docs/topics/asm.rst           | 311 ++++++++++++++++
 gcc/jit/docs/topics/compatibility.rst |  17 +
 gcc/jit/docs/topics/functions.rst     |   3 +
 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                | 125 ++++++-
 gcc/jit/jit-playback.h                |  27 ++
 gcc/jit/jit-recording.c               | 514 ++++++++++++++++++++++++++
 gcc/jit/jit-recording.h               | 215 +++++++++++
 gcc/jit/libgccjit++.h                 | 170 +++++++++
 gcc/jit/libgccjit.c                   | 188 +++++++++-
 gcc/jit/libgccjit.h                   | 103 ++++++
 gcc/jit/libgccjit.map                 |  13 +
 gcc/testsuite/jit.dg/jit.exp          |  31 ++
 gcc/testsuite/jit.dg/test-asm.c       | 492 ++++++++++++++++++++++++
 gcc/testsuite/jit.dg/test-asm.cc      | 453 +++++++++++++++++++++++
 19 files changed, 2972 insertions(+), 3 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..69e2d1e8da7
--- /dev/null
+++ b/gcc/jit/docs/cp/topics/asm.rst
@@ -0,0 +1,308 @@
+.. 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++
+========================================
+
+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.
+
+These entrypoints were added in :ref:`LIBGCCJIT_ABI_15`; you can test
+for their presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ASM_STATEMENTS
+
+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,\
+                                                  gccjit::location loc = location ())
+
+   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..b91514d4140
--- /dev/null
+++ b/gcc/jit/docs/topics/asm.rst
@@ -0,0 +1,311 @@
+.. 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
+======================================
+
+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.
+
+These entrypoints were added in :ref:`LIBGCCJIT_ABI_15`; you can test
+for their presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ASM_STATEMENTS
+
+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,\
+                                                 gcc_jit_location *loc,\
+                                                 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 6bfa101ed71..b953da590f1 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -226,3 +226,20 @@ entrypoints:
 --------------------
 ``LIBGCCJIT_ABI_14`` covers the addition of
 :func:`gcc_jit_global_set_initializer`
+
+.. _LIBGCCJIT_ABI_15:
+
+``LIBGCCJIT_ABI_15``
+-----------------------
+``LIBGCCJIT_ABI_15`` covers the addition of API entrypoints for directly
+embedding assembler instructions:
+
+  * :func:`gcc_jit_block_add_extended_asm`
+  * :func:`gcc_jit_block_end_with_extended_asm_goto`
+  * :func:`gcc_jit_extended_asm_as_object`
+  * :func:`gcc_jit_extended_asm_set_volatile_flag`
+  * :func:`gcc_jit_extended_asm_set_inline_flag`
+  * :func:`gcc_jit_extended_asm_add_output_operand`
+  * :func:`gcc_jit_extended_asm_add_input_operand`
+  * :func:`gcc_jit_extended_asm_add_clobber`
+  * :func:`gcc_jit_context_add_top_level_asm`
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index eb40d64010e..b869256a1cd 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -458,3 +458,6 @@ Statements
        :start-after: /* Quote from here in docs/topics/functions.rst.  */
        :end-before: /* Quote up to here in docs/topics/functions.rst.  */
        :language: c
+
+See also :type:`gcc_jit_extended_asm` for entrypoints for adding inline
+assembler statements to a function.
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 4fac64dcab7..5bccf591a3f 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,18 @@ namespace jit {
  Playback.
  **********************************************************************/
 
+/* Build a STRING_CST tree for STR, or return NULL if it is NULL.
+   The TREE_TYPE is not initialized.  */
+
+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)
@@ -774,7 +787,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
@@ -821,6 +834,18 @@ as_truth_value (tree expr, location *loc)
   return expr;
 }
 
+/* Add a "top-level" basic asm statement (i.e. one outside of any functions)
+   containing ASM_STMTS.
+
+   Compare with c_parser_asm_definition.  */
+
+void
+playback::context::add_top_level_asm (const char *asm_stmts)
+{
+  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.  */
 
@@ -1897,6 +1922,104 @@ add_switch (location *loc,
   add_stmt (switch_stmt);
 }
 
+/* Convert OPERANDS to a tree-based chain suitable for creating an
+   extended asm stmt.
+   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;
+}
+
+/* Convert CLOBBERS to a tree-based list suitable for creating an
+   extended asm stmt.
+   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;
+}
+
+/* Convert BLOCKS to a tree-based list suitable for creating an
+   extended asm stmt.
+   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);
+}
+
+/* Add an extended asm statement to this block.
+
+   Compare with c_parser_asm_statement (in c/c-parser.c)
+   and build_asm_expr (in c/c-typeck.c).  */
+
+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)
+{
+  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.  */
+  ASM_VOLATILE_P (asm_stmt) = (outputs->length () == 0);
+  ASM_INPUT_P (asm_stmt) = 0; /* extended asm stmts are not "simple".  */
+  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 50b69753bb4..ff1f778d026 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -252,6 +252,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 ();
 
@@ -514,6 +516,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:
@@ -563,6 +580,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 3a84c1fc5c0..1b0f8bc7db3 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -1578,6 +1578,10 @@ recording::context::dump_to_file (const char *path, bool update_locations)
     {
       fn->write_to_dump (d);
     }
+
+  top_level_asm *tla;
+  FOR_EACH_VEC_ELT (m_top_level_asms, i, tla)
+    tla->write_to_dump (d);
 }
 
 static const char * const
@@ -1904,6 +1908,22 @@ recording::context::get_all_requested_dumps (vec <recording::requested_dump> *ou
   out->splice (m_requested_dumps);
 }
 
+/* Create a recording::top_level_asm instance and add it to this
+   context's list of mementos and to m_top_level_asms.
+
+   Implements the post-error-checking part of
+   gcc_jit_context_add_top_level_asm.  */
+
+void
+recording::context::add_top_level_asm (recording::location *loc,
+				       const char *asm_stmts)
+{
+  recording::top_level_asm *asm_obj
+    = new recording::top_level_asm (this, loc, 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.  */
@@ -4206,6 +4226,23 @@ recording::block::add_comment (recording::location *loc,
   return result;
 }
 
+/* Create a recording::extended_asm_simple instance and add it to
+   the block's context's list of mementos, and to the block's
+   list of statements.
+
+   Implements the heart of gcc_jit_block_add_extended_asm.  */
+
+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.
@@ -4288,6 +4325,30 @@ recording::block::end_with_switch (recording::location *loc,
   return result;
 }
 
+/* Create a recording::extended_asm_goto instance and add it to
+   the block's context's list of mementos, and to the block's
+   list of statements.
+
+   Implements the heart of gcc_jit_block_end_with_extended_asm_goto.  */
+
+
+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
@@ -6508,6 +6569,459 @@ 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.  */
+}
+
+recording::string *
+recording::asm_operand::make_debug_string ()
+{
+  pretty_printer pp;
+  print (&pp);
+  return m_ctxt->new_string (pp_formatted_text (&pp), false);
+}
+
+/* class output_asm_operand : public asm_operand.  */
+
+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.  */
+
+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.  */
+
+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);
+}
+
+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);
+}
+
+void
+recording::extended_asm::add_clobber (const char *victim)
+{
+  m_clobbers.safe_push (new_string (victim));
+}
+
+/* Implementation of 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_string (&pp, m_asm_template->get_debug_string ());
+  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 ());
+}
+
+/* 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 ()),
+	   m_asm_template->get_debug_string ());
+  write_flags (r);
+  write_clobbers (r);
+}
+
+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.  */
+
+/* recording::extended_asm_goto's ctor.  */
+
+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]);
+}
+
+/* Implementation of recording::memento::replay_into
+   for recording::extended_asm_goto.  */
+
+void
+recording::extended_asm_goto::replay_into (replayer *r)
+{
+  /* Chain up to base class impl.  */
+  recording::extended_asm::replay_into (r);
+
+  /* ...and potentially add a goto for the fallthrough.  */
+  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 ()),
+	   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);
+}
+
+/* Override the poisoned default implementation of
+   gcc::jit::recording::statement::get_successor_blocks
+
+   An extended_asm_goto can jump to the m_goto_blocks, and to
+   the (optional) m_fallthrough_block.  */
+
+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;
+}
+
+/* Vfunc for use by recording::extended_asm::make_debug_string.  */
+
+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 ());
+}
+
+/* Vfunc for use by recording::extended_asm::replay_into.  */
+
+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,
+					 location *loc,
+					 string *asm_stmts)
+: memento (ctxt),
+  m_loc (loc),
+  m_asm_stmts (asm_stmts)
+{
+}
+
+/* Implementation of recording::memento::replay_into for top-level asm.  */
+
+void
+recording::top_level_asm::replay_into (replayer *r)
+{
+  r->add_top_level_asm (m_asm_stmts->c_str ());
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   top-level asm.  */
+
+recording::string *
+recording::top_level_asm::make_debug_string ()
+{
+  return string::from_printf (m_ctxt, "asm (%s)",
+			      m_asm_stmts->get_debug_string ());
+}
+
+/* Override the default implementation of
+   recording::memento::write_to_dump.
+   Don't indent the string.  */
+
+void
+recording::top_level_asm::write_to_dump (dump &d)
+{
+  d.write ("%s;\n", get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for top-level asm. */
+
+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, /* gcc_jit_location *loc */\n"
+	   "                                     %s); /* const char *asm_stmts */\n",
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   m_asm_stmts->get_debug_string ());
+}
+
 } // namespace gcc::jit
 
 } // namespace gcc
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 9a43a7bf33a..e6948828d2a 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 (location *loc, 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;
@@ -1275,6 +1278,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,
@@ -1296,6 +1303,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
   {
@@ -2112,6 +2126,207 @@ 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;
+
+private:
+  string * make_debug_string () FINAL OVERRIDE;
+
+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:
+  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:
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  rvalue *m_src;
+};
+
+/* Abstract base class for extended_asm statements.  */
+
+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;
+};
+
+/* A group of top-level asm statements, as created by
+   gcc_jit_context_add_top_level_asm.  */
+
+class top_level_asm : public memento
+{
+public:
+  top_level_asm (context *ctxt, location *loc, string *asm_stmts);
+
+  void write_to_dump (dump &d) FINAL OVERRIDE;
+
+private:
+  void replay_into (replayer *r) FINAL OVERRIDE;
+  string * make_debug_string () FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  location *m_loc;
+  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 1b9ef1a5db9..b4901ceb68a 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,9 @@ namespace gccjit
 		    rvalue max_value,
 		    block dest_block);
 
+    void add_top_level_asm (const char *asm_stmts,
+			    location loc = location ());
+
   private:
     gcc_jit_context *m_inner_ctxt;
   };
@@ -449,6 +453,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
@@ -509,6 +520,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).
 
@@ -1259,6 +1304,14 @@ context::new_case (rvalue min_value,
 					  dest_block.get_inner_block ()));
 }
 
+inline void
+context::add_top_level_asm (const char *asm_stmts, location loc)
+{
+  gcc_jit_context_add_top_level_asm (m_inner_ctxt,
+				     loc.get_inner_location (),
+				     asm_stmts);
+}
+
 // class object
 inline context
 object::get_context () const
@@ -1554,6 +1607,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)
@@ -1767,6 +1851,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 a00aefc7108..f9c33c63c64 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;
@@ -3290,3 +3295,182 @@ gcc_jit_version_patchlevel (void)
   version_info vi;
   return vi.patchlevel;
 }
+
+/**********************************************************************
+ Asm support.
+ **********************************************************************/
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::add_extended_asm, in
+   jit-recording.c.  */
+
+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);
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::end_with_extended_asm_goto, in
+   jit-recording.c.  */
+
+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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::extended_asm::set_volatile_flag, in
+   jit-recording.c.  */
+
+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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::extended_asm::set_inline_flag, in
+   jit-recording.c.  */
+
+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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::extended_asm::add_output_operand, in
+   jit-recording.c.  */
+
+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 ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::extended_asm::add_input_operand, in
+   jit-recording.c.  */
+
+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 ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::extended_asm::add_clobber, in
+   jit-recording.c.  */
+
+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 ();
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  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.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::add_top_level_asm, in
+   jit-recording.c.  */
+
+void
+gcc_jit_context_add_top_level_asm (gcc_jit_context *ctxt,
+				   gcc_jit_location *loc,
+				   const char *asm_stmts)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_IF_FAIL (asm_stmts, ctxt, NULL, "NULL asm_stmts");
+  ctxt->add_top_level_asm (loc, asm_stmts);
+}
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 7fbaa9f3162..c523f93a821 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,12 @@ typedef struct gcc_jit_param gcc_jit_param;
    destination block.  */
 typedef struct gcc_jit_case gcc_jit_case;
 
+/* A gcc_jit_extended_asm represents an assembly language statement,
+   analogous to an extended "asm" statement in GCC's C front-end: a series
+   of low-level instructions inside a function that convert inputs to
+   outputs.  */
+typedef struct gcc_jit_extended_asm gcc_jit_extended_asm;
+
 /* Acquire a JIT-compilation context.  */
 extern gcc_jit_context *
 gcc_jit_context_acquire (void);
@@ -1518,6 +1525,102 @@ 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
+
+   These API entrypoints were added in LIBGCCJIT_ABI_15; you can test for their
+   presence using
+     #ifdef LIBGCCJIT_HAVE_ASM_STATEMENTS
+*/
+
+#define LIBGCCJIT_HAVE_ASM_STATEMENTS
+
+/* 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);
+
+/* Add "asm_stmts", a set of top-level asm statements, analogous to
+   those created by GCC's "basic" asm syntax in C at file scope.  */
+
+extern void
+gcc_jit_context_add_top_level_asm (gcc_jit_context *ctxt,
+				   gcc_jit_location *loc,
+				   const char *asm_stmts);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index a6e67e781a4..bcabe167c05 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -192,3 +192,16 @@ LIBGCCJIT_ABI_14 {
   global:
     gcc_jit_global_set_initializer;
 } LIBGCCJIT_ABI_13;
+
+LIBGCCJIT_ABI_15 {
+  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_14;
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 2d8c884b6b8..9af87f9c6ad 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -37,12 +37,16 @@ load_lib target-libpath.exp
 load_lib gcc.exp
 load_lib g++.exp
 load_lib dejagnu.exp
+load_lib target-supports-dg.exp
 
 # Skip these tests for targets that don't support -lgccjit
 if { ![check_effective_target_lgccjit] } {
     return
 }
 
+# The default do-what keyword.
+set dg-do-what-default compile
+
 # Look for lines of the form:
 #   definitely lost: 11,316 bytes in 235 blocks
 #   indirectly lost: 352 bytes in 4 blocks
@@ -379,6 +383,33 @@ proc jit-dg-test { prog do_what extra_tool_flags } {
     verbose "  do_what: $do_what"
     verbose "  extra_tool_flags: $extra_tool_flags"
 
+    global dg-do-what-default
+    set dg-do-what [list ${dg-do-what-default} "" P]
+
+    set tmp [dg-get-options $prog]
+    foreach op $tmp {
+	verbose "Processing option: $op" 3
+	set status [catch "$op" errmsg]
+	if { $status != 0 } {
+	    if { 0 && [info exists errorInfo] } {
+		# This also prints a backtrace which will just confuse
+		# testcase writers, so it's disabled.
+		perror "$name: $errorInfo\n"
+	    } else {
+		perror "$name: $errmsg for \"$op\"\n"
+	    }
+	    perror "$name: $errmsg for \"$op\"" 0
+	    return
+	}
+    }
+
+    # If we're not supposed to try this test on this target, we're done.
+    if { [lindex ${dg-do-what} 1] == "N" } {
+	unsupported "$name"
+	verbose "$name not supported on this target, skipping it" 3
+	return
+    }
+
     # test-threads.c needs to be linked against pthreads
     if {[string match "*test-threads.c" $prog]} {
 	append extra_tool_flags " -lpthread"
diff --git a/gcc/testsuite/jit.dg/test-asm.c b/gcc/testsuite/jit.dg/test-asm.c
new file mode 100644
index 00000000000..e7777eeb4fe
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-asm.c
@@ -0,0 +1,492 @@
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+/**********************************************************************
+ Support fns for creating code.
+ **********************************************************************/
+
+/* Make a "void FUNC_NAME (void)" function with a single block, returning
+   that block.  */
+
+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 ();
+}
+
+/**********************************************************************
+ 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, NULL,
+                                     "\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)
+{
+  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..6f1828060ff
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-asm.cc
@@ -0,0 +1,453 @@
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+
+#include "libgccjit++.h"
+
+#include "harness.h"
+
+/**********************************************************************
+ Support fns for creating code.
+ **********************************************************************/
+
+/* Make a "void FUNC_NAME (void)" function with a single block, returning
+   that block.  */
+
+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 ();
+}
+
+/**********************************************************************
+ 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)
+{
+  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


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-11-12 22:36 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-12 22:35 [committed] jit: add support for inline asm [PR87291] David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).