Normally libgccjit verifies that all blocks within a function are reachable by some path from the entry block, issuing a hard error if it encounters an unreachable block, which is useful for the common case.
However, some bytecode interpreters can generate unreachable bytecodes, so it's useful when JIT-compiling them to have a way to turn off this verification. This patch provides a way to turn off this check, by adding a new boolean option. However, adding it to "enum gcc_jit_bool_option" is unsatisfactory, since there would be no way to statically identify client binaries using the new option. Hence this patch adds a new entrypoint for the option: gcc_jit_context_set_bool_allow_unreachable_blocks within a new ABI tag, LIBGCCJIT_ABI_2. It adds a new "inner_bool_option" enum internally, for such new options, which is not exposed as part of the public API. Tested via "make check-jit"; jit.sum goes from 8039 to 8054 passes. Committed to trunk as r225206. gcc/jit/ChangeLog: PR jit/66546 * docs/cp/topics/contexts.rst (gccjit::context::set_bool_allow_unreachable_blocks): New. * docs/topics/compatibility.rst (LIBGCCJIT_ABI_2): New. * docs/topics/contexts.rst (Options): Add notes discussing the transition from enums to entrypoints for new options. (gcc_jit_context_set_bool_allow_unreachable_blocks): New. * docs/_build/texinfo/libgccjit.texi: Regenerate. * jit-common.h (gcc::jit::inner_bool_option): New enum. * jit-recording.c: Within namespace gcc::jit... (recording::context::context): Handle m_inner_bool_options. (recording::context::set_inner_bool_option): New. (inner_bool_option_reproducer_strings): New. (recording::context::log_all_options): Log the "inner" bool options. (recording::context::log_inner_bool_option): New. (recording::context::dump_reproducer_to_file): Write initializers for "inner" bool options. (recording::function::validate): Don't check for block reachability if INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS is set. * jit-recording.h: Within namespace gcc::jit... (recording::context::set_inner_bool_option): New. (recording::context::get_inner_bool_option): New. (recording::context::log_inner_bool_option): New. (recording::context::m_inner_bool_options): New. * libgccjit++.h (gccjit::context::set_bool_allow_unreachable_blocks): New. * libgccjit.c (gcc_jit_context_set_bool_allow_unreachable_blocks): New. * libgccjit.h: Add note about options present in the initial release of libgccjit. (gcc_jit_context_set_bool_allow_unreachable_blocks): New API entrypoint. (LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks): New macro. * libgccjit.map (LIBGCCJIT_ABI_2): New, containing... (gcc_jit_context_set_bool_allow_unreachable_blocks): ...this new entrypoint. gcc/testsuite/ChangeLog: PR jit/66546 * jit.dg/all-non-failing-tests.h: Add note about test-validly-unreachable-block.c. * jit.dg/test-validly-unreachable-block.c: New file. --- gcc/jit/docs/cp/topics/contexts.rst | 17 ++++++++ gcc/jit/docs/topics/compatibility.rst | 7 +++ gcc/jit/docs/topics/contexts.rst | 29 +++++++++++- gcc/jit/jit-common.h | 10 +++++ gcc/jit/jit-recording.c | 42 +++++++++++++++++- gcc/jit/jit-recording.h | 12 +++++ gcc/jit/libgccjit++.h | 8 ++++ gcc/jit/libgccjit.c | 17 ++++++++ gcc/jit/libgccjit.h | 28 ++++++++++++ gcc/jit/libgccjit.map | 6 +++ gcc/testsuite/jit.dg/all-non-failing-tests.h | 4 ++ .../jit.dg/test-validly-unreachable-block.c | 51 ++++++++++++++++++++++ 12 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-validly-unreachable-block.c diff --git a/gcc/jit/docs/cp/topics/contexts.rst b/gcc/jit/docs/cp/topics/contexts.rst index b26a29d..162e4ae 100644 --- a/gcc/jit/docs/cp/topics/contexts.rst +++ b/gcc/jit/docs/cp/topics/contexts.rst @@ -184,6 +184,23 @@ Boolean options :c:func:`gcc_jit_context_set_bool_option`; the options have the same meaning. +.. function:: void \ + gccjit::context::set_bool_allow_unreachable_blocks (int bool_value) + + By default, libgccjit will issue an error about unreachable blocks + within a function. + + This entrypoint can be used to disable that error; it is a thin wrapper + around the C API + :c:func:`gcc_jit_context_set_bool_allow_unreachable_blocks`. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_2`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks + Integer options *************** diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index dff1d0b..91bbb05 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -88,3 +88,10 @@ continue to work, with this being handled transparently by the linker ------------------- ``LIBGCCJIT_ABI_1`` covers the addition of :func:`gcc_jit_context_add_command_line_option` + +.. _LIBGCCJIT_ABI_2: + +``LIBGCCJIT_ABI_2`` +------------------- +``LIBGCCJIT_ABI_2`` covers the addition of +:func:`gcc_jit_context_set_bool_allow_unreachable_blocks` diff --git a/gcc/jit/docs/topics/contexts.rst b/gcc/jit/docs/topics/contexts.rst index 401cbf1..cca4bdc 100644 --- a/gcc/jit/docs/topics/contexts.rst +++ b/gcc/jit/docs/topics/contexts.rst @@ -293,6 +293,15 @@ future activies on a context to the given `FILE *`. Options ------- +Options present in the initial release of libgccjit were handled using +enums, whereas those added subsequently have their own per-option API +entrypoints. + +Adding entrypoints for each new option means that client code that use +the new options can be identified directly from binary metadata, which +would not be possible if we instead extended the various +``enum gcc_jit_*_option``. + String Options ************** @@ -307,7 +316,7 @@ String Options The parameter ``value`` can be NULL. If non-NULL, it is copied, so the buffer does not need to outlive the call. - There is currently just one string option: + There is just one string option specified this way: .. macro:: GCC_JIT_STR_OPTION_PROGNAME @@ -444,6 +453,22 @@ Boolean options If true, the :type:`gcc_jit_context` will not clean up intermediate files written to the filesystem, and will display their location on stderr. +.. function:: void \ + gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt, \ + int bool_value) + + By default, libgccjit will issue an error about unreachable blocks + within a function. + + This entrypoint can be used to disable that error. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_2`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks + Integer options *************** @@ -455,7 +480,7 @@ Integer options .. type:: enum gcc_jit_int_option - There is currently just one integer option: + There is just one integer option specified this way: .. macro:: GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index e5e1a1e..edf8d2c 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -185,6 +185,16 @@ private: FILE *m_file; }; +/* A hidden enum of boolean options that are only exposed via API + entrypoints, rather than via gcc_jit_context_set_bool_option. */ + +enum inner_bool_option +{ + INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS, + + NUM_INNER_BOOL_OPTIONS +}; + } // namespace gcc::jit } // namespace gcc diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 3d7d571..ad13aaa 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -489,6 +489,9 @@ recording::context::context (context *parent_ctxt) memcpy (m_bool_options, parent_ctxt->m_bool_options, sizeof (m_bool_options)); + memcpy (m_inner_bool_options, + parent_ctxt->m_inner_bool_options, + sizeof (m_inner_bool_options)); set_logger (parent_ctxt->get_logger ()); } else @@ -496,6 +499,7 @@ recording::context::context (context *parent_ctxt) memset (m_str_options, 0, sizeof (m_str_options)); memset (m_int_options, 0, sizeof (m_int_options)); memset (m_bool_options, 0, sizeof (m_bool_options)); + memset (m_inner_bool_options, 0, sizeof (m_inner_bool_options)); } memset (m_basic_types, 0, sizeof (m_basic_types)); @@ -1141,6 +1145,16 @@ recording::context::set_bool_option (enum gcc_jit_bool_option opt, log_bool_option (opt); } +void +recording::context::set_inner_bool_option (enum inner_bool_option inner_opt, + int value) +{ + gcc_assert (inner_opt >= 0 && inner_opt < NUM_INNER_BOOL_OPTIONS); + m_inner_bool_options[inner_opt] = value ? true : false; + log_inner_bool_option (inner_opt); +} + + /* Add the given optname to this context's list of extra options. Implements the post-error-checking part of @@ -1418,6 +1432,10 @@ static const char * const "GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES" }; +static const char * const + inner_bool_option_reproducer_strings[NUM_INNER_BOOL_OPTIONS] = { + "gcc_jit_context_set_bool_allow_unreachable_blocks" +}; /* Write the current value of all options to the log file (if any). */ @@ -1437,6 +1455,8 @@ recording::context::log_all_options () const for (opt_idx = 0; opt_idx < GCC_JIT_NUM_BOOL_OPTIONS; opt_idx++) log_bool_option ((enum gcc_jit_bool_option)opt_idx); + for (opt_idx = 0; opt_idx < NUM_INNER_BOOL_OPTIONS; opt_idx++) + log_inner_bool_option ((enum inner_bool_option)opt_idx); } /* Write the current value of the given string option to the @@ -1484,6 +1504,19 @@ recording::context::log_bool_option (enum gcc_jit_bool_option opt) const m_bool_options[opt] ? "true" : "false"); } +/* Write the current value of the given "inner" bool option to the + log file (if any). */ + +void +recording::context::log_inner_bool_option (enum inner_bool_option opt) const +{ + gcc_assert (opt < NUM_INNER_BOOL_OPTIONS); + if (get_logger ()) + log ("%s: %s", + inner_bool_option_reproducer_strings[opt], + m_inner_bool_options[opt] ? "true" : "false"); +} + /* Write C source code to PATH that attempts to replay the API calls made to this context (and its parents), for use in minimizing test cases for libgccjit. @@ -1623,6 +1656,11 @@ recording::context::dump_reproducer_to_file (const char *path) r.get_identifier (contexts[ctxt_idx]), bool_option_reproducer_strings[opt_idx], m_bool_options[opt_idx]); + for (int opt_idx = 0; opt_idx < NUM_INNER_BOOL_OPTIONS; opt_idx++) + r.write (" %s (%s, %i);\n", + inner_bool_option_reproducer_strings[opt_idx], + r.get_identifier (contexts[ctxt_idx]), + m_inner_bool_options[opt_idx]); if (!m_command_line_options.is_empty ()) { @@ -3452,7 +3490,9 @@ recording::function::validate () } /* Check that all blocks are reachable. */ - if (m_blocks.length () > 0 && 0 == num_invalid_blocks) + if (!m_ctxt->get_inner_bool_option + (INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS) + && m_blocks.length () > 0 && 0 == num_invalid_blocks) { /* Iteratively walk the graph of blocks, marking their "m_is_reachable" flag, starting at the initial block. */ diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 8cd2289..a9bcbb5 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -196,6 +196,10 @@ public: int value); void + set_inner_bool_option (enum inner_bool_option inner_opt, + int value); + + void add_command_line_option (const char *optname); void @@ -223,6 +227,12 @@ public: return m_bool_options[opt]; } + int + get_inner_bool_option (enum inner_bool_option opt) const + { + return m_inner_bool_options[opt]; + } + result * compile (); @@ -266,6 +276,7 @@ private: void log_str_option (enum gcc_jit_str_option opt) const; void log_int_option (enum gcc_jit_int_option opt) const; void log_bool_option (enum gcc_jit_bool_option opt) const; + void log_inner_bool_option (enum inner_bool_option opt) const; void validate (); @@ -287,6 +298,7 @@ private: char *m_str_options[GCC_JIT_NUM_STR_OPTIONS]; int m_int_options[GCC_JIT_NUM_INT_OPTIONS]; bool m_bool_options[GCC_JIT_NUM_BOOL_OPTIONS]; + bool m_inner_bool_options[NUM_INNER_BOOL_OPTIONS]; auto_vec <char *> m_command_line_options; /* Dumpfiles that were requested via gcc_jit_context_enable_dump. */ diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h index a5353ca..cbdc96f 100644 --- a/gcc/jit/libgccjit++.h +++ b/gcc/jit/libgccjit++.h @@ -120,6 +120,8 @@ namespace gccjit void set_bool_option (enum gcc_jit_bool_option opt, int value); + void set_bool_allow_unreachable_blocks (int bool_value); + void add_command_line_option (const char *optname); location @@ -602,7 +604,13 @@ context::set_bool_option (enum gcc_jit_bool_option opt, int value) { gcc_jit_context_set_bool_option (m_inner_ctxt, opt, value); +} +inline void +context::set_bool_allow_unreachable_blocks (int bool_value) +{ + gcc_jit_context_set_bool_allow_unreachable_blocks (m_inner_ctxt, + bool_value); } inline void diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index 44791ab..7e0bfa6 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -2185,6 +2185,23 @@ gcc_jit_context_set_bool_option (gcc_jit_context *ctxt, /* Public entrypoint. See description in libgccjit.h. After error-checking, the real work is done by the + gcc::jit::recording::context::set_inner_bool_option method in + jit-recording.c. */ + +void +gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt, + int bool_value) +{ + RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context"); + JIT_LOG_FUNC (ctxt->get_logger ()); + ctxt->set_inner_bool_option ( + gcc::jit::INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS, + bool_value); +} + +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the gcc::jit::recording::context::add_command_line_option method in jit-recording.c. */ diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index 2c1a99a..7085b41 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -140,6 +140,9 @@ gcc_jit_context_acquire (void); extern void gcc_jit_context_release (gcc_jit_context *ctxt); +/* Options present in the initial release of libgccjit. + These were handled using enums. */ + /* Options taking string values. */ enum gcc_jit_str_option { @@ -243,6 +246,31 @@ gcc_jit_context_set_bool_option (gcc_jit_context *ctxt, enum gcc_jit_bool_option opt, int value); +/* Options added after the initial release of libgccjit. + These are handled by providing an entrypoint per option, + rather than by extending the enum gcc_jit_*_option, + so that client code that use these new options can be identified + from binary metadata. */ + +/* By default, libgccjit will issue an error about unreachable blocks + within a function. + + This option can be used to disable that error. + + This entrypoint was added in LIBGCCJIT_ABI_2; you can test for + its presence using + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks +*/ + +extern void +gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt, + int bool_value); + +/* Pre-canned feature macro to indicate the presence of + gcc_jit_context_set_bool_allow_unreachable_blocks. This can be + tested for with #ifdef. */ +#define LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks + /* Add an arbitrary gcc command-line option to the context. The context takes a copy of the string, so the (const char *) optname is not needed anymore after the call diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index a42bc74..b55df1e 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -114,3 +114,9 @@ LIBGCCJIT_ABI_1 { global: gcc_jit_context_add_command_line_option; } LIBGCCJIT_ABI_0; + +# Add support for disabling the check for unreachable blocks (PR jit/66546). +LIBGCCJIT_ABI_2 { + global: + gcc_jit_context_set_bool_allow_unreachable_blocks; +} LIBGCCJIT_ABI_1; diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h index 8584555..7034c77 100644 --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h @@ -196,6 +196,10 @@ #undef create_code #undef verify_code +/* test-validly-unreachable-block.c: We don't use this one, since the use + of gcc_jit_context_set_bool_allow_unreachable_blocks affects the whole + context. */ + /* test-volatile.c */ #define create_code create_code_volatile #define verify_code verify_code_volatile diff --git a/gcc/testsuite/jit.dg/test-validly-unreachable-block.c b/gcc/testsuite/jit.dg/test-validly-unreachable-block.c new file mode 100644 index 0000000..2664818 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-validly-unreachable-block.c @@ -0,0 +1,51 @@ +#include <math.h> +#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: + void + test_fn () + { + return; + + return; + } + where the second block is unreachable, but have it + survive validation (PR jit/66546). + */ + gcc_jit_context_set_bool_allow_unreachable_blocks (ctxt, 1); + + gcc_jit_type *void_t = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); + + /* Build the test_fn. */ + gcc_jit_function *test_fn = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + void_t, + "test_fn", + 0, NULL, + 0); + gcc_jit_block *initial = + gcc_jit_function_new_block (test_fn, "a"); + gcc_jit_block *unreachable = + gcc_jit_function_new_block (test_fn, "b"); + + gcc_jit_block_end_with_void_return (initial, NULL); + + gcc_jit_block_end_with_void_return (unreachable, NULL); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + /* Ensure that the "unreachable blocks" validator was ignored. */ + CHECK_NON_NULL (result); +} -- 1.8.5.3