>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, ¶m_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, ¶m_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