From 195879c135925a2bc2d53374716dc4b57c79d843 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 9 Jun 2021 18:29:14 -0400 Subject: [PATCH] libgccjit: Add support for bitcasts [PR104071] 2022-04-09 Antoni Boucher gcc/jit/ PR jit/104071 * docs/topics/compatibility.rst (LIBGCCJIT_ABI_21): New ABI tag. * docs/topics/expressions.rst: Add documentation for the function gcc_jit_context_new_bitcast. * jit-playback.cc: New function (new_bitcast). * jit-playback.h: New function (new_bitcast). * jit-recording.cc: New functions (new_bitcast, bitcast::replay_into, bitcast::visit_children, bitcast::make_debug_string, bitcast::write_reproducer). * jit-recording.h: New class (bitcast) and new function (new_bitcast, bitcast::replay_into, bitcast::visit_children, bitcast::make_debug_string, bitcast::write_reproducer, bitcast::get_precedence). * libgccjit.cc: New function (gcc_jit_context_new_bitcast) * libgccjit.h: New function (gcc_jit_context_new_bitcast) * libgccjit.map (LIBGCCJIT_ABI_21): New ABI tag. gcc/testsuite/ PR jit/104071 * jit.dg/all-non-failing-tests.h: Add new test-bitcast. * jit.dg/test-bitcast.c: New test. * jit.dg/test-error-bad-bitcast.c: New test. * jit.dg/test-error-bad-bitcast2.c: New test. gcc/ PR jit/104071 * toplev.cc: Call the new function tree_cc_finalize in toplev::finalize. * tree.cc: New functions (clear_nonstandard_integer_type_cache and tree_cc_finalize) to clear the cache of non-standard integer types to avoid having issues with some optimizations of bitcast where the SSA_NAME will have a size of a cached integer type that should have been invalidated, causing a comparison of integer constant to fail. * tree.h: New function (tree_cc_finalize). --- gcc/jit/docs/topics/compatibility.rst | 9 +++ gcc/jit/docs/topics/expressions.rst | 19 ++++++ gcc/jit/jit-playback.cc | 27 ++++++++ gcc/jit/jit-playback.h | 5 ++ gcc/jit/jit-recording.cc | 66 +++++++++++++++++++ gcc/jit/jit-recording.h | 32 +++++++++ gcc/jit/libgccjit.cc | 22 +++++++ gcc/jit/libgccjit.h | 15 +++++ gcc/jit/libgccjit.map | 9 +++ gcc/testsuite/jit.dg/all-non-failing-tests.h | 10 +++ gcc/testsuite/jit.dg/test-bitcast.c | 60 +++++++++++++++++ gcc/testsuite/jit.dg/test-error-bad-bitcast.c | 62 +++++++++++++++++ .../jit.dg/test-error-bad-bitcast2.c | 62 +++++++++++++++++ gcc/toplev.cc | 1 + gcc/tree.cc | 15 +++++ gcc/tree.h | 1 + 16 files changed, 415 insertions(+) create mode 100644 gcc/testsuite/jit.dg/test-bitcast.c create mode 100644 gcc/testsuite/jit.dg/test-error-bad-bitcast.c create mode 100644 gcc/testsuite/jit.dg/test-error-bad-bitcast2.c diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 16cebe31a10..610d9dc175f 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -302,3 +302,12 @@ thread-local storage model of a variable: section of a variable: * :func:`gcc_jit_lvalue_set_link_section` + +.. _LIBGCCJIT_ABI_21: + +``LIBGCCJIT_ABI_21`` +----------------------- +``LIBGCCJIT_ABI_21`` covers the addition of an API entrypoint to bitcast a +value from one type to another: + + * :func:`gcc_jit_context_new_bitcast` diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst index 791a20398ca..e0b6f426aee 100644 --- a/gcc/jit/docs/topics/expressions.rst +++ b/gcc/jit/docs/topics/expressions.rst @@ -649,6 +649,25 @@ Type-coercion * int <-> bool * P* <-> Q*, for pointer types P and Q +.. function:: gcc_jit_rvalue *\ + gcc_jit_context_new_bitcast (gcc_jit_context *ctxt,\ + gcc_jit_location *loc,\ + gcc_jit_rvalue *rvalue,\ + gcc_jit_type *type) + + Given an rvalue of T, bitcast it to another type, meaning that this will + generate a new rvalue by interpreting the bits of ``rvalue`` to the layout + of ``type``. + + The type of rvalue must be the same size as the size of ``type``. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_21`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast + Lvalues ------- diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc index d1835c79863..d2efae1c163 100644 --- a/gcc/jit/jit-playback.cc +++ b/gcc/jit/jit-playback.cc @@ -1390,6 +1390,33 @@ new_cast (playback::location *loc, return new rvalue (this, t_cast); } +playback::rvalue * +playback::context:: +new_bitcast (location *loc, + rvalue *expr, + type *type_) +{ + tree expr_size = TYPE_SIZE (expr->get_type ()->as_tree ()); + tree type_size = TYPE_SIZE (type_->as_tree ()); + tree t_expr = expr->as_tree (); + tree t_dst_type = type_->as_tree (); + if (expr_size != type_size) + { + active_playback_ctxt->add_error (loc, + "bitcast with types of different sizes"); + fprintf (stderr, "input expression (size: %ld):\n", + tree_to_uhwi (expr_size)); + debug_tree (t_expr); + fprintf (stderr, "requested type (size: %ld):\n", + tree_to_uhwi (type_size)); + debug_tree (t_dst_type); + } + tree t_bitcast = build1 (VIEW_CONVERT_EXPR, t_dst_type, t_expr); + if (loc) + set_tree_location (t_bitcast, loc); + return new rvalue (this, t_bitcast); +} + /* Construct a playback::lvalue instance (wrapping a tree) for an array access. */ diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index c93d7055d43..5b107be9d69 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -180,6 +180,11 @@ public: rvalue *expr, type *type_); + rvalue * + new_bitcast (location *loc, + rvalue *expr, + type *type_); + lvalue * new_array_access (location *loc, rvalue *ptr, diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc index 1e3fadfacd7..e1029086da8 100644 --- a/gcc/jit/jit-recording.cc +++ b/gcc/jit/jit-recording.cc @@ -1242,6 +1242,22 @@ recording::context::new_cast (recording::location *loc, return result; } +/* Create a recording::bitcast instance and add it to this context's list + of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_bitcast. */ + +recording::rvalue * +recording::context::new_bitcast (location *loc, + rvalue *expr, + type *type_) +{ + recording::rvalue *result = new bitcast (this, loc, expr, type_); + record (result); + return result; +} + /* Create a recording::call instance and add it to this context's list of mementos. @@ -5740,6 +5756,56 @@ recording::cast::write_reproducer (reproducer &r) r.get_identifier_as_type (get_type ())); } +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::bitcast. */ + +void +recording::bitcast::replay_into (replayer *r) +{ + set_playback_obj (r->new_bitcast (playback_location (r, m_loc), + m_rvalue->playback_rvalue (), + get_type ()->playback_type ())); +} + +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::bitcast. */ +void +recording::bitcast::visit_children (rvalue_visitor *v) +{ + v->visit (m_rvalue); +} + +/* Implementation of recording::memento::make_debug_string for + casts. */ + +recording::string * +recording::bitcast::make_debug_string () +{ + enum precedence prec = get_precedence (); + return string::from_printf (m_ctxt, + "bitcast(%s, %s)", + m_rvalue->get_debug_string_parens (prec), + get_type ()->get_debug_string ()); +} + +/* Implementation of recording::memento::write_reproducer for casts. */ + +void +recording::bitcast::write_reproducer (reproducer &r) +{ + const char *id = r.make_identifier (this, "rvalue"); + r.write (" gcc_jit_rvalue *%s =\n" + " gcc_jit_context_new_bitcast (%s,\n" + " %s, /* gcc_jit_location *loc */\n" + " %s, /* gcc_jit_rvalue *rvalue */\n" + " %s); /* gcc_jit_type *type */\n", + id, + r.get_identifier (get_context ()), + r.get_identifier (m_loc), + r.get_identifier_as_rvalue (m_rvalue), + r.get_identifier_as_type (get_type ())); +} + /* The implementation of class gcc::jit::recording::base_call. */ /* The constructor for gcc::jit::recording::base_call. */ diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 846d65cb202..ed03b3a24c8 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -205,6 +205,11 @@ public: rvalue *expr, type *type_); + rvalue * + new_bitcast (location *loc, + rvalue *expr, + type *type_); + lvalue * new_array_access (location *loc, rvalue *ptr, @@ -1691,6 +1696,33 @@ private: rvalue *m_rvalue; }; +class bitcast : public rvalue +{ +public: + bitcast (context *ctxt, + location *loc, + rvalue *a, + type *type_) + : rvalue (ctxt, loc, type_), + m_rvalue (a) + {} + + void replay_into (replayer *r) FINAL OVERRIDE; + + void visit_children (rvalue_visitor *v) 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_CAST; + } + +private: + rvalue *m_rvalue; +}; + class base_call : public rvalue { public: diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc index 4c352e8c93d..3535203975c 100644 --- a/gcc/jit/libgccjit.cc +++ b/gcc/jit/libgccjit.cc @@ -2405,6 +2405,28 @@ gcc_jit_context_new_cast (gcc_jit_context *ctxt, return static_cast (ctxt->new_cast (loc, rvalue, type)); } +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::context::new_bitcast method in jit-recording.c. */ + +gcc_jit_rvalue * +gcc_jit_context_new_bitcast (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue, + gcc_jit_type *type) +{ + RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context"); + JIT_LOG_FUNC (ctxt->get_logger ()); + /* LOC can be NULL. */ + RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue"); + RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type"); + /* We cannot check if the size of rvalue matches the size of type here, so + we'll do it at playback. */ + + return static_cast (ctxt->new_bitcast (loc, rvalue, type)); +} + /* 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 2a5ffacb1fe..960e4f4261e 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -1206,6 +1206,21 @@ gcc_jit_context_new_cast (gcc_jit_context *ctxt, gcc_jit_rvalue *rvalue, gcc_jit_type *type); +/* Reinterpret a value as another type. + +#define LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast + + The types must be of the same size. + + This API entrypoint was added in LIBGCCJIT_ABI_21; you can test for its + presence using + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast */ +extern gcc_jit_rvalue * +gcc_jit_context_new_bitcast (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue, + gcc_jit_type *type); + extern gcc_jit_lvalue * gcc_jit_context_new_array_access (gcc_jit_context *ctxt, gcc_jit_location *loc, diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index f373fd39ac7..38c355437bf 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -238,8 +238,17 @@ LIBGCCJIT_ABI_18 { } LIBGCCJIT_ABI_17; LIBGCCJIT_ABI_19 { + global: gcc_jit_context_new_array_constructor; gcc_jit_context_new_struct_constructor; gcc_jit_context_new_union_constructor; gcc_jit_global_set_initializer_rvalue; } LIBGCCJIT_ABI_18; + +LIBGCCJIT_ABI_20 { +} LIBGCCJIT_ABI_19; + +LIBGCCJIT_ABI_21 { + global: + gcc_jit_context_new_bitcast; +} LIBGCCJIT_ABI_20; diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h index 29afe064db6..656351edce1 100644 --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h @@ -77,6 +77,13 @@ /* test-builtin-unreachable.c: We don't add this one, since it touches the optimization level of the context as a whole. */ +/* test-bitcast.c */ +#define create_code create_code_bitcast +#define verify_code verify_code_bitcast +#include "test-bitcast.c" +#undef create_code +#undef verify_code + /* test-calling-external-function.c */ #define create_code create_code_calling_external_function #define verify_code verify_code_calling_external_function @@ -400,6 +407,9 @@ const struct testcase testcases[] = { {"builtin-memcpy", create_code_builtin_memcpy, verify_code_builtin_memcpy}, + {"bitcast", + create_code_bitcast, + verify_code_bitcast}, {"calling_external_function", create_code_calling_external_function, verify_code_calling_external_function}, diff --git a/gcc/testsuite/jit.dg/test-bitcast.c b/gcc/testsuite/jit.dg/test-bitcast.c new file mode 100644 index 00000000000..16d889d3abe --- /dev/null +++ b/gcc/testsuite/jit.dg/test-bitcast.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#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 +my_bitcast (float x) +{ + return bitcast(x, int); +} + */ + gcc_jit_type *int_type = + gcc_jit_context_get_int_type (ctxt, 4, 1); + gcc_jit_type *float_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_FLOAT); + + gcc_jit_param *x = + gcc_jit_context_new_param ( + ctxt, + NULL, + float_type, "x"); + gcc_jit_param *params[1] = {x}; + gcc_jit_function *func = + gcc_jit_context_new_function (ctxt, + NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "my_bitcast", + 1, params, 0); + + gcc_jit_block *initial = + gcc_jit_function_new_block (func, "initial"); + + gcc_jit_block_end_with_return(initial, NULL, + gcc_jit_context_new_bitcast(ctxt, + NULL, + gcc_jit_param_as_rvalue(x), + int_type + )); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + typedef int (*my_bitcast_fn_type) (float); + CHECK_NON_NULL (result); + my_bitcast_fn_type my_bitcast = + (my_bitcast_fn_type)gcc_jit_result_get_code (result, "my_bitcast"); + CHECK_NON_NULL (my_bitcast); + int val = my_bitcast (-5.1298714); + note ("my_bitcast returned: %d", val); + CHECK_VALUE (val, 35569201); +} diff --git a/gcc/testsuite/jit.dg/test-error-bad-bitcast.c b/gcc/testsuite/jit.dg/test-error-bad-bitcast.c new file mode 100644 index 00000000000..642890605ad --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-bad-bitcast.c @@ -0,0 +1,62 @@ +#include +#include + +#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_fn () + { + char f[4096]; + return bitcast(f, int); + } + + and verify that the API complains about the bad cast. + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + gcc_jit_type *char_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR); + + + gcc_jit_type *array_type = + gcc_jit_context_new_array_type (ctxt, NULL, char_type, 4096); + + gcc_jit_function *test_fn = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + int_type, + "test_fn", + 0, NULL, + 0); + gcc_jit_lvalue *f = + gcc_jit_function_new_local ( + test_fn, + NULL, + array_type, "f"); + + gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL); + + gcc_jit_block_end_with_return ( + block, NULL, + gcc_jit_context_new_bitcast (ctxt, NULL, + gcc_jit_lvalue_as_rvalue (f), + int_type)); +} + +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), + "bitcast with types of different sizes"); +} + diff --git a/gcc/testsuite/jit.dg/test-error-bad-bitcast2.c b/gcc/testsuite/jit.dg/test-error-bad-bitcast2.c new file mode 100644 index 00000000000..602ae407076 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-bad-bitcast2.c @@ -0,0 +1,62 @@ +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + + char[4096] + test_fn () + { + int f; + return bitcast(f, char[4096]); + } + + and verify that the API complains about the bad cast. + */ + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + gcc_jit_type *char_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR); + + + gcc_jit_type *array_type = + gcc_jit_context_new_array_type (ctxt, NULL, char_type, 4096); + + gcc_jit_function *test_fn = + gcc_jit_context_new_function (ctxt, NULL, + GCC_JIT_FUNCTION_EXPORTED, + array_type, + "test_fn", + 0, NULL, + 0); + gcc_jit_lvalue *f = + gcc_jit_function_new_local ( + test_fn, + NULL, + int_type, "f"); + + gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL); + + gcc_jit_block_end_with_return ( + block, NULL, + gcc_jit_context_new_bitcast (ctxt, NULL, + gcc_jit_lvalue_as_rvalue (f), + array_type)); +} + +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), + "bitcast with types of different sizes"); +} + diff --git a/gcc/toplev.cc b/gcc/toplev.cc index 534da1462e8..bc4921974eb 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -2368,6 +2368,7 @@ toplev::finalize (void) gcse_c_finalize (); ipa_cp_c_finalize (); ira_costs_c_finalize (); + tree_cc_finalize (); /* save_decoded_options uses opts_obstack, so these must be cleaned up together. */ diff --git a/gcc/tree.cc b/gcc/tree.cc index ae159ee20ce..fe9d9083026 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -6963,6 +6963,15 @@ build_reference_type (tree to_type) (HOST_BITS_PER_WIDE_INT > 64 ? HOST_BITS_PER_WIDE_INT : 64) static GTY(()) tree nonstandard_integer_type_cache[2 * MAX_INT_CACHED_PREC + 2]; +static void +clear_nonstandard_integer_type_cache (void) +{ + for (size_t i = 0 ; i < 2 * MAX_INT_CACHED_PREC + 2 ; i++) + { + nonstandard_integer_type_cache[i] = NULL; + } +} + /* Builds a signed or unsigned integer type of precision PRECISION. Used for C bitfields whose precision does not match that of built-in target types. */ @@ -14565,6 +14574,12 @@ get_attr_nonstring_decl (tree expr, tree *ref) return NULL_TREE; } +void +tree_cc_finalize (void) +{ + clear_nonstandard_integer_type_cache (); +} + #if CHECKING_P namespace selftest { diff --git a/gcc/tree.h b/gcc/tree.h index 30bc53c2996..bf886fc2472 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5385,6 +5385,7 @@ extern bool real_minus_onep (const_tree); extern void init_ttree (void); extern void build_common_tree_nodes (bool); extern void build_common_builtin_nodes (void); +extern void tree_cc_finalize (void); extern tree build_nonstandard_integer_type (unsigned HOST_WIDE_INT, int); extern tree build_nonstandard_boolean_type (unsigned HOST_WIDE_INT); extern tree build_range_type (tree, tree, tree); -- 2.26.2.7.g19db9cfb68.dirty