>From b55719183abf5ff37373115b5929ebd6ca914258 Mon Sep 17 00:00:00 2001
From: Vincent Li <[email protected]>
Date: Fri, 23 Oct 2026 13:23:48 -0400
Subject: [PATCH] jit: Add support for computed gotos and block
 addresses

This patch adds `gcc_jit_block_get_address` and
`gcc_jit_block_end_with_indirect_jump`
under ABI version LIBGCCJIT_ABI_39. This exposes GNU C
labels-as-values (`&&label`)
and indirect jumps (`goto *expr;`) in libgccjit, enabling JIT-compiled
interpreter
dispatch loops and state machines to implement direct-threaded code tables.

Tested on x86_64-pc-linux-gnu. All 30 iterations of jit.dg/test-indirect-jump.c
passed across -O0 to -O3. check_GNU_style.py reports 0 errors.

gcc/jit/ChangeLog:

    * docs/topics/expressions.rst (gcc_jit_block_get_address): Document.
    * docs/topics/functions.rst
(gcc_jit_block_end_with_indirect_jump): Document.
    * jit-playback.cc (playback::block::get_address): New method.
    (playback::block::add_indirect_jump): New method.
    * jit-playback.h (playback::block::get_address): New method.
    (playback::block::add_indirect_jump): New method.
    * jit-recording.cc (recording::block::get_address): New method.
    (recording::block::end_with_indirect_jump): New method.
    (recording::get_block_address::replay_into): New method.
    (recording::get_block_address::make_debug_string): New method.
    (recording::get_block_address::write_reproducer): New method.
    (recording::indirect_jump::replay_into): New method.
    (recording::indirect_jump::get_successor_blocks): New method.
    (recording::indirect_jump::make_debug_string): New method.
    (recording::indirect_jump::write_reproducer): New method.
    * jit-recording.h (class get_block_address): New class.
    (class indirect_jump): New class.
    (recording::block::get_address): New method.
    (recording::block::end_with_indirect_jump): New method.
    * libgccjit.cc (gcc_jit_block_get_address): New entrypoint.
    (gcc_jit_block_end_with_indirect_jump): New entrypoint.
    * libgccjit.h (LIBGCCJIT_HAVE_gcc_jit_block_get_address): New macro.
    (LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump): New macro.
    (gcc_jit_block_get_address): New declaration.
    (gcc_jit_block_end_with_indirect_jump): New declaration.
    * libgccjit.map (LIBGCCJIT_ABI_39): Add gcc_jit_block_get_address and
    gcc_jit_block_end_with_indirect_jump.

gcc/testsuite/ChangeLog:

    * jit.dg/all-non-failing-tests.h: Include test-indirect-jump.c.
    * jit.dg/test-indirect-jump.c: New test.

Signed-off-by: Vincent Li <[email protected]>
---
 gcc/jit/docs/topics/expressions.rst          |  14 ++
 gcc/jit/docs/topics/functions.rst            |  21 +++
 gcc/jit/jit-playback.cc                      |  39 +++++
 gcc/jit/jit-playback.h                       |   7 +
 gcc/jit/jit-recording.cc                     | 157 +++++++++++++++++++
 gcc/jit/jit-recording.h                      |  57 ++++++-
 gcc/jit/libgccjit.cc                         |  63 ++++++++
 gcc/jit/libgccjit.h                          |  39 ++++-
 gcc/jit/libgccjit.map                        |   6 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h |  10 ++
 gcc/testsuite/jit.dg/test-indirect-jump.c    | 108 +++++++++++++
 11 files changed, 518 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-indirect-jump.c

diff --git a/gcc/jit/docs/topics/expressions.rst
b/gcc/jit/docs/topics/expressions.rst
index 680ea1d86..0f9f705fe 100644
--- a/gcc/jit/docs/topics/expressions.rst
+++ b/gcc/jit/docs/topics/expressions.rst
@@ -806,6 +806,20 @@ where the rvalue is computed by reading from the
storage area.

    in C.

+.. function:: gcc_jit_rvalue * \
+          gcc_jit_block_get_address (gcc_jit_block *block, \
+                     gcc_jit_location *loc)
+
+   Get the address of a basic block as an rvalue of type ``void *``,
+   analogous to GNU C's ``&&label``.
+
+   This API entrypoint was added in ``LIBGCCJIT_ABI_39``; you can test for its
+   presence using:
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_get_address
+
 .. function:: void\
               gcc_jit_lvalue_set_tls_model (gcc_jit_lvalue *lvalue,\
                                             enum gcc_jit_tls_model model)
diff --git a/gcc/jit/docs/topics/functions.rst
b/gcc/jit/docs/topics/functions.rst
index 66d4b6b2f..1f2eefb1c 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -466,6 +466,27 @@ Statements

       goto target;

+.. function:: void\
+          gcc_jit_block_end_with_indirect_jump (\
+          gcc_jit_block *block,\
+          gcc_jit_location *loc,\
+          gcc_jit_rvalue *target_address,\
+          int num_destinations,\
+          gcc_jit_block **destinations)
+
+   Terminate a basic block by adding an indirect jump to ``target_address``,
+   analogous to GNU C's ``goto *target_address;``.
+
+   All possible destination blocks must be listed in the ``destinations``
+   array to allow GCC's middle-end to construct valid CFG edges.
+
+   This API entrypoint was added in ``LIBGCCJIT_ABI_39``; you can test for its
+   presence using:
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump
+
 .. function:: void\
               gcc_jit_block_end_with_return (gcc_jit_block *block,\
                                              gcc_jit_location *loc,\
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 56c9413f8..66a1e8fef 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -2547,6 +2547,45 @@ c_finish_goto_label (location_t loc, tree label)

 }

+/* Take the address of a block as a void * rvalue (&&block).  */
+
+playback::rvalue *
+playback::block::
+get_address (location *loc)
+{
+  tree label = as_label_decl ();
+  TREE_USED (label) = 1;
+  FORCED_LABEL (label) = 1;
+
+  tree addr = build1 (ADDR_EXPR, ptr_type_node, label);
+  if (loc)
+    set_tree_location (addr, loc);
+
+  return new rvalue (m_func->m_ctxt, addr);
+}
+
+/* Add an indirect jump statement (goto *target_address;).  */
+
+void
+playback::block::add_indirect_jump (location *loc,
+                    rvalue *target_address,
+                    auto_vec <block *> *destinations)
+{
+  unsigned i;
+  block *dest;
+  FOR_EACH_VEC_ELT (*destinations, i, dest)
+    {
+      tree label = dest->as_label_decl ();
+      TREE_USED (label) = 1;
+      FORCED_LABEL (label) = 1;
+    }
+
+  tree stmt = build1 (GOTO_EXPR, void_type_node, target_address->as_tree ());
+  if (loc)
+    set_tree_location (stmt, loc);
+  add_stmt (stmt);
+}
+
 /* Add a return statement to the function's statement list.  */

 void
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index ad3e41f3d..d32ed4d49 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -692,6 +692,13 @@ public:
   add_jump (location *loc,
         block *target);

+  rvalue *get_address (location *loc);
+
+  void
+  add_indirect_jump (location *loc,
+             rvalue *target_address,
+             auto_vec <block *> *destinations);
+
   void
   add_return (location *loc,
           rvalue *rvalue);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index e28e5f67c..47983e06c 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -5072,6 +5072,26 @@ recording::block::end_with_jump
(recording::location *loc,
   return result;
 }

+/* Create a recording::end_with_indirect_jump 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_indirect_jump.  */
+
+recording::statement *
+recording::block::end_with_indirect_jump (location *loc,
+                      rvalue *target_address,
+                      int num_destinations,
+                      block **destinations)
+{
+  statement *result = new indirect_jump (this, loc, target_address,
+                     num_destinations, destinations);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+  return result;
+}
+
 /* Create a recording::end_with_return instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
@@ -5197,6 +5217,20 @@ recording::block::get_loc () const
     return NULL;
 }

+/* Create a recording::get_address 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_get_address.  */
+
+recording::rvalue *
+recording::block::get_address (location *loc)
+{
+  recording::rvalue *result = new get_block_address (m_ctxt, loc, this);
+  m_ctxt->record (result);
+  return result;
+}
+
 /* Get the first statement within a block, if any.  */

 recording::statement *
@@ -7332,6 +7366,45 @@
recording::get_address_of_lvalue::write_reproducer (reproducer &r)
        r.get_identifier (m_loc));
 }

+/* The implementation of class gcc::jit::recording::get_block_address.  */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::get_block_address.  */
+
+void
+recording::get_block_address::replay_into (replayer *r)
+{
+  set_playback_obj (
+    m_block->playback_block ()->
+      get_address (playback_location (r, m_loc)));
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   getting the address of a block.  */
+
+recording::string *
+recording::get_block_address::make_debug_string ()
+{
+  return string::from_printf (m_ctxt,
+                  "&&%s",
+                  m_block->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   get_block_address.  */
+
+void
+recording::get_block_address::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "address_of");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+       "    gcc_jit_block_get_address (%s,\n"
+       "                   %s);\n",
+       id,
+       r.get_identifier (m_block),
+       r.get_identifier (m_loc));
+}
+
 /* The implementation of class gcc::jit::recording::function_pointer.  */

 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -7765,6 +7838,90 @@ recording::jump::write_reproducer (reproducer &r)
        r.get_identifier (m_target));
 }

+/* The implementation of class gcc::jit::recording::indirect_jump.  */
+
+/* gcc::jit::recording::indirect_jump's constructor.  */
+
+recording::indirect_jump::indirect_jump (block *b,
+                     location *loc,
+                     rvalue *target_address,
+                     int num_destinations,
+                     block **destinations)
+: statement (b, loc),
+  m_target_address (target_address)
+{
+  m_destinations.reserve_exact (num_destinations);
+  for (int i = 0; i < num_destinations; i++)
+    m_destinations.quick_push (destinations[i]);
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::indirect_jump.  */
+
+void
+recording::indirect_jump::replay_into (replayer *r)
+{
+  auto_vec <playback::block *> pb_dests;
+  pb_dests.reserve_exact (m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    pb_dests.quick_push (m_destinations[i]->playback_block ());
+
+  playback_block (get_block ())
+    ->add_indirect_jump (playback_location (r),
+             m_target_address->playback_rvalue (),
+             &pb_dests);
+}
+
+/* Override the poisoned default implementation of
+   gcc::jit::recording::statement::get_successor_blocks.  */
+
+vec <recording::block *>
+recording::indirect_jump::get_successor_blocks () const
+{
+  vec <block *> result;
+  result.create (m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    result.quick_push (m_destinations[i]);
+  return result;
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   an indirect jump statement.  */
+
+recording::string *
+recording::indirect_jump::make_debug_string ()
+{
+  return string::from_printf (m_ctxt,
+                  "goto *%s;",
+                  m_target_address->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   indirect_jump statements.  */
+
+void
+recording::indirect_jump::write_reproducer (reproducer &r)
+{
+  r.make_identifier (this, "indirect_jump");
+  const char *dests_id = r.make_tmp_identifier ("destinations_for", this);
+  r.write ("  gcc_jit_block *%s[%i] = {\n",
+       dests_id,
+       m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    r.write ("    %s,\n", r.get_identifier (m_destinations[i]));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_block_end_with_indirect_jump (%s,\n"
+       "                        %s,\n"
+       "                        %s,\n"
+       "                        %i,\n"
+       "                        %s);\n",
+       r.get_identifier (get_block ()),
+       r.get_identifier (get_loc ()),
+       r.get_identifier_as_rvalue (m_target_address),
+       m_destinations.length (),
+       dests_id);
+}
+
 /* The implementation of class gcc::jit::recording::return_.  */

 /* Implementation of pure virtual hook recording::memento::replay_into
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 2a5f23a05..c500ea659 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -966,7 +966,7 @@ public:
   type *unqualified () final override { return m_other_type; }

   void replay_into (replayer *) final override;
-  memento_of_get_aligned *dyn_cast_aligned_type () final override
+  memento_of_get_aligned *dyn_cast_aligned_type () final override
   {
     return this;
   }
@@ -1682,6 +1682,13 @@ public:
   end_with_jump (location *loc,
          block *target);

+  rvalue * get_address (location *loc);
+
+  statement * end_with_indirect_jump (location *loc,
+                     rvalue *target_address,
+                     int num_destinations,
+                     block **destinations);
+
   statement *
   end_with_return (location *loc,
            rvalue *rvalue);
@@ -2427,6 +2434,32 @@ private:
   lvalue *m_lvalue;
 };

+class get_block_address : public rvalue
+{
+public:
+  get_block_address (context *ctxt,
+         location *loc,
+         block *b)
+  : rvalue (ctxt, loc, ctxt->get_type (GCC_JIT_TYPE_VOID_PTR)),
+    m_block (b)
+  {}
+
+  void replay_into (replayer *r) final override;
+
+  void visit_children (rvalue_visitor *) final override {}
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+  enum precedence get_precedence () const final override
+  {
+    return PRECEDENCE_UNARY;
+  }
+
+private:
+  block *m_block;
+};
+
 class function_pointer : public rvalue
 {
 public:
@@ -2650,6 +2683,28 @@ private:
   block *m_target;
 };

+class indirect_jump : public statement
+{
+public:
+  indirect_jump (block *b,
+         location *loc,
+         rvalue *target_address,
+         int num_destinations,
+         block **destinations);
+
+  void replay_into (replayer *r) final override;
+
+  vec <block *> get_successor_blocks () const final override;
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+
+private:
+  rvalue *m_target_address;
+  auto_vec <block *> m_destinations;
+};
+
 class return_ : public statement
 {
 public:
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 5a874fc48..4fac8c56b 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -2874,6 +2874,23 @@ gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
   return (gcc_jit_rvalue *)lvalue->get_address (loc);
 }

+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::get_address method in
+   jit-recording.cc.  */
+
+gcc_jit_rvalue *
+gcc_jit_block_get_address (gcc_jit_block *block,
+               gcc_jit_location *loc)
+{
+  RETURN_NULL_IF_FAIL (block, NULL, loc, "NULL block");
+  JIT_LOG_FUNC (block->get_context ()->get_logger ());
+  /* LOC can be NULL.  */
+
+  return (gcc_jit_rvalue *)block->get_address (loc);
+}
+
 /* Public entrypoint.  See description in libgccjit.h.

    After error-checking, the real work is done by the
@@ -3258,6 +3275,52 @@ gcc_jit_block_end_with_jump (gcc_jit_block *block,
   block->end_with_jump (loc, target);
 }

+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::end_with_indirect_jump method in
+   jit-recording.cc.  */
+
+void
+gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
+                      gcc_jit_location *loc,
+                      gcc_jit_rvalue *target_address,
+                      int num_destinations,
+                      gcc_jit_block **destinations)
+{
+  RETURN_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_IF_FAIL (target_address, ctxt, loc, "NULL target_address");
+  RETURN_IF_FAIL_PRINTF1 (
+    target_address->get_type ()->is_pointer (),
+    ctxt, loc,
+    "target_address: %s is not of a pointer type",
+    target_address->get_debug_string ());
+  RETURN_IF_FAIL (num_destinations > 0, ctxt, loc, "num_destinations <= 0");
+  RETURN_IF_FAIL (destinations, ctxt, loc, "NULL destinations");
+
+  for (int i = 0; i < num_destinations; i++)
+    {
+      RETURN_IF_FAIL_PRINTF1 (destinations[i], ctxt, loc,
+                  "NULL destination block at index %d", i);
+      RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == destinations[i]->get_function (),
+    ctxt, loc,
+    "destination block %d (%s) is not in the same function"
+    " (function %s) as source block %s",
+    i,
+    destinations[i]->get_debug_string (),
+    destinations[i]->get_function ()->get_debug_string (),
+    block->get_debug_string ());
+    }
+
+  block->end_with_indirect_jump (loc, target_address,
+                 num_destinations,
+                 (gcc::jit::recording::block **)destinations);
+}
+
 /* Public entrypoint.  See description in libgccjit.h.

    After error-checking, the real work is done by the
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 2247c99de..9d00ba33a 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1455,16 +1455,31 @@ extern gcc_jit_rvalue *
 gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
                 gcc_jit_location *loc);

+#define LIBGCCJIT_HAVE_gcc_jit_block_get_address
+
+/* Taking the address of a block; analogous to:
+     &&block
+   in C.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_39; you can test for its
+   presence using
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_get_address
+*/
+extern gcc_jit_rvalue *
+gcc_jit_block_get_address (gcc_jit_block *block,
+               gcc_jit_location *loc);
+
 #define LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model

 /* Set the thread-local storage model of a global variable

    This API entrypoint was added in LIBGCCJIT_ABI_17; you can test for its
    presence using
-     #ifdef LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model  */
+     #ifdef LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model
+*/
 extern void
 gcc_jit_lvalue_set_tls_model (gcc_jit_lvalue *lvalue,
-                enum gcc_jit_tls_model model);
+                  enum gcc_jit_tls_model model);

 #define LIBGCCJIT_HAVE_gcc_jit_lvalue_set_link_section

@@ -1592,6 +1607,26 @@ gcc_jit_block_end_with_jump (gcc_jit_block *block,
                  gcc_jit_location *loc,
                  gcc_jit_block *target);

+#define LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump
+
+/* Terminate a block by adding an indirect jump to target_address.
+
+   All possible indirect jump targets must be constrained to a fixed array.
+
+   This is roughly equivalent to this C code:
+
+      goto *target_address;
+
+   This API entrypoint was added in LIBGCCJIT_ABI_39; you can test for its
+   presence using
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump.  */
+extern void
+gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
+                      gcc_jit_location *loc,
+                      gcc_jit_rvalue *target_address,
+                      int num_destinations,
+                      gcc_jit_block **destinations);
+
 /* Terminate a block by adding evaluation of an rvalue, returning the value.

    This is roughly equivalent to this C code:
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 78b6be9ff..9412700a2 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -349,3 +349,9 @@ LIBGCCJIT_ABI_38 {
   global:
     gcc_jit_type_is_floating_point;
 } LIBGCCJIT_ABI_37;
+
+LIBGCCJIT_ABI_39 {
+  global:
+    gcc_jit_block_get_address;
+    gcc_jit_block_end_with_indirect_jump;
+} LIBGCCJIT_ABI_38;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index e8f8463e8..5bf18a874 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -448,6 +448,13 @@
 #undef create_code
 #undef verify_code

+/* test-indirect-jump.c */
+#define create_code create_code_indirect_jump
+#define verify_code verify_code_indirect_jump
+#include "test-indirect-jump.c"
+#undef create_code
+#undef verify_code
+
 /* test-target-info.c: This can't be in the testcases array as it
    is target-specific.  */

@@ -688,6 +695,9 @@ const struct testcase testcases[] = {
   {"vector_perm",
    create_code_vector_perm,
    verify_code_vector_perm},
+  {"indirect_jump",
+   create_code_indirect_jump,
+   verify_code_indirect_jump},
 };

 const int num_testcases = (sizeof (testcases) / sizeof (testcases[0]));
diff --git a/gcc/testsuite/jit.dg/test-indirect-jump.c
b/gcc/testsuite/jit.dg/test-indirect-jump.c
new file mode 100644
index 000000000..3d407f7ea
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-indirect-jump.c
@@ -0,0 +1,108 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+     int
+     test_indirect_jump (int idx)
+     {
+       void *targets[3];
+       targets[0] = &&b_zero;
+       targets[1] = &&b_one;
+       targets[2] = &&b_two;
+       goto *targets[idx];
+
+     b_zero:
+       return 10;
+     b_one:
+       return 20;
+     b_two:
+       return 30;
+     }
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *void_ptr_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+  gcc_jit_type *array_type =
+    gcc_jit_context_new_array_type (ctxt, NULL, void_ptr_type, 3);
+
+  gcc_jit_param *param_idx =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "idx");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+                  GCC_JIT_FUNCTION_EXPORTED,
+                  int_type,
+                  "test_indirect_jump",
+                  1, &param_idx,
+                  0);
+
+  gcc_jit_block *b_entry = gcc_jit_function_new_block (func, "entry");
+  gcc_jit_block *b_zero = gcc_jit_function_new_block (func, "b_zero");
+  gcc_jit_block *b_one = gcc_jit_function_new_block (func, "b_one");
+  gcc_jit_block *b_two = gcc_jit_function_new_block (func, "b_two");
+
+  gcc_jit_lvalue *targets =
+    gcc_jit_function_new_local (func, NULL, array_type, "targets");
+
+  gcc_jit_block *blocks[3] = { b_zero, b_one, b_two };
+  for (int i = 0; i < 3; i++)
+    {
+      gcc_jit_rvalue *idx_val =
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, i);
+      gcc_jit_lvalue *elem =
+    gcc_jit_context_new_array_access (ctxt, NULL,
+                      gcc_jit_lvalue_as_rvalue (targets),
+                      idx_val);
+      gcc_jit_rvalue *blk_addr =
+    gcc_jit_block_get_address (blocks[i], NULL);
+      gcc_jit_block_add_assignment (b_entry, NULL, elem, blk_addr);
+    }
+
+  /* void *target_addr = targets[idx]; */
+  gcc_jit_lvalue *selected_elem =
+    gcc_jit_context_new_array_access (ctxt, NULL,
+                      gcc_jit_lvalue_as_rvalue (targets),
+                      gcc_jit_param_as_rvalue (param_idx));
+
+  /* goto *targets[idx]; */
+  gcc_jit_block_end_with_indirect_jump (
+    b_entry, NULL,
+    gcc_jit_lvalue_as_rvalue (selected_elem),
+    3, blocks);
+
+  /* b_zero: return 10; */
+  gcc_jit_block_end_with_return (
+    b_zero, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 10));
+
+  /* b_one: return 20; */
+  gcc_jit_block_end_with_return (
+    b_one, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 20));
+
+  /* b_two: return 30; */
+  gcc_jit_block_end_with_return (
+    b_two, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 30));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*fn_type) (int);
+  CHECK_NON_NULL (result);
+
+  fn_type test_indirect_jump =
+    (fn_type)gcc_jit_result_get_code (result, "test_indirect_jump");
+  CHECK_NON_NULL (test_indirect_jump);
+
+  CHECK_VALUE (test_indirect_jump (0), 10);
+  CHECK_VALUE (test_indirect_jump (1), 20);
+  CHECK_VALUE (test_indirect_jump (2), 30);
+}
-- 
2.56.0
From b55719183abf5ff37373115b5929ebd6ca914258 Mon Sep 17 00:00:00 2001
From: Vincent Li <[email protected]>
Date: Fri, 23 Oct 2026 13:23:48 -0400
Subject: [PATCH] [PATCH] jit: Add support for computed gotos and block
 addresses

This patch adds `gcc_jit_block_get_address` and `gcc_jit_block_end_with_indirect_jump`
under ABI version LIBGCCJIT_ABI_39. This exposes GNU C labels-as-values (`&&label`)
and indirect jumps (`goto *expr;`) in libgccjit, enabling JIT-compiled interpreter
dispatch loops and state machines to implement direct-threaded code tables.

Tested on x86_64-pc-linux-gnu. All 30 iterations of jit.dg/test-indirect-jump.c
passed across -O0 to -O3. check_GNU_style.py reports 0 errors.

gcc/jit/ChangeLog:

	* docs/topics/expressions.rst (gcc_jit_block_get_address): Document.
	* docs/topics/functions.rst (gcc_jit_block_end_with_indirect_jump): Document.
	* jit-playback.cc (playback::block::get_address): New method.
	(playback::block::add_indirect_jump): New method.
	* jit-playback.h (playback::block::get_address): New method.
	(playback::block::add_indirect_jump): New method.
	* jit-recording.cc (recording::block::get_address): New method.
	(recording::block::end_with_indirect_jump): New method.
	(recording::get_block_address::replay_into): New method.
	(recording::get_block_address::make_debug_string): New method.
	(recording::get_block_address::write_reproducer): New method.
	(recording::indirect_jump::replay_into): New method.
	(recording::indirect_jump::get_successor_blocks): New method.
	(recording::indirect_jump::make_debug_string): New method.
	(recording::indirect_jump::write_reproducer): New method.
	* jit-recording.h (class get_block_address): New class.
	(class indirect_jump): New class.
	(recording::block::get_address): New method.
	(recording::block::end_with_indirect_jump): New method.
	* libgccjit.cc (gcc_jit_block_get_address): New entrypoint.
	(gcc_jit_block_end_with_indirect_jump): New entrypoint.
	* libgccjit.h (LIBGCCJIT_HAVE_gcc_jit_block_get_address): New macro.
	(LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump): New macro.
	(gcc_jit_block_get_address): New declaration.
	(gcc_jit_block_end_with_indirect_jump): New declaration.
	* libgccjit.map (LIBGCCJIT_ABI_39): Add gcc_jit_block_get_address and
	gcc_jit_block_end_with_indirect_jump.

gcc/testsuite/ChangeLog:

	* jit.dg/all-non-failing-tests.h: Include test-indirect-jump.c.
	* jit.dg/test-indirect-jump.c: New test.

Signed-off-by: Vincent Li <[email protected]>
---
 gcc/jit/docs/topics/expressions.rst          |  14 ++
 gcc/jit/docs/topics/functions.rst            |  21 +++
 gcc/jit/jit-playback.cc                      |  39 +++++
 gcc/jit/jit-playback.h                       |   7 +
 gcc/jit/jit-recording.cc                     | 157 +++++++++++++++++++
 gcc/jit/jit-recording.h                      |  57 ++++++-
 gcc/jit/libgccjit.cc                         |  63 ++++++++
 gcc/jit/libgccjit.h                          |  39 ++++-
 gcc/jit/libgccjit.map                        |   6 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h |  10 ++
 gcc/testsuite/jit.dg/test-indirect-jump.c    | 108 +++++++++++++
 11 files changed, 518 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-indirect-jump.c

diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst
index 680ea1d86..0f9f705fe 100644
--- a/gcc/jit/docs/topics/expressions.rst
+++ b/gcc/jit/docs/topics/expressions.rst
@@ -806,6 +806,20 @@ where the rvalue is computed by reading from the storage area.
 
    in C.
 
+.. function:: gcc_jit_rvalue * \
+	      gcc_jit_block_get_address (gcc_jit_block *block, \
+					 gcc_jit_location *loc)
+
+   Get the address of a basic block as an rvalue of type ``void *``,
+   analogous to GNU C's ``&&label``.
+
+   This API entrypoint was added in ``LIBGCCJIT_ABI_39``; you can test for its
+   presence using:
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_get_address
+
 .. function:: void\
               gcc_jit_lvalue_set_tls_model (gcc_jit_lvalue *lvalue,\
                                             enum gcc_jit_tls_model model)
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index 66d4b6b2f..1f2eefb1c 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -466,6 +466,27 @@ Statements
 
       goto target;
 
+.. function:: void\
+	      gcc_jit_block_end_with_indirect_jump (\
+	      gcc_jit_block *block,\
+	      gcc_jit_location *loc,\
+	      gcc_jit_rvalue *target_address,\
+	      int num_destinations,\
+	      gcc_jit_block **destinations)
+
+   Terminate a basic block by adding an indirect jump to ``target_address``,
+   analogous to GNU C's ``goto *target_address;``.
+
+   All possible destination blocks must be listed in the ``destinations``
+   array to allow GCC's middle-end to construct valid CFG edges.
+
+   This API entrypoint was added in ``LIBGCCJIT_ABI_39``; you can test for its
+   presence using:
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump
+
 .. function:: void\
               gcc_jit_block_end_with_return (gcc_jit_block *block,\
                                              gcc_jit_location *loc,\
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 56c9413f8..66a1e8fef 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -2547,6 +2547,45 @@ c_finish_goto_label (location_t loc, tree label)
 
 }
 
+/* Take the address of a block as a void * rvalue (&&block).  */
+
+playback::rvalue *
+playback::block::
+get_address (location *loc)
+{
+  tree label = as_label_decl ();
+  TREE_USED (label) = 1;
+  FORCED_LABEL (label) = 1;
+
+  tree addr = build1 (ADDR_EXPR, ptr_type_node, label);
+  if (loc)
+    set_tree_location (addr, loc);
+
+  return new rvalue (m_func->m_ctxt, addr);
+}
+
+/* Add an indirect jump statement (goto *target_address;).  */
+
+void
+playback::block::add_indirect_jump (location *loc,
+				    rvalue *target_address,
+				    auto_vec <block *> *destinations)
+{
+  unsigned i;
+  block *dest;
+  FOR_EACH_VEC_ELT (*destinations, i, dest)
+    {
+      tree label = dest->as_label_decl ();
+      TREE_USED (label) = 1;
+      FORCED_LABEL (label) = 1;
+    }
+
+  tree stmt = build1 (GOTO_EXPR, void_type_node, target_address->as_tree ());
+  if (loc)
+    set_tree_location (stmt, loc);
+  add_stmt (stmt);
+}
+
 /* Add a return statement to the function's statement list.  */
 
 void
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index ad3e41f3d..d32ed4d49 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -692,6 +692,13 @@ public:
   add_jump (location *loc,
 	    block *target);
 
+  rvalue *get_address (location *loc);
+
+  void
+  add_indirect_jump (location *loc,
+		     rvalue *target_address,
+		     auto_vec <block *> *destinations);
+
   void
   add_return (location *loc,
 	      rvalue *rvalue);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index e28e5f67c..47983e06c 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -5072,6 +5072,26 @@ recording::block::end_with_jump (recording::location *loc,
   return result;
 }
 
+/* Create a recording::end_with_indirect_jump 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_indirect_jump.  */
+
+recording::statement *
+recording::block::end_with_indirect_jump (location *loc,
+					  rvalue *target_address,
+					  int num_destinations,
+					  block **destinations)
+{
+  statement *result = new indirect_jump (this, loc, target_address,
+					 num_destinations, destinations);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+  return result;
+}
+
 /* Create a recording::end_with_return instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
@@ -5197,6 +5217,20 @@ recording::block::get_loc () const
     return NULL;
 }
 
+/* Create a recording::get_address 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_get_address.  */
+
+recording::rvalue *
+recording::block::get_address (location *loc)
+{
+  recording::rvalue *result = new get_block_address (m_ctxt, loc, this);
+  m_ctxt->record (result);
+  return result;
+}
+
 /* Get the first statement within a block, if any.  */
 
 recording::statement *
@@ -7332,6 +7366,45 @@ recording::get_address_of_lvalue::write_reproducer (reproducer &r)
 	   r.get_identifier (m_loc));
 }
 
+/* The implementation of class gcc::jit::recording::get_block_address.  */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::get_block_address.  */
+
+void
+recording::get_block_address::replay_into (replayer *r)
+{
+  set_playback_obj (
+    m_block->playback_block ()->
+      get_address (playback_location (r, m_loc)));
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   getting the address of a block.  */
+
+recording::string *
+recording::get_block_address::make_debug_string ()
+{
+  return string::from_printf (m_ctxt,
+			      "&&%s",
+			      m_block->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   get_block_address.  */
+
+void
+recording::get_block_address::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "address_of");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_block_get_address (%s,\n"
+	   "				   %s);\n",
+	   id,
+	   r.get_identifier (m_block),
+	   r.get_identifier (m_loc));
+}
+
 /* The implementation of class gcc::jit::recording::function_pointer.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -7765,6 +7838,90 @@ recording::jump::write_reproducer (reproducer &r)
 	   r.get_identifier (m_target));
 }
 
+/* The implementation of class gcc::jit::recording::indirect_jump.  */
+
+/* gcc::jit::recording::indirect_jump's constructor.  */
+
+recording::indirect_jump::indirect_jump (block *b,
+					 location *loc,
+					 rvalue *target_address,
+					 int num_destinations,
+					 block **destinations)
+: statement (b, loc),
+  m_target_address (target_address)
+{
+  m_destinations.reserve_exact (num_destinations);
+  for (int i = 0; i < num_destinations; i++)
+    m_destinations.quick_push (destinations[i]);
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::indirect_jump.  */
+
+void
+recording::indirect_jump::replay_into (replayer *r)
+{
+  auto_vec <playback::block *> pb_dests;
+  pb_dests.reserve_exact (m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    pb_dests.quick_push (m_destinations[i]->playback_block ());
+
+  playback_block (get_block ())
+    ->add_indirect_jump (playback_location (r),
+			 m_target_address->playback_rvalue (),
+			 &pb_dests);
+}
+
+/* Override the poisoned default implementation of
+   gcc::jit::recording::statement::get_successor_blocks.  */
+
+vec <recording::block *>
+recording::indirect_jump::get_successor_blocks () const
+{
+  vec <block *> result;
+  result.create (m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    result.quick_push (m_destinations[i]);
+  return result;
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   an indirect jump statement.  */
+
+recording::string *
+recording::indirect_jump::make_debug_string ()
+{
+  return string::from_printf (m_ctxt,
+			      "goto *%s;",
+			      m_target_address->get_debug_string ());
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   indirect_jump statements.  */
+
+void
+recording::indirect_jump::write_reproducer (reproducer &r)
+{
+  r.make_identifier (this, "indirect_jump");
+  const char *dests_id = r.make_tmp_identifier ("destinations_for", this);
+  r.write ("  gcc_jit_block *%s[%i] = {\n",
+	   dests_id,
+	   m_destinations.length ());
+  for (unsigned i = 0; i < m_destinations.length (); i++)
+    r.write ("    %s,\n", r.get_identifier (m_destinations[i]));
+  r.write ("  };\n");
+  r.write ("  gcc_jit_block_end_with_indirect_jump (%s,\n"
+	   "					    %s,\n"
+	   "					    %s,\n"
+	   "					    %i,\n"
+	   "					    %s);\n",
+	   r.get_identifier (get_block ()),
+	   r.get_identifier (get_loc ()),
+	   r.get_identifier_as_rvalue (m_target_address),
+	   m_destinations.length (),
+	   dests_id);
+}
+
 /* The implementation of class gcc::jit::recording::return_.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 2a5f23a05..c500ea659 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -966,7 +966,7 @@ public:
   type *unqualified () final override { return m_other_type; }
 
   void replay_into (replayer *) final override;
-  memento_of_get_aligned *dyn_cast_aligned_type () final override 
+  memento_of_get_aligned *dyn_cast_aligned_type () final override
   {
     return this;
   }
@@ -1682,6 +1682,13 @@ public:
   end_with_jump (location *loc,
 		 block *target);
 
+  rvalue * get_address (location *loc);
+
+  statement * end_with_indirect_jump (location *loc,
+				     rvalue *target_address,
+				     int num_destinations,
+				     block **destinations);
+
   statement *
   end_with_return (location *loc,
 		   rvalue *rvalue);
@@ -2427,6 +2434,32 @@ private:
   lvalue *m_lvalue;
 };
 
+class get_block_address : public rvalue
+{
+public:
+  get_block_address (context *ctxt,
+		 location *loc,
+		 block *b)
+  : rvalue (ctxt, loc, ctxt->get_type (GCC_JIT_TYPE_VOID_PTR)),
+    m_block (b)
+  {}
+
+  void replay_into (replayer *r) final override;
+
+  void visit_children (rvalue_visitor *) final override {}
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+  enum precedence get_precedence () const final override
+  {
+    return PRECEDENCE_UNARY;
+  }
+
+private:
+  block *m_block;
+};
+
 class function_pointer : public rvalue
 {
 public:
@@ -2650,6 +2683,28 @@ private:
   block *m_target;
 };
 
+class indirect_jump : public statement
+{
+public:
+  indirect_jump (block *b,
+		 location *loc,
+		 rvalue *target_address,
+		 int num_destinations,
+		 block **destinations);
+
+  void replay_into (replayer *r) final override;
+
+  vec <block *> get_successor_blocks () const final override;
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+
+private:
+  rvalue *m_target_address;
+  auto_vec <block *> m_destinations;
+};
+
 class return_ : public statement
 {
 public:
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 5a874fc48..4fac8c56b 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -2874,6 +2874,23 @@ gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
   return (gcc_jit_rvalue *)lvalue->get_address (loc);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::get_address method in
+   jit-recording.cc.  */
+
+gcc_jit_rvalue *
+gcc_jit_block_get_address (gcc_jit_block *block,
+			   gcc_jit_location *loc)
+{
+  RETURN_NULL_IF_FAIL (block, NULL, loc, "NULL block");
+  JIT_LOG_FUNC (block->get_context ()->get_logger ());
+  /* LOC can be NULL.  */
+
+  return (gcc_jit_rvalue *)block->get_address (loc);
+}
+
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
@@ -3258,6 +3275,52 @@ gcc_jit_block_end_with_jump (gcc_jit_block *block,
   block->end_with_jump (loc, target);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::block::end_with_indirect_jump method in
+   jit-recording.cc.  */
+
+void
+gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
+				      gcc_jit_location *loc,
+				      gcc_jit_rvalue *target_address,
+				      int num_destinations,
+				      gcc_jit_block **destinations)
+{
+  RETURN_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_IF_FAIL (target_address, ctxt, loc, "NULL target_address");
+  RETURN_IF_FAIL_PRINTF1 (
+    target_address->get_type ()->is_pointer (),
+    ctxt, loc,
+    "target_address: %s is not of a pointer type",
+    target_address->get_debug_string ());
+  RETURN_IF_FAIL (num_destinations > 0, ctxt, loc, "num_destinations <= 0");
+  RETURN_IF_FAIL (destinations, ctxt, loc, "NULL destinations");
+
+  for (int i = 0; i < num_destinations; i++)
+    {
+      RETURN_IF_FAIL_PRINTF1 (destinations[i], ctxt, loc,
+			      "NULL destination block at index %d", i);
+      RETURN_IF_FAIL_PRINTF4 (
+	block->get_function () == destinations[i]->get_function (),
+	ctxt, loc,
+	"destination block %d (%s) is not in the same function"
+	" (function %s) as source block %s",
+	i,
+	destinations[i]->get_debug_string (),
+	destinations[i]->get_function ()->get_debug_string (),
+	block->get_debug_string ());
+    }
+
+  block->end_with_indirect_jump (loc, target_address,
+				 num_destinations,
+				 (gcc::jit::recording::block **)destinations);
+}
+
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 2247c99de..9d00ba33a 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1455,16 +1455,31 @@ extern gcc_jit_rvalue *
 gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
 			    gcc_jit_location *loc);
 
+#define LIBGCCJIT_HAVE_gcc_jit_block_get_address
+
+/* Taking the address of a block; analogous to:
+     &&block
+   in C.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_39; you can test for its
+   presence using
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_get_address
+*/
+extern gcc_jit_rvalue *
+gcc_jit_block_get_address (gcc_jit_block *block,
+			   gcc_jit_location *loc);
+
 #define LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model
 
 /* Set the thread-local storage model of a global variable
 
    This API entrypoint was added in LIBGCCJIT_ABI_17; you can test for its
    presence using
-     #ifdef LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model  */
+     #ifdef LIBGCCJIT_HAVE_gcc_jit_lvalue_set_tls_model
+*/
 extern void
 gcc_jit_lvalue_set_tls_model (gcc_jit_lvalue *lvalue,
-			    enum gcc_jit_tls_model model);
+			      enum gcc_jit_tls_model model);
 
 #define LIBGCCJIT_HAVE_gcc_jit_lvalue_set_link_section
 
@@ -1592,6 +1607,26 @@ gcc_jit_block_end_with_jump (gcc_jit_block *block,
 			     gcc_jit_location *loc,
 			     gcc_jit_block *target);
 
+#define LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump
+
+/* Terminate a block by adding an indirect jump to target_address.
+
+   All possible indirect jump targets must be constrained to a fixed array.
+
+   This is roughly equivalent to this C code:
+
+      goto *target_address;
+
+   This API entrypoint was added in LIBGCCJIT_ABI_39; you can test for its
+   presence using
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_block_end_with_indirect_jump.  */
+extern void
+gcc_jit_block_end_with_indirect_jump (gcc_jit_block *block,
+				      gcc_jit_location *loc,
+				      gcc_jit_rvalue *target_address,
+				      int num_destinations,
+				      gcc_jit_block **destinations);
+
 /* Terminate a block by adding evaluation of an rvalue, returning the value.
 
    This is roughly equivalent to this C code:
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 78b6be9ff..9412700a2 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -349,3 +349,9 @@ LIBGCCJIT_ABI_38 {
   global:
     gcc_jit_type_is_floating_point;
 } LIBGCCJIT_ABI_37;
+
+LIBGCCJIT_ABI_39 {
+  global:
+    gcc_jit_block_get_address;
+    gcc_jit_block_end_with_indirect_jump;
+} LIBGCCJIT_ABI_38;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index e8f8463e8..5bf18a874 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -448,6 +448,13 @@
 #undef create_code
 #undef verify_code
 
+/* test-indirect-jump.c */
+#define create_code create_code_indirect_jump
+#define verify_code verify_code_indirect_jump
+#include "test-indirect-jump.c"
+#undef create_code
+#undef verify_code
+
 /* test-target-info.c: This can't be in the testcases array as it
    is target-specific.  */
 
@@ -688,6 +695,9 @@ const struct testcase testcases[] = {
   {"vector_perm",
    create_code_vector_perm,
    verify_code_vector_perm},
+  {"indirect_jump",
+   create_code_indirect_jump,
+   verify_code_indirect_jump},
 };
 
 const int num_testcases = (sizeof (testcases) / sizeof (testcases[0]));
diff --git a/gcc/testsuite/jit.dg/test-indirect-jump.c b/gcc/testsuite/jit.dg/test-indirect-jump.c
new file mode 100644
index 000000000..3d407f7ea
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-indirect-jump.c
@@ -0,0 +1,108 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+     int
+     test_indirect_jump (int idx)
+     {
+       void *targets[3];
+       targets[0] = &&b_zero;
+       targets[1] = &&b_one;
+       targets[2] = &&b_two;
+       goto *targets[idx];
+
+     b_zero:
+       return 10;
+     b_one:
+       return 20;
+     b_two:
+       return 30;
+     }
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *void_ptr_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+  gcc_jit_type *array_type =
+    gcc_jit_context_new_array_type (ctxt, NULL, void_ptr_type, 3);
+
+  gcc_jit_param *param_idx =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "idx");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "test_indirect_jump",
+				  1, &param_idx,
+				  0);
+
+  gcc_jit_block *b_entry = gcc_jit_function_new_block (func, "entry");
+  gcc_jit_block *b_zero = gcc_jit_function_new_block (func, "b_zero");
+  gcc_jit_block *b_one = gcc_jit_function_new_block (func, "b_one");
+  gcc_jit_block *b_two = gcc_jit_function_new_block (func, "b_two");
+
+  gcc_jit_lvalue *targets =
+    gcc_jit_function_new_local (func, NULL, array_type, "targets");
+
+  gcc_jit_block *blocks[3] = { b_zero, b_one, b_two };
+  for (int i = 0; i < 3; i++)
+    {
+      gcc_jit_rvalue *idx_val =
+	gcc_jit_context_new_rvalue_from_int (ctxt, int_type, i);
+      gcc_jit_lvalue *elem =
+	gcc_jit_context_new_array_access (ctxt, NULL,
+					  gcc_jit_lvalue_as_rvalue (targets),
+					  idx_val);
+      gcc_jit_rvalue *blk_addr =
+	gcc_jit_block_get_address (blocks[i], NULL);
+      gcc_jit_block_add_assignment (b_entry, NULL, elem, blk_addr);
+    }
+
+  /* void *target_addr = targets[idx]; */
+  gcc_jit_lvalue *selected_elem =
+    gcc_jit_context_new_array_access (ctxt, NULL,
+				      gcc_jit_lvalue_as_rvalue (targets),
+				      gcc_jit_param_as_rvalue (param_idx));
+
+  /* goto *targets[idx]; */
+  gcc_jit_block_end_with_indirect_jump (
+    b_entry, NULL,
+    gcc_jit_lvalue_as_rvalue (selected_elem),
+    3, blocks);
+
+  /* b_zero: return 10; */
+  gcc_jit_block_end_with_return (
+    b_zero, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 10));
+
+  /* b_one: return 20; */
+  gcc_jit_block_end_with_return (
+    b_one, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 20));
+
+  /* b_two: return 30; */
+  gcc_jit_block_end_with_return (
+    b_two, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 30));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*fn_type) (int);
+  CHECK_NON_NULL (result);
+
+  fn_type test_indirect_jump =
+    (fn_type)gcc_jit_result_get_code (result, "test_indirect_jump");
+  CHECK_NON_NULL (test_indirect_jump);
+
+  CHECK_VALUE (test_indirect_jump (0), 10);
+  CHECK_VALUE (test_indirect_jump (1), 20);
+  CHECK_VALUE (test_indirect_jump (2), 30);
+}
-- 
2.56.0

Reply via email to