@@ -152,6 +152,53 @@ Debugging
:macro:`GCC_JIT_BOOL_OPTION_DEBUGINFO` to allow stepping through the
code in a debugger.
+.. function:: void\
+ gcc_jit_context_enable_dump (gcc_jit_context *ctxt,\
+ const char *dumpname, \
+ char **out_ptr)
+
+ Enable the dumping of a specific set of internal state from the
+ compilation, capturing the result in-memory as a buffer.
+
+ Parameter "dumpname" corresponds to the equivalent gcc command-line
+ option, without the "-fdump-" prefix.
+ For example, to get the equivalent of :option:`-fdump-tree-vrp1`,
+ supply ``"tree-vrp1"``:
+
+ .. code-block:: c
+
+ static char *dump_vrp1;
+
+ void
+ create_code (gcc_jit_context *ctxt)
+ {
+ gcc_jit_context_enable_dump (ctxt, "tree-vrp1", &dump_vrp1);
+ /* (other API calls omitted for brevity) */
+ }
+
+ The context directly stores the dumpname as a ``(const char *)``, so
+ the passed string must outlive the context.
+
+ :func:`gcc_jit_context_compile` will capture the dump as a
+ dynamically-allocated buffer, writing it to ``*out_ptr``.
+
+ The caller becomes responsible for calling:
+
+ .. code-block:: c
+
+ free (*out_ptr)
+
+ each time that :func:`gcc_jit_context_compile` is called.
+ ``*out_ptr`` will be written to, either with the address of a buffer,
+ or with ``NULL`` if an error occurred.
+
+ .. warning::
+
+ This API entrypoint is likely to be less stable than the others.
+ In particular, both the precise dumpnames, and the format and content
+ of the dumps are subject to change.
+
+ It exists primarily for writing the library's own test suite.
Options
-------
@@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimplify.h"
#include "gcc-driver-name.h"
#include "attribs.h"
+#include "context.h"
#include "jit-common.h"
#include "jit-playback.h"
@@ -1552,6 +1553,26 @@ make_tempdir_path_template ()
return result;
}
+/* A subclass of auto_vec <char *> that frees all of its elements on
+ deletion. */
+
+class auto_argvec : public auto_vec <char *>
+{
+ public:
+ ~auto_argvec ();
+};
+
+/* auto_argvec's dtor, freeing all contained strings, automatically
+ chaining up to ~auto_vec <char *>, which frees the internal buffer. */
+
+auto_argvec::~auto_argvec ()
+{
+ int i;
+ char *str;
+ FOR_EACH_VEC_ELT (*this, i, str)
+ free (str);
+}
+
/* Compile a playback::context:
- Use the context's options to cconstruct command-line options, and
@@ -1594,14 +1615,25 @@ compile ()
if (!ctxt_progname)
ctxt_progname = "libgccjit.so";
- auto_vec <const char *> fake_args;
- make_fake_args (&fake_args, ctxt_progname);
+ auto_vec <recording::requested_dump> requested_dumps;
+ m_recording_ctxt->get_all_requested_dumps (&requested_dumps);
+
+ auto_argvec fake_args;
+ make_fake_args (&fake_args, ctxt_progname, &requested_dumps);
if (errors_occurred ())
return NULL;
+ /* This runs the compiler. */
toplev toplev (false);
toplev.main (fake_args.length (),
const_cast <char **> (fake_args.address ()));
+
+ /* Extracting dumps makes use of the gcc::dump_manager, hence we
+ need to do it between toplev::main (which creates the dump manager)
+ and toplev::finalize (which deletes it). */
+ extract_any_requested_dumps (&requested_dumps);
+
+ /* Clean up the compiler. */
toplev.finalize ();
active_playback_ctxt = NULL;
@@ -1645,10 +1677,12 @@ compile ()
void
playback::context::
-make_fake_args (auto_vec <const char *> *argvec,
- const char *ctxt_progname)
+make_fake_args (vec <char *> *argvec,
+ const char *ctxt_progname,
+ vec <recording::requested_dump> *requested_dumps)
{
-#define ADD_ARG(arg) argvec->safe_push (arg)
+#define ADD_ARG(arg) argvec->safe_push (xstrdup (arg))
+#define ADD_ARG_TAKE_OWNERSHIP(arg) argvec->safe_push (arg)
ADD_ARG (ctxt_progname);
ADD_ARG (m_path_c_file);
@@ -1707,7 +1741,104 @@ make_fake_args (auto_vec <const char *> *argvec,
ADD_ARG ("-fdump-rtl-all");
ADD_ARG ("-fdump-ipa-all");
}
+
+ /* Add "-fdump-" options for any calls to
+ gcc_jit_context_enable_dump. */
+ {
+ int i;
+ recording::requested_dump *d;
+ FOR_EACH_VEC_ELT (*requested_dumps, i, d)
+ {
+ char *arg = concat ("-fdump-", d->m_dumpname, NULL);
+ ADD_ARG_TAKE_OWNERSHIP (arg);
+ }
+ }
+
#undef ADD_ARG
+#undef ADD_ARG_TAKE_OWNERSHIP
+}
+
+/* The second half of the implementation of gcc_jit_context_enable_dump.
+ Iterate through the requested dumps, reading the underlying files
+ into heap-allocated buffers, writing pointers to the buffers into
+ the char ** pointers provided by client code.
+ Client code is responsible for calling free on the results. */
+
+void
+playback::context::
+extract_any_requested_dumps (vec <recording::requested_dump> *requested_dumps)
+{
+ int i;
+ recording::requested_dump *d;
+ FOR_EACH_VEC_ELT (*requested_dumps, i, d)
+ {
+ dump_file_info *dfi;
+ char *filename;
+ char *content;
+
+ dfi = g->get_dumps ()->get_dump_file_info_by_switch (d->m_dumpname);
+ if (!dfi)
+ {
+ add_error (NULL, "unrecognized dump: %s", d->m_dumpname);
+ continue;
+ }
+
+ filename = g->get_dumps ()->get_dump_file_name (dfi);
+ content = read_dump_file (filename);
+ *(d->m_out_ptr) = content;
+ free (filename);
+ }
+}
+
+/* Helper function for playback::context::extract_any_requested_dumps
+ (itself for use in implementation of gcc_jit_context_enable_dump).
+
+ Attempt to read the complete file at the given path, returning the
+ bytes found there as a buffer.
+ The caller is responsible for calling free on the result.
+ Errors will be reported on the context, and lead to NULL being
+ returned; an out-of-memory error will terminate the process. */
+
+char *
+playback::context::read_dump_file (const char *path)
+{
+ char *result = NULL;
+ size_t total_sz = 0;
+ char buf[4096];
+ size_t sz;
+ FILE *f_in;
+
+ f_in = fopen (path, "r");
+ if (!f_in)
+ {
+ add_error (NULL, "unable to open %s for reading", path);
+ return NULL;
+ }
+
+ while ( (sz = fread (buf, 1, sizeof (buf), f_in)) )
+ {
+ size_t old_total_sz = total_sz;
+ total_sz += sz;
+ result = reinterpret_cast <char *> (xrealloc (result, total_sz + 1));
+ memcpy (result + old_total_sz, buf, sz);
+ }
+
+ if (!feof (f_in))
+ {
+ add_error (NULL, "error reading from %s", path);
+ free (result);
+ return NULL;
+ }
+
+ fclose (f_in);
+
+ if (result)
+ {
+ result[total_sz] = '\0';
+ return result;
+ }
+ else
+ return xstrdup ("");
}
/* Part of playback::context::compile ().
@@ -236,8 +236,16 @@ private:
/* Functions for implementing "compile". */
void
- make_fake_args (auto_vec <const char *> *argvec,
- const char *ctxt_progname);
+ make_fake_args (vec <char *> *argvec,
+ const char *ctxt_progname,
+ vec <recording::requested_dump> *requested_dumps);
+
+ void
+ extract_any_requested_dumps
+ (vec <recording::requested_dump> *requested_dumps);
+
+ char *
+ read_dump_file (const char *path);
void
convert_to_dso (const char *ctxt_progname);
@@ -868,6 +868,27 @@ recording::context::set_bool_option (enum gcc_jit_bool_option opt,
m_bool_options[opt] = value ? true : false;
}
+/* Add the given dumpname/out_ptr pair to this context's list of requested
+ dumps.
+
+ Implements the post-error-checking part of
+ gcc_jit_context_enable_dump. */
+
+void
+recording::context::enable_dump (const char *dumpname,
+ char **out_ptr)
+{
+ requested_dump d;
+ gcc_assert (dumpname);
+ gcc_assert (out_ptr);
+
+ d.m_dumpname = dumpname;
+ d.m_out_ptr = out_ptr;
+ *out_ptr = NULL;
+ m_requested_dumps.safe_push (d);
+}
+
+
/* This mutex guards gcc::jit::recording::context::compile, so that only
one thread can be accessing the bulk of GCC's state at once. */
@@ -1026,6 +1047,19 @@ recording::context::dump_to_file (const char *path, bool update_locations)
}
}
+/* Copy the requested dumps within this context and all ancestors into
+ OUT. */
+
+void
+recording::context::get_all_requested_dumps (vec <recording::requested_dump> *out)
+{
+ if (m_parent_ctxt)
+ m_parent_ctxt->get_all_requested_dumps (out);
+
+ out->reserve (m_requested_dumps.length ());
+ out->splice (m_requested_dumps);
+}
+
/* This is a pre-compilation check for the context (and any parents).
Detect errors within the context, adding errors if any are found. */
@@ -45,6 +45,13 @@ playback_string (string *str);
playback::block *
playback_block (block *b);
+/* A recording of a call to gcc_jit_context_enable_dump. */
+struct requested_dump
+{
+ const char *m_dumpname;
+ char **m_out_ptr;
+};
+
/* A JIT-compilation context. */
class context
{
@@ -191,6 +198,10 @@ public:
set_bool_option (enum gcc_jit_bool_option opt,
int value);
+ void
+ enable_dump (const char *dumpname,
+ char **out_ptr);
+
const char *
get_str_option (enum gcc_jit_str_option opt) const
{
@@ -235,6 +246,9 @@ public:
void dump_to_file (const char *path, bool update_locations);
+ void
+ get_all_requested_dumps (vec <recording::requested_dump> *out);
+
private:
void validate ();
@@ -250,6 +264,9 @@ private:
int m_int_options[GCC_JIT_NUM_INT_OPTIONS];
bool m_bool_options[GCC_JIT_NUM_BOOL_OPTIONS];
+ /* Dumpfiles that were requested via gcc_jit_context_enable_dump. */
+ auto_vec<requested_dump> m_requested_dumps;
+
/* Recorded API usage. */
auto_vec<memento *> m_mementos;
@@ -2004,6 +2004,24 @@ 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::enable_dump method in
+ jit-recording.c. */
+
+void
+gcc_jit_context_enable_dump (gcc_jit_context *ctxt,
+ const char *dumpname,
+ char **out_ptr)
+{
+ RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+ RETURN_IF_FAIL (dumpname, ctxt, NULL, "NULL dumpname");
+ RETURN_IF_FAIL (out_ptr, ctxt, NULL, "NULL out_ptr");
+
+ ctxt->enable_dump (dumpname, out_ptr);
+}
+
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
gcc::jit::recording::context::compile method in
jit-recording.c. */
@@ -985,6 +985,40 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block,
extern gcc_jit_context *
gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt);
+/**********************************************************************
+ Implementation support.
+ **********************************************************************/
+
+/* Enable the dumping of a specific set of internal state from the
+ compilation, capturing the result in-memory as a buffer.
+
+ Parameter "dumpname" corresponds to the equivalent gcc command-line
+ option, without the "-fdump-" prefix.
+ For example, to get the equivalent of "-fdump-tree-vrp1", supply
+ "tree-vrp1".
+ The context directly stores the dumpname as a (const char *), so the
+ passed string must outlive the context.
+
+ gcc_jit_context_compile will capture the dump as a
+ dynamically-allocated buffer, writing it to ``*out_ptr``.
+
+ The caller becomes responsible for calling
+ free (*out_ptr)
+ each time that gcc_jit_context_compile is called. *out_ptr will be
+ written to, either with the address of a buffer, or with NULL if an
+ error occurred.
+
+ This API entrypoint is likely to be less stable than the others.
+ In particular, both the precise dumpnames, and the format and content
+ of the dumps are subject to change.
+
+ It exists primarily for writing the library's own test suite. */
+
+extern void
+gcc_jit_context_enable_dump (gcc_jit_context *ctxt,
+ const char *dumpname,
+ char **out_ptr);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -33,6 +33,7 @@
gcc_jit_context_acquire;
gcc_jit_context_compile;
gcc_jit_context_dump_to_file;
+ gcc_jit_context_enable_dump;
gcc_jit_context_get_builtin_function;
gcc_jit_context_get_first_error;
gcc_jit_context_get_type;
@@ -84,6 +84,9 @@ static char test[1024];
#define CHECK_STRING_STARTS_WITH(ACTUAL, EXPECTED_PREFIX) \
check_string_starts_with ((ACTUAL), (EXPECTED_PREFIX));
+#define CHECK_STRING_CONTAINS(ACTUAL, EXPECTED_SUBSTRING) \
+ check_string_contains (#ACTUAL, (ACTUAL), (EXPECTED_SUBSTRING));
+
#define CHECK(COND) \
do { \
if (COND) \
@@ -110,6 +113,11 @@ extern void
check_string_starts_with (const char *actual,
const char *expected_prefix);
+extern void
+check_string_contains (const char *name,
+ const char *actual,
+ const char *expected_substring);
+
/* Implement framework needed for turning the testcase hooks into an
executable. test-combination.c and test-threads.c each combine multiple
testcases into larger testcases, so we have COMBINED_TEST as a way of
@@ -168,6 +176,31 @@ check_string_starts_with (const char *actual,
test, actual, expected_prefix);
}
+void
+check_string_contains (const char *name,
+ const char *actual,
+ const char *expected_substring)
+{
+ if (!actual)
+ {
+ fail ("%s: %s: actual: NULL does not contain expected substring: \"%s\"",
+ test, name, expected_substring);
+ fprintf (stderr, "incorrect value\n");
+ abort ();
+ }
+
+ if (!strstr (actual, expected_substring))
+ {
+ fail ("%s: %s: actual: \"%s\" did not contain expected substring: \"%s\"",
+ test, name, actual, expected_substring);
+ fprintf (stderr, "incorrect value\n");
+ abort ();
+ }
+
+ pass ("%s: %s: found substring: \"%s\"",
+ test, name, expected_substring);
+}
+
static void set_options (gcc_jit_context *ctxt, const char *argv0)
{
/* Set up options. */
new file mode 100644
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+static char *dump;
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ gcc_jit_context_enable_dump (ctxt,
+ "not-a-valid-dump-switch",
+ &dump);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ CHECK_VALUE (result, NULL);
+
+ /* Verify that the correct error message was emitted. */
+ CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+ "unrecognized dump: not-a-valid-dump-switch");
+}
+
@@ -167,6 +167,9 @@ create_test_of_builtin_strcmp (gcc_jit_context *ctxt)
gcc_jit_block_end_with_return (initial, NULL, call);
}
+static char *trig_sincos_dump;
+static char *trig_statistics_dump;
+
static void
create_test_of_builtin_trig (gcc_jit_context *ctxt)
{
@@ -178,6 +181,14 @@ create_test_of_builtin_trig (gcc_jit_context *ctxt)
}
(in theory, optimizable to sin (2 * theta))
*/
+
+ gcc_jit_context_enable_dump (ctxt,
+ "tree-sincos",
+ &trig_sincos_dump);
+ gcc_jit_context_enable_dump (ctxt,
+ "statistics",
+ &trig_statistics_dump);
+
gcc_jit_type *double_t =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
@@ -266,6 +277,22 @@ verify_test_of_builtin_trig (gcc_jit_context *ctxt, gcc_jit_result *result)
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_2 ), 0.0);
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4 * 3.0), -1.0);
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI ), 0.0);
+
+ /* PR jit/64020:
+ The "sincos" pass merges sin/cos calls into the cexpi builtin.
+ Verify that a dump of the "sincos" pass was provided, and that it
+ shows a call to the cexpi builtin on a SSA name of "theta". */
+ CHECK_NON_NULL (trig_sincos_dump);
+ CHECK_STRING_CONTAINS (trig_sincos_dump, " = __builtin_cexpi (theta_");
+ free (trig_sincos_dump);
+
+ /* Similarly, verify that the statistics dump was provided, and that
+ it shows the sincos optimization. */
+ CHECK_NON_NULL (trig_statistics_dump);
+ CHECK_STRING_CONTAINS (
+ trig_statistics_dump,
+ "sincos \"sincos statements inserted\" \"test_of_builtin_trig\" 1");
+ free (trig_statistics_dump);
}
static void
@@ -6,6 +6,8 @@
#include "harness.h"
+static char *dump_vrp1;
+
void
create_code (gcc_jit_context *ctxt, void *user_data)
{
@@ -22,6 +24,8 @@ create_code (gcc_jit_context *ctxt, void *user_data)
}
return sum;
*/
+ gcc_jit_context_enable_dump (ctxt, "tree-vrp1", &dump_vrp1);
+
gcc_jit_type *the_type =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type *return_type = the_type;
@@ -123,4 +127,16 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
int val = loop_test (10);
note ("loop_test returned: %d", val);
CHECK_VALUE (val, 285);
+
+ CHECK_NON_NULL (dump_vrp1);
+ /* PR jit/64166
+ An example of using gcc_jit_context_enable_dump to verify a property
+ of the compile.
+
+ In this case, verify that vrp is able to deduce the
+ bounds of the iteration variable. Specifically, verify that some
+ variable is known to be in the range negative infinity to some
+ expression based on param "n" (actually n-1). */
+ CHECK_STRING_CONTAINS (dump_vrp1, ": [-INF, n_");
+ free (dump_vrp1);
}