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:
parent
af80ea97b6
commit
30f7c83e9c
File diff suppressed because it is too large
Load Diff
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
-------
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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. */
|
||||
|
|
15
gcc/tree.cc
15
gcc/tree.cc
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue