Hi David! I updated the patch to allow initializing global variables with values of type array or struct.
I also fixed the bug I was talking in my previous message by using the following workaround: I create a new memento for the action of setting the global variable initial value and as such, both the global variable and the initial value are bound to exist when setting the global variable initializer. Is that workaround good enough? (I guess that workaround could be used to fix the same issue that we have for inline assembly.) Thanks for the review! Le vendredi 11 juin 2021 à 16:44 -0400, Antoni Boucher a écrit : > David: this one wasn't reviewed yet by you, so you can review it. > > Le jeudi 20 mai 2021 à 21:27 -0400, Antoni Boucher a écrit : > > Hi. > > > > I made this patch to set an arbitrary value to a global variable. > > > > This patch suffers from the same issue as inline assembly > > (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100380), i.e. it > > segfaults if the `rvalue` is created after the global variable. > > It seems to be a design issue so I'm not sure what would be the fix > > for > > it and having it fixed would allow me to test this new function > > much > > more and see if things are missing (i.e. it might require a way to > > create a constant struct). > > See the link above for an explanation of this issue. > > > > Thanks for the review. >
From a095291f43ca8348b6c84f94a598895969d94d1b Mon Sep 17 00:00:00 2001 From: Antoni Boucher <boua...@zoho.com> Date: Sat, 25 Sep 2021 16:37:47 -0400 Subject: [PATCH] libgccjit: Add function to set the initial value of a global variable [PR96089] 2021-11-22 Antoni Boucher <boua...@zoho.com> gcc/jit/ PR target/96089 * docs/topics/compatibility.rst (LIBGCCJIT_ABI_21): New ABI tag. * docs/topics/expressions.rst: Add documentation for the function gcc_jit_global_set_initializer_value, gcc_jit_context_new_rvalue_from_array, and gcc_jit_context_new_rvalue_from_struct. * jit-common.h: Add missing reference to array_type in class hierarchy. * jit-playback.c: New functions (new_global_with_value, set_global_initial_value, new_rvalue_from_struct, new_rvalue_from_array). * jit-playback.h: New functions (new_global_with_value, set_global_initial_value, new_rvalue_from_struct, new_rvalue_from_array). * jit-recording.c: Add support for setting a value to a global variable and new methods (global_initializer::write_reproducer, global_initializer::make_debug_string, global_initializer::write_to_dump, global_initializer::replay_into, context::new_global_value_initializer, memento_of_new_rvalue_from_struct::write_reproducer, memento_of_new_rvalue_from_struct::make_debug_string, memento_of_new_rvalue_from_struct::visit_children, memento_of_new_rvalue_from_struct::replay_into, memento_of_new_rvalue_from_struct:: memento_of_new_rvalue_from_struct, context::new_rvalue_from_struct, memento_of_new_rvalue_from_array::write_reproducer, memento_of_new_rvalue_from_array::make_debug_string, memento_of_new_rvalue_from_array::visit_children, memento_of_new_rvalue_from_array::replay_into, memento_of_new_rvalue_from_array:: memento_of_new_rvalue_from_array, new_rvalue_from_array). * jit-recording.h: New functions (set_initializer_value, new_global_value_initializer, new_rvalue_from_struct, new_rvalue_from_array, get_kind), new field m_initializer_value and new classes (global_initializer, memento_of_new_rvalue_from_struct, memento_of_new_rvalue_from_array). * libgccjit.c: New macro RETURN_IF_FAIL_PRINTF5 and new functions (gcc_jit_global_set_initializer_value, gcc_jit_context_new_rvalue_from_struct, gcc_jit_context_new_rvalue_from_array). * libgccjit.h: New functions (gcc_jit_global_set_initializer_value, gcc_jit_context_new_rvalue_from_struct, gcc_jit_context_new_rvalue_from_array). * libgccjit.map (LIBGCCJIT_ABI_21): New ABI tag. gcc/testsuite/ PR target/96089 * jit.dg/test-global-set-initializer.c: Add test for the new function (gcc_jit_global_set_initializer_value). * jit.dg/test-error-imported-global-initializer.c: Add test for error checking in setting a value to a global variable. --- gcc/jit/docs/topics/compatibility.rst | 13 + gcc/jit/docs/topics/expressions.rst | 62 ++++ gcc/jit/jit-common.h | 1 + gcc/jit/jit-playback.c | 72 +++++ gcc/jit/jit-playback.h | 21 ++ gcc/jit/jit-recording.c | 305 +++++++++++++++++- gcc/jit/jit-recording.h | 140 ++++++++ gcc/jit/libgccjit.c | 161 +++++++++ gcc/jit/libgccjit.h | 27 ++ gcc/jit/libgccjit.map | 19 ++ .../test-error-imported-global-initializer.c | 40 +++ .../jit.dg/test-global-set-initializer.c | 81 +++++ 12 files changed, 936 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-error-imported-global-initializer.c diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 52ee3f860a7..f6dc0e43cad 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -284,3 +284,16 @@ entrypoints: * :func:`gcc_jit_struct_get_field` * :func:`gcc_jit_struct_get_field_count` + +.. _LIBGCCJIT_ABI_21: + +``LIBGCCJIT_ABI_21`` +----------------------- +``LIBGCCJIT_ABI_21`` covers the addition of an API entrypoint to set the value +of a global variable: + + * :func:`gcc_jit_global_set_initializer_value` + + * :func:`gcc_jit_context_new_rvalue_from_struct` + + * :func:`gcc_jit_context_new_rvalue_from_array` diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst index 396259ef07e..a59309f172f 100644 --- a/gcc/jit/docs/topics/expressions.rst +++ b/gcc/jit/docs/topics/expressions.rst @@ -150,6 +150,54 @@ Vector expressions #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_rvalue_from_vector +Array expressions +****************** + +.. function:: gcc_jit_rvalue * \ + gcc_jit_context_new_rvalue_from_array (gcc_jit_context *ctxt, \ + gcc_jit_location *loc, \ + gcc_jit_type *array_type, \ + size_t num_elements, \ + gcc_jit_rvalue **elements) + + Build an array rvalue from an array of elements. + + "type" should be an array type, created using + :func:`gcc_jit_context_new_array_type`. + + "num_elements" should match that of the array type. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_21`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_GLOBAL_INITIALIZER_VALUE + +Struct expressions +****************** + +.. function:: gcc_jit_rvalue * \ + gcc_jit_context_new_rvalue_from_struct (gcc_jit_context *ctxt, \ + gcc_jit_location *loc, \ + gcc_jit_struct *struct_type, \ + size_t num_fields, \ + gcc_jit_rvalue **fields) + + Build a struct rvalue from an array of fields. + + "struct_type" should be a struct type, created using + :func:`gcc_jit_context_new_struct_type`. + + "num_fields" should match that the number of fields in the struct type. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_21`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_GLOBAL_INITIALIZER_VALUE + Unary Operations **************** @@ -603,6 +651,20 @@ Global variables #ifdef LIBGCCJIT_HAVE_gcc_jit_global_set_initializer +.. function:: void + gcc_jit_global_set_initializer_value (gcc_jit_lvalue *global,\ + gcc_jit_rvalue *value) + + Set an initializer for ``global`` using the specified constant value. + ``global`` must be the same type as ``value``. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_21`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_GLOBAL_INITIALIZER_VALUE + Working with pointers, structs and unions ----------------------------------------- diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index f88e6755b00..2898db7dd75 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -117,6 +117,7 @@ namespace recording { class compound_type; class struct_; class union_; + class array_type; class vector_type; class field; class bitfield; diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c index 59399dee251..3e046cba62c 100644 --- a/gcc/jit/jit-playback.c +++ b/gcc/jit/jit-playback.c @@ -667,6 +667,38 @@ new_global_initialized (location *loc, return global_finalize_lvalue (inner); } +playback::lvalue * +playback::context:: +new_global_with_value (location *loc, + enum gcc_jit_global_kind kind, + type *type, + playback::rvalue *value, + const char *name) +{ + tree inner = global_new_decl (loc, kind, type, name); + + tree initial = value->as_tree (); + gcc_assert (TREE_CONSTANT (initial)); + DECL_INITIAL (inner) = initial; + + return global_finalize_lvalue (inner); +} + +void +playback::context:: +set_global_initial_value (playback::lvalue *global, + playback::rvalue *value) +{ + tree initial = value->as_tree (); + if (!TREE_CONSTANT (initial)) + { + add_error (NULL, "initial value for global is not a constant"); + debug_tree (initial); + gcc_assert (TREE_CONSTANT (initial)); + } + DECL_INITIAL (global->as_tree ()) = initial; +} + /* Implementation of the various gcc::jit::playback::context::new_rvalue_from_const <HOST_TYPE> methods. @@ -817,6 +849,46 @@ playback::context::new_rvalue_from_vector (location *, return new rvalue (this, t_ctor); } +/* Construct a playback::rvalue instance (wrapping a tree) for a + struct. */ + +playback::rvalue * +playback::context::new_rvalue_from_struct (location *, + type *type, + const auto_vec<rvalue *> &fields) +{ + vec<constructor_elt, va_gc> *v; + vec_alloc (v, fields.length ()); + tree field_decl = TYPE_FIELDS (type->as_tree ()); + for (unsigned i = 0; i < fields.length (); ++i) + { + CONSTRUCTOR_APPEND_ELT (v, field_decl, fields[i]->as_tree ()); + field_decl = TREE_CHAIN (field_decl); + } + + tree t_ctor = build_constructor (type->as_tree (), v); + return new rvalue (this, t_ctor); +} + +/* Construct a playback::rvalue instance (wrapping a tree) for a + array. */ + +playback::rvalue * +playback::context::new_rvalue_from_array (location *, + type *type, + const auto_vec<rvalue *> &elements) +{ + vec<constructor_elt, va_gc> *v; + vec_alloc (v, elements.length ()); + for (unsigned i = 0; i < elements.length (); ++i) + { + tree index = build_int_cst (long_unsigned_type_node, i); + CONSTRUCTOR_APPEND_ELT (v, index, elements[i]->as_tree ()); + } + tree t_ctor = build_constructor (type->as_tree (), v); + return new rvalue (this, t_ctor); +} + /* Coerce a tree expression into a boolean tree expression. */ tree diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index f670c9e81df..e4c1816dbaf 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -120,6 +120,17 @@ public: const void *initializer, const char *name); + lvalue* + new_global_with_value (location *loc, + enum gcc_jit_global_kind kind, + type *type, + rvalue *value, + const char *name); + + void + set_global_initial_value (playback::lvalue *global, + playback::rvalue *value); + template <typename HOST_TYPE> rvalue * new_rvalue_from_const (type *type, @@ -133,6 +144,16 @@ public: type *type, const auto_vec<rvalue *> &elements); + rvalue * + new_rvalue_from_struct (location *loc, + type *type, + const auto_vec<rvalue *> &fields); + + rvalue * + new_rvalue_from_array (location *loc, + type *type, + const auto_vec<rvalue *> &elements); + rvalue * new_unary_op (location *loc, enum gcc_jit_unary_op op, diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 117ff70114c..21813f497cb 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -1062,6 +1062,21 @@ recording::context::new_global (recording::location *loc, return result; } +/* Create a memento instance to initialize a global variable and add it to this + * context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_global_set_initializer_value. */ + +void +recording::context::new_global_value_initializer (recording::lvalue *global, + recording::rvalue *value) +{ + recording::global_initializer *result = + new recording::global_initializer (global, value); + record (result); +} + /* Create a recording::memento_of_new_string_literal instance and add it to this context's list of mementos. @@ -1077,6 +1092,40 @@ recording::context::new_string_literal (const char *value) return result; } +/* Create a recording::memento_of_new_rvalue_from_struct instance and add it + to this context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_rvalue_from_struct. */ + +recording::rvalue * +recording::context::new_rvalue_from_struct (location *loc, + struct_ *type, + rvalue **fields) +{ + recording::rvalue *result = + new memento_of_new_rvalue_from_struct (this, loc, type, fields); + record (result); + return result; +} + +/* Create a recording::memento_of_new_rvalue_from_array instance and add it + to this context's list of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_rvalue_from_array. */ + +recording::rvalue * +recording::context::new_rvalue_from_array (location *loc, + array_type *type, + rvalue **elements) +{ + recording::rvalue *result = + new memento_of_new_rvalue_from_array (this, loc, type, elements); + record (result); + return result; +} + /* Create a recording::memento_of_new_rvalue_from_vector instance and add it to this context's list of mementos. @@ -4547,20 +4596,34 @@ recording::block::dump_edges_to_dot (pretty_printer *pp) void recording::global::replay_into (replayer *r) { - set_playback_obj ( - m_initializer - ? r->new_global_initialized (playback_location (r, m_loc), + playback::lvalue *global; + if (m_initializer) + { + global = r->new_global_initialized (playback_location (r, m_loc), m_kind, m_type->playback_type (), m_type->dereference ()->get_size (), m_initializer_num_bytes / m_type->dereference ()->get_size (), m_initializer, - playback_string (m_name)) - : r->new_global (playback_location (r, m_loc), + playback_string (m_name)); + } + else if (m_initializer_value) + { + global = r->new_global_with_value (playback_location (r, m_loc), + m_kind, + m_type->playback_type (), + m_initializer_value->playback_rvalue (), + playback_string (m_name)); + } + else + { + global = r->new_global (playback_location (r, m_loc), m_kind, m_type->playback_type (), - playback_string (m_name))); + playback_string (m_name)); + } + set_playback_obj (global); } /* Override the default implementation of @@ -4697,6 +4760,56 @@ recording::global::write_reproducer (reproducer &r) } } +/* The implementation of class gcc::jit::recording::global_initializer. */ + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::global_initializer. */ + +void +recording::global_initializer::replay_into (replayer *r) +{ + r->set_global_initial_value (m_global->playback_lvalue (), + m_value->playback_rvalue ()); +} + +/* Override the default implementation of + recording::memento::write_to_dump for globals. + This will be of the form: + + GLOBAL = INITIALIZER; + + These are written to the top of the dump by + recording::context::dump_to_file. */ + +void +recording::global_initializer::write_to_dump (dump &d) +{ + d.write (" %s = %s;\n", + m_global->get_debug_string (), + m_value->get_debug_string ()); +} + +recording::string * +recording::global_initializer::make_debug_string () +{ + return string::from_printf (m_ctxt, + "%s = %s", + m_global->get_debug_string (), + m_value->get_debug_string ()); +} + +/* Implementation of recording::memento::write_reproducer for global + initializers. */ + +void +recording::global_initializer::write_reproducer (reproducer &r) +{ + r.write (" gcc_jit_global_set_initializer_value (%s, /* gcc_jit_lvalue *global */\n" + " %s); /* gcc_jit_rvalue *value */\n", + r.get_identifier_as_lvalue (m_global), + r.get_identifier_as_rvalue (m_value)); +} + /* The implementation of the various const-handling classes: gcc::jit::recording::memento_of_new_rvalue_from_const <HOST_TYPE>. */ @@ -5070,6 +5183,186 @@ recording::memento_of_new_rvalue_from_vector::write_reproducer (reproducer &r) elements_id); } +/* The implementation of class + gcc::jit::recording::memento_of_new_rvalue_from_struct. */ + +/* The constructor for + gcc::jit::recording::memento_of_new_rvalue_from_struct. */ + +recording::memento_of_new_rvalue_from_struct:: +memento_of_new_rvalue_from_struct (context *ctxt, + location *loc, + struct_ *type, + rvalue **fields) +: rvalue (ctxt, loc, type), + m_struct_type (type), + m_fields () +{ + for (int i = 0; i < type->get_fields ()->length (); i++) + m_fields.safe_push (fields[i]); +} + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::memento_of_new_rvalue_from_struct. */ + +void +recording::memento_of_new_rvalue_from_struct::replay_into (replayer *r) +{ + auto_vec<playback::rvalue *> playback_fields; + playback_fields.create (m_fields.length ()); + for (unsigned i = 0; i< m_fields.length (); i++) + playback_fields.safe_push (m_fields[i]->playback_rvalue ()); + + set_playback_obj (r->new_rvalue_from_struct (playback_location (r, m_loc), + m_type->playback_type (), + playback_fields)); +} + +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::memento_of_new_rvalue_from_struct. */ + +void +recording::memento_of_new_rvalue_from_struct::visit_children (rvalue_visitor *v) +{ + for (unsigned i = 0; i< m_fields.length (); i++) + v->visit (m_fields[i]); +} + +/* Implementation of recording::memento::make_debug_string for + vectors. */ + +recording::string * +recording::memento_of_new_rvalue_from_struct::make_debug_string () +{ + comma_separated_string fields (m_fields, get_precedence ()); + + /* Now build a string. */ + string *result = string::from_printf (m_ctxt, + "{%s}", + fields.as_char_ptr ()); + + return result; + +} + +/* Implementation of recording::memento::write_reproducer for + vectors. */ + +void +recording::memento_of_new_rvalue_from_struct::write_reproducer (reproducer &r) +{ + const char *id = r.make_identifier (this, "struct"); + const char *fields_id = r.make_tmp_identifier ("fields_for_", this); + r.write (" gcc_jit_rvalue *%s[%i] = {\n", + fields_id, + m_fields.length ()); + for (unsigned i = 0; i< m_fields.length (); i++) + r.write (" %s,\n", r.get_identifier_as_rvalue (m_fields[i])); + r.write (" };\n"); + r.write (" gcc_jit_rvalue *%s =\n" + " gcc_jit_context_new_rvalue_from_struct (%s, /* gcc_jit_context *ctxt */\n" + " %s, /* gcc_jit_location *loc */\n" + " %s, /* gcc_jit_struct *struct_type */\n" + " %i, /* size_t num_fields */ \n" + " %s); /* gcc_jit_rvalue **fields*/\n", + id, + r.get_identifier (get_context ()), + r.get_identifier (m_loc), + r.get_identifier (m_struct_type), + m_fields.length (), + fields_id); +} + +/* The implementation of class + gcc::jit::recording::memento_of_new_rvalue_from_array. */ + +/* The constructor for + gcc::jit::recording::memento_of_new_rvalue_from_array. */ + +recording::memento_of_new_rvalue_from_array:: +memento_of_new_rvalue_from_array (context *ctxt, + location *loc, + array_type *type, + rvalue **elements) +: rvalue (ctxt, loc, type), + m_array_type (type), + m_elements () +{ + for (int i = 0; i < type->num_elements (); i++) + m_elements.safe_push (elements[i]); +} + +/* Implementation of pure virtual hook recording::memento::replay_into + for recording::memento_of_new_rvalue_from_array. */ + +void +recording::memento_of_new_rvalue_from_array::replay_into (replayer *r) +{ + auto_vec<playback::rvalue *> playback_elements; + playback_elements.create (m_elements.length ()); + for (unsigned i = 0; i< m_elements.length (); i++) + playback_elements.safe_push (m_elements[i]->playback_rvalue ()); + + set_playback_obj (r->new_rvalue_from_array (playback_location (r, m_loc), + m_type->playback_type (), + playback_elements)); +} + +/* Implementation of pure virtual hook recording::rvalue::visit_children + for recording::memento_of_new_rvalue_from_array. */ + +void +recording::memento_of_new_rvalue_from_array::visit_children (rvalue_visitor *v) +{ + for (unsigned i = 0; i< m_elements.length (); i++) + v->visit (m_elements[i]); +} + +/* Implementation of recording::memento::make_debug_string for + vectors. */ + +recording::string * +recording::memento_of_new_rvalue_from_array::make_debug_string () +{ + comma_separated_string elements (m_elements, get_precedence ()); + + /* Now build a string. */ + string *result = string::from_printf (m_ctxt, + "{%s}", + elements.as_char_ptr ()); + + return result; + +} + +/* Implementation of recording::memento::write_reproducer for + vectors. */ + +void +recording::memento_of_new_rvalue_from_array::write_reproducer (reproducer &r) +{ + const char *id = r.make_identifier (this, "array"); + const char *elements_id = r.make_tmp_identifier ("elements_for_", this); + r.write (" gcc_jit_rvalue *%s[%i] = {\n", + elements_id, + m_elements.length ()); + for (unsigned i = 0; i< m_elements.length (); i++) + r.write (" %s,\n", r.get_identifier_as_rvalue (m_elements[i])); + r.write (" };\n"); + r.write (" gcc_jit_rvalue *%s =\n" + " gcc_jit_context_new_rvalue_from_array (%s, /* gcc_jit_context *ctxt */\n" + " %s, /* gcc_jit_location *loc */\n" + " %s, /* gcc_jit_type *array_type */\n" + " %i, /* size_t num_elements */ \n" + " %s); /* gcc_jit_rvalue **elements*/\n", + id, + r.get_identifier (get_context ()), + r.get_identifier (m_loc), + r.get_identifier (m_array_type), + m_elements.length (), + elements_id); +} + /* The implementation of class gcc::jit::recording::unary_op. */ /* Implementation of pure virtual hook recording::memento::replay_into diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 4a994fe7094..3ab2373cb9e 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -149,6 +149,10 @@ public: type *type, const char *name); + void + new_global_value_initializer (recording::lvalue *global, + recording::rvalue *value); + template <typename HOST_TYPE> rvalue * new_rvalue_from_const (type *type, @@ -162,6 +166,16 @@ public: vector_type *type, rvalue **elements); + rvalue * + new_rvalue_from_struct (location *loc, + struct_ *type, + rvalue **fields); + + rvalue * + new_rvalue_from_array (location *loc, + array_type *type, + rvalue **elements); + rvalue * new_unary_op (location *loc, enum gcc_jit_unary_op op, @@ -522,6 +536,7 @@ public: virtual function_type *as_a_function_type() { gcc_unreachable (); return NULL; } virtual struct_ *dyn_cast_struct () { return NULL; } virtual vector_type *dyn_cast_vector_type () { return NULL; } + virtual array_type *dyn_cast_array_type () { return NULL; } /* Is it typesafe to copy to this type from rtype? */ virtual bool accepts_writes_from (type *rtype) @@ -773,6 +788,8 @@ class array_type : public type type *dereference () FINAL OVERRIDE; + array_type *dyn_cast_array_type () FINAL OVERRIDE { return this; } + bool is_int () const FINAL OVERRIDE { return false; } bool is_float () const FINAL OVERRIDE { return false; } bool is_bool () const FINAL OVERRIDE { return false; } @@ -1134,6 +1151,11 @@ public: const char *access_as_rvalue (reproducer &r) OVERRIDE; virtual const char *access_as_lvalue (reproducer &r); virtual bool is_global () const { return false; } + + bool is_constant () const FINAL OVERRIDE + { + return is_global (); + } }; class param : public lvalue @@ -1367,6 +1389,7 @@ public: m_name (name) { m_initializer = NULL; + m_initializer_value = NULL; m_initializer_num_bytes = 0; } ~global () @@ -1393,6 +1416,14 @@ public: m_initializer_num_bytes = num_bytes; } + void + set_initializer_value (rvalue* value) + { + m_initializer_value = value; + } + + enum gcc_jit_global_kind get_kind () const { return m_kind; } + private: string * make_debug_string () FINAL OVERRIDE { return m_name; } template <typename T> @@ -1407,9 +1438,30 @@ private: enum gcc_jit_global_kind m_kind; string *m_name; void *m_initializer; + rvalue *m_initializer_value; size_t m_initializer_num_bytes; }; +class global_initializer : public memento +{ +public: + void write_to_dump (dump &d) FINAL OVERRIDE; + void replay_into (replayer *) FINAL OVERRIDE; + + global_initializer (lvalue *global, rvalue *value) + : memento (global->m_ctxt), + m_global (global), + m_value (value) {} + +private: + void write_reproducer (reproducer &r) FINAL OVERRIDE; + string * make_debug_string () FINAL OVERRIDE; + +private: + lvalue *m_global; + rvalue *m_value; +}; + template <typename HOST_TYPE> class memento_of_new_rvalue_from_const : public rvalue { @@ -1454,6 +1506,8 @@ public: void visit_children (rvalue_visitor *) FINAL OVERRIDE {} + virtual bool is_constant () const { return true; } + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; @@ -1491,6 +1545,78 @@ private: auto_vec<rvalue *> m_elements; }; +class memento_of_new_rvalue_from_array : public rvalue +{ +public: + memento_of_new_rvalue_from_array (context *ctxt, + location *loc, + array_type *type, + rvalue **elements); + + void replay_into (replayer *r) FINAL OVERRIDE; + + void visit_children (rvalue_visitor *) FINAL OVERRIDE; + + virtual bool is_constant () const { + for (rvalue *element : m_elements) + { + if (!element->is_constant ()) + { + return false; + } + } + return true; + } + +private: + string * make_debug_string () FINAL OVERRIDE; + void write_reproducer (reproducer &r) FINAL OVERRIDE; + enum precedence get_precedence () const FINAL OVERRIDE + { + return PRECEDENCE_PRIMARY; + } + +private: + array_type *m_array_type; + auto_vec<rvalue *> m_elements; +}; + +class memento_of_new_rvalue_from_struct : public rvalue +{ +public: + memento_of_new_rvalue_from_struct (context *ctxt, + location *loc, + struct_ *type, + rvalue **fields); + + void replay_into (replayer *r) FINAL OVERRIDE; + + void visit_children (rvalue_visitor *) FINAL OVERRIDE; + + virtual bool is_constant () const { + for (rvalue *field : m_fields) + { + if (!field->is_constant ()) + { + return false; + } + } + return true; + } + +private: + string * make_debug_string () FINAL OVERRIDE; + void write_reproducer (reproducer &r) FINAL OVERRIDE; + enum precedence get_precedence () const FINAL OVERRIDE + { + return PRECEDENCE_PRIMARY; + } + +private: + struct_ *m_struct_type; + auto_vec<rvalue *> m_fields; +}; + class unary_op : public rvalue { public: @@ -1538,6 +1664,10 @@ public: void visit_children (rvalue_visitor *v) FINAL OVERRIDE; + virtual bool is_constant () const { + return m_a->is_constant () && m_b->is_constant (); + } + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; @@ -1592,6 +1722,10 @@ public: void visit_children (rvalue_visitor *v) FINAL OVERRIDE; + virtual bool is_constant () const { + return m_rvalue->is_constant (); + } + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; @@ -1830,6 +1964,10 @@ public: void visit_children (rvalue_visitor *v) FINAL OVERRIDE; + virtual bool is_constant () const { + return m_lvalue->is_constant (); + } + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; @@ -1856,6 +1994,8 @@ public: void visit_children (rvalue_visitor *v) FINAL OVERRIDE; + virtual bool is_constant () const { return true; } + private: string * make_debug_string () FINAL OVERRIDE; void write_reproducer (reproducer &r) FINAL OVERRIDE; diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index c744b634f4b..f48636c844f 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -277,6 +277,17 @@ struct gcc_jit_extended_asm : public gcc::jit::recording::extended_asm } \ JIT_END_STMT +#define RETURN_IF_FAIL_PRINTF5(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, \ + A4) \ + JIT_BEGIN_STMT \ + if (!(TEST_EXPR)) \ + { \ + jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \ + __func__, (A0), (A1), (A2), (A3), (A4)); \ + return; \ + } \ + JIT_END_STMT + /* Check that BLOCK is non-NULL, and that it's OK to add statements to it. This will fail if BLOCK has already been terminated by some kind of jump or a return. */ @@ -1425,6 +1436,48 @@ gcc_jit_global_set_initializer (gcc_jit_lvalue *global, return global; } +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::global::set_initializer_value method, in + jit-recording.c. */ + +void +gcc_jit_global_set_initializer_value (gcc_jit_lvalue *global, + gcc_jit_rvalue *value) +{ + RETURN_IF_FAIL (global, NULL, NULL, "NULL global"); + RETURN_IF_FAIL (value, NULL, NULL, "NULL value"); + RETURN_IF_FAIL_PRINTF1 (global->is_global (), NULL, NULL, + "lvalue \"%s\" not a global", + global->get_debug_string ()); + + gcc::jit::recording::global *global_variable = + reinterpret_cast <gcc::jit::recording::global *> (global); + RETURN_IF_FAIL_PRINTF1 (global_variable->get_kind () + != GCC_JIT_GLOBAL_IMPORTED, global->get_context (), NULL, + "global \"%s\" cannot be of imported kind", + global->get_debug_string ()); + + RETURN_IF_FAIL_PRINTF5 ( + compatible_types (global->get_type (), + value->get_type ()), + NULL, NULL, + "mismatching types for global \"%s\":" + " assignment to global %s (type: %s) from %s (type: %s)", + global->get_debug_string (), + global->get_debug_string (), + global->get_type ()->get_debug_string (), + value->get_debug_string (), + value->get_type ()->get_debug_string ()); + + RETURN_IF_FAIL_PRINTF1 (value->is_constant (), NULL, NULL, + "rvalue \"%s\" not a constant", + value->get_debug_string ()); + + global->get_context ()->new_global_value_initializer (global, value); +} + /* Public entrypoint. See description in libgccjit.h. After error-checking, this calls the trivial @@ -1647,6 +1700,114 @@ gcc_jit_context_new_string_literal (gcc_jit_context *ctxt, return (gcc_jit_rvalue *)ctxt->new_string_literal (value); } +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::context::new_rvalue_from_struct method in + jit-recording.c. */ + +gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_struct (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_struct *struct_type, + size_t num_fields, + gcc_jit_rvalue **fields) +{ + RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context"); + JIT_LOG_FUNC (ctxt->get_logger ()); + + /* LOC can be NULL. */ + RETURN_NULL_IF_FAIL (struct_type, ctxt, loc, "NULL struct_type"); + + /* "num_fields" must match. */ + RETURN_NULL_IF_FAIL_PRINTF1 ( + num_fields == (size_t) struct_type->get_fields ()->length (), ctxt, loc, + "num_fields != %d", struct_type->get_fields ()->length ()); + + /* "fields must be non-NULL. */ + RETURN_NULL_IF_FAIL (fields, ctxt, loc, "NULL fields"); + + /* Each of "fields" must be non-NULL and of the correct type. */ + for (size_t i = 0; i < num_fields; i++) + { + RETURN_NULL_IF_FAIL_PRINTF1 ( + fields[i], ctxt, loc, "NULL fields[%zi]", i); + gcc::jit::recording::type *field_type + = struct_type->get_fields ()->get_field (i)->get_type (); + RETURN_NULL_IF_FAIL_PRINTF4 ( + compatible_types (field_type, + fields[i]->get_type ()), + ctxt, loc, + "mismatching type for field[%zi] (expected type: %s): %s (type: %s)", + i, + field_type->get_debug_string (), + fields[i]->get_debug_string (), + fields[i]->get_type ()->get_debug_string ()); + RETURN_NULL_IF_FAIL_PRINTF2 (fields[i]->is_constant (), ctxt, NULL, + "fields[%ld] is not a constant: %s", i, + fields[i]->get_debug_string ()); + } + + return (gcc_jit_rvalue *)ctxt->new_rvalue_from_struct (loc, struct_type, + (gcc::jit::recording::rvalue **)fields); +} + +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::context::new_rvalue_from_array method in + jit-recording.c. */ + +gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_array (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *array_type, + size_t num_elements, + gcc_jit_rvalue **elements) +{ + RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context"); + JIT_LOG_FUNC (ctxt->get_logger ()); + + /* LOC can be NULL. */ + RETURN_NULL_IF_FAIL (array_type, ctxt, loc, "NULL type"); + + /* "array_type" must be an array type. */ + gcc::jit::recording::array_type *as_array_type + = array_type->dyn_cast_array_type (); + RETURN_NULL_IF_FAIL_PRINTF1 (as_array_type, ctxt, loc, + "%s is not an array type", + array_type->get_debug_string ()); + + /* "num_elements" must match. */ + RETURN_NULL_IF_FAIL_PRINTF1 ( + num_elements == (size_t) as_array_type->num_elements (), ctxt, loc, + "num_elements != %d", as_array_type->num_elements ()); + + /* "elements must be non-NULL. */ + RETURN_NULL_IF_FAIL (elements, ctxt, loc, "NULL elements"); + + /* Each of "elements" must be non-NULL and of the correct type. */ + gcc::jit::recording::type *element_type + = as_array_type->is_array (); + for (size_t i = 0; i < num_elements; i++) + { + RETURN_NULL_IF_FAIL_PRINTF1 ( + elements[i], ctxt, loc, "NULL elements[%zi]", i); + RETURN_NULL_IF_FAIL_PRINTF4 ( + compatible_types (element_type, + elements[i]->get_type ()), + ctxt, loc, + "mismatching type for array[%zi] (expected type: %s): %s (type: %s)", + i, + element_type->get_debug_string (), + elements[i]->get_debug_string (), + elements[i]->get_type ()->get_debug_string ()); + } + + return (gcc_jit_rvalue *)ctxt->new_rvalue_from_array (loc, as_array_type, + (gcc::jit::recording::rvalue **)elements); +} + /* 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 a1c9436c545..ee959d83c67 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -827,6 +827,19 @@ gcc_jit_global_set_initializer (gcc_jit_lvalue *global, const void *blob, size_t num_bytes); +#define LIBGCCJIT_HAVE_GLOBAL_INITIALIZER_VALUE + +/* Set an initial value for a global, which must be a constant. + + This API entrypoint was added in LIBGCCJIT_ABI_21; you can test for its + presence using + #ifdef LIBGCCJIT_HAVE_GLOBAL_INITIALIZER_VALUE +*/ + +extern void +gcc_jit_global_set_initializer_value (gcc_jit_lvalue *global, + gcc_jit_rvalue *value); + /* Upcasting. */ extern gcc_jit_object * gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue); @@ -880,6 +893,20 @@ extern gcc_jit_rvalue * gcc_jit_context_new_string_literal (gcc_jit_context *ctxt, const char *value); +extern gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_struct (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_struct *struct_type, + size_t num_fields, + gcc_jit_rvalue **fields); + +extern gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_array (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *array_type, + size_t num_elements, + gcc_jit_rvalue **elements); + enum gcc_jit_unary_op { /* Negate an arithmetic value; analogous to: diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index 64e790949e8..84059261845 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -226,3 +226,22 @@ LIBGCCJIT_ABI_16 { gcc_jit_type_is_struct; gcc_jit_struct_get_field_count; } LIBGCCJIT_ABI_15; + +LIBGCCJIT_ABI_17 { +} LIBGCCJIT_ABI_16; + +LIBGCCJIT_ABI_18 { +} LIBGCCJIT_ABI_17; + +LIBGCCJIT_ABI_19 { +} LIBGCCJIT_ABI_18; + +LIBGCCJIT_ABI_20 { +} LIBGCCJIT_ABI_19; + +LIBGCCJIT_ABI_21 { + global: + gcc_jit_global_set_initializer_value; + gcc_jit_context_new_rvalue_from_struct; + gcc_jit_context_new_rvalue_from_array; +} LIBGCCJIT_ABI_20; diff --git a/gcc/testsuite/jit.dg/test-error-imported-global-initializer.c b/gcc/testsuite/jit.dg/test-error-imported-global-initializer.c new file mode 100644 index 00000000000..cfa3657e420 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-error-imported-global-initializer.c @@ -0,0 +1,40 @@ +#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 import a global variable and set an initial value + and verify that the API complains about setting a value to an + imported variable. + */ + gcc_jit_type *unsigned_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_UNSIGNED_INT); + + gcc_jit_rvalue *forty_two = gcc_jit_context_new_rvalue_from_int ( + ctxt, unsigned_type, 42); + + gcc_jit_lvalue *glob = + gcc_jit_context_new_global ( + ctxt, NULL, GCC_JIT_GLOBAL_IMPORTED, + unsigned_type, + "integer"); + gcc_jit_global_set_initializer_value (glob, forty_two); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + void *glob = gcc_jit_result_get_global (result, "integer"); + CHECK_VALUE (glob, NULL); + + /* Verify that the correct error message was emitted. */ + CHECK_STRING_VALUE (gcc_jit_context_get_last_error (ctxt), + "gcc_jit_global_set_initializer_value: " + "global \"integer\" cannot be of imported kind"); +} + diff --git a/gcc/testsuite/jit.dg/test-global-set-initializer.c b/gcc/testsuite/jit.dg/test-global-set-initializer.c index d38aba7d73f..2bbf72664ea 100644 --- a/gcc/testsuite/jit.dg/test-global-set-initializer.c +++ b/gcc/testsuite/jit.dg/test-global-set-initializer.c @@ -13,6 +13,11 @@ static signed char test_blob1[] = { 0xc, 0xa, 0xf, 0xf, 0xe }; static unsigned test_blob2[] = { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 }; static unsigned char test_blob3[BIG_BLOB_SIZE]; +struct struct_of_integers { + int a; + int b; +}; + void create_code (gcc_jit_context *ctxt, void *user_data) { @@ -21,6 +26,13 @@ create_code (gcc_jit_context *ctxt, void *user_data) signed char bin_blob1[] = { 0xc, 0xa, 0xf, 0xf, 0xe }; unsigned bin_blob2[] = { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 }; unsigned char bin_blob3[4096]... + unsigned int integer = 42; + unsigned int integers[] = {42, 43, 45}; + struct struct_of_integers { + int a; + int b; + }; + struct struct_of_integers integer_struct = {44, 40}; */ gcc_jit_type *unsigned_char_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_UNSIGNED_CHAR); @@ -56,6 +68,58 @@ create_code (gcc_jit_context *ctxt, void *user_data) sizeof (test_blob3)), "bin_blob3"); gcc_jit_global_set_initializer (glob, test_blob3, sizeof (test_blob3)); + + gcc_jit_rvalue *forty_two = gcc_jit_context_new_rvalue_from_int ( + ctxt, unsigned_type, 42); + + glob = + gcc_jit_context_new_global ( + ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, + unsigned_type, + "integer"); + gcc_jit_global_set_initializer_value (glob, forty_two); + + gcc_jit_type *array_type = gcc_jit_context_new_array_type ( + ctxt, NULL, unsigned_type, 3); + + gcc_jit_rvalue *elements[3] = { + forty_two, + gcc_jit_context_new_rvalue_from_int ( ctxt, unsigned_type, 43), + gcc_jit_context_new_rvalue_from_int ( ctxt, unsigned_type, 45), + }; + + gcc_jit_rvalue *integers = gcc_jit_context_new_rvalue_from_array ( + ctxt, NULL, array_type, 3, elements); + + glob = + gcc_jit_context_new_global ( + ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, + array_type, + "integers"); + gcc_jit_global_set_initializer_value (glob, integers); + + gcc_jit_field *fields[2] = { + gcc_jit_context_new_field (ctxt, NULL, unsigned_type, "a"), + gcc_jit_context_new_field (ctxt, NULL, unsigned_type, "b"), + }; + + gcc_jit_struct *struct_type = gcc_jit_context_new_struct_type ( + ctxt, NULL, "struct_of_integers", 2, fields); + + gcc_jit_rvalue *struct_elements[2] = { + gcc_jit_context_new_rvalue_from_int ( ctxt, unsigned_type, 44), + gcc_jit_context_new_rvalue_from_int ( ctxt, unsigned_type, 40), + }; + + gcc_jit_rvalue *integer_fields = gcc_jit_context_new_rvalue_from_struct ( + ctxt, NULL, struct_type, 2, struct_elements); + + glob = + gcc_jit_context_new_global ( + ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, + gcc_jit_struct_as_type (struct_type), + "integer_struct"); + gcc_jit_global_set_initializer_value (glob, integer_fields); } void @@ -75,4 +139,21 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) CHECK_NON_NULL (glob); CHECK_VALUE (memcmp (test_blob3, glob, sizeof (test_blob3)), 0); + glob = gcc_jit_result_get_global (result, "integer"); + CHECK_NON_NULL (glob); + int *value = glob; + CHECK_VALUE (*value, 42); + + glob = gcc_jit_result_get_global (result, "integers"); + CHECK_NON_NULL (glob); + int *values = glob; + CHECK_VALUE (values[0], 42); + CHECK_VALUE (values[1], 43); + CHECK_VALUE (values[2], 45); + + glob = gcc_jit_result_get_global (result, "integer_struct"); + CHECK_NON_NULL (glob); + struct struct_of_integers *struct_values = glob; + CHECK_VALUE (struct_values->a, 44); + CHECK_VALUE (struct_values->b, 40); } -- 2.26.2.7.g19db9cfb68.dirty