From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 303B6386F825 for ; Thu, 12 Nov 2020 22:36:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 303B6386F825 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-590-R04Nhp1yNeWDyLQUHckRNA-1; Thu, 12 Nov 2020 17:36:03 -0500 X-MC-Unique: R04Nhp1yNeWDyLQUHckRNA-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 96034107464A; Thu, 12 Nov 2020 22:36:02 +0000 (UTC) Received: from t470.redhat.com (ovpn-112-135.phx2.redhat.com [10.3.112.135]) by smtp.corp.redhat.com (Postfix) with ESMTP id E3B705B4C2; Thu, 12 Nov 2020 22:35:58 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org, jit@gcc.gnu.org Subject: [committed] jit: add support for inline asm [PR87291] Date: Thu, 12 Nov 2020 17:35:56 -0500 Message-Id: <20201112223556.3590957-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: jit@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Jit mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Nov 2020 22:36:19 -0000 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 + + 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 + . + +.. 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 `_ +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 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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 + + 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 + . + +.. 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 `_ +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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 `_ + 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 @@ -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 *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 *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 *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 *outputs, + const auto_vec *inputs, + const auto_vec *clobbers, + const auto_vec *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 *cases); + void + add_extended_asm (location *loc, + const char *asm_template, + bool is_volatile, + bool is_inline, + const auto_vec *outputs, + const auto_vec *inputs, + const auto_vec *clobbers, + const auto_vec *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 *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_output_ops; + auto_vec playback_input_ops; + auto_vec playback_clobbers; + auto_vec 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 *) +{ + /* 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::extended_asm_goto::get_successor_blocks () const +{ + vec 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 *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 m_compound_types; auto_vec m_globals; auto_vec m_functions; + auto_vec 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 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 *out) = 0; + +protected: + string *m_asm_template; + bool m_is_volatile; + bool m_is_inline; + auto_vec m_output_ops; + auto_vec m_input_ops; + auto_vec 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 *) 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 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 *out) FINAL OVERRIDE; + +private: + auto_vec 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 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 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 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 (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 (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 (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 (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 +#include +#include +#include + +#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 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 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 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 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 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