libgccjit: Add support for bitcasts [PR104071]

gcc/jit/
	PR jit/104071
	* docs/_build/texinfo/libgccjit.texi: Regenerate.
	* 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).
This commit is contained in:
Antoni Boucher 2022-04-12 17:17:50 -04:00 committed by David Malcolm
parent af80ea97b6
commit 30f7c83e9c
17 changed files with 1079 additions and 625 deletions

File diff suppressed because it is too large Load Diff

View File

@ -334,3 +334,12 @@ of a global with an rvalue and to use constructors:
* :c:macro:`GCC_JIT_TYPE_INT32_T`
* :c:macro:`GCC_JIT_TYPE_INT64_T`
* :c:macro:`GCC_JIT_TYPE_INT128_T`
.. _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`

View File

@ -653,6 +653,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
-------

View File

@ -1422,6 +1422,36 @@ new_cast (playback::location *loc,
return new rvalue (this, t_cast);
}
/* Construct a playback::rvalue instance (wrapping a tree) for a
bitcast. */
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. */

View File

@ -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,

View File

@ -1246,6 +1246,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.
@ -5882,6 +5898,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. */

View File

@ -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,
@ -1709,6 +1714,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:

View File

@ -2433,6 +2433,28 @@ gcc_jit_context_new_cast (gcc_jit_context *ctxt,
return static_cast <gcc_jit_rvalue *> (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 <gcc_jit_rvalue *> (ctxt->new_bitcast (loc, rvalue, type));
}
/* Public entrypoint. See description in libgccjit.h.
After error-checking, the real work is done by the

View File

@ -1234,6 +1234,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,

View File

@ -238,6 +238,7 @@ 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;
@ -249,3 +250,8 @@ LIBGCCJIT_ABI_20 {
gcc_jit_compatible_types;
gcc_jit_type_get_size;
} LIBGCCJIT_ABI_19;
LIBGCCJIT_ABI_21 {
global:
gcc_jit_context_new_bitcast;
} LIBGCCJIT_ABI_20;

View File

@ -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},

View File

@ -0,0 +1,61 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.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:
int32_t
my_bitcast (float x)
{
return bitcast(x, int32_t);
}
*/
gcc_jit_type *int32 =
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,
int32,
"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),
int32
));
}
void
verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
{
typedef int32_t (*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);
int32_t val = my_bitcast (3.1415927f);
note ("my_bitcast returned: 0x%x", val);
CHECK_VALUE (val, 0x40490FDB);
}

View File

@ -0,0 +1,62 @@
#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_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");
}

View File

@ -0,0 +1,62 @@
#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:
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");
}

View File

@ -2378,6 +2378,7 @@ toplev::finalize (void)
gcse_cc_finalize ();
ipa_cp_cc_finalize ();
ira_costs_cc_finalize ();
tree_cc_finalize ();
/* save_decoded_options uses opts_obstack, so these must
be cleaned up together. */

View File

@ -6985,6 +6985,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. */
@ -14675,6 +14684,12 @@ get_target_clone_attr_len (tree arglist)
return str_len_sum;
}
void
tree_cc_finalize (void)
{
clear_nonstandard_integer_type_cache ();
}
#if CHECKING_P
namespace selftest {

View File

@ -5401,6 +5401,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);