From a095291f43ca8348b6c84f94a598895969d94d1b Mon Sep 17 00:00:00 2001 From: Antoni Boucher 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 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 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 &fields) +{ + vec *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 &elements) +{ + vec *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 rvalue * new_rvalue_from_const (type *type, @@ -133,6 +144,16 @@ public: type *type, const auto_vec &elements); + rvalue * + new_rvalue_from_struct (location *loc, + type *type, + const auto_vec &fields); + + rvalue * + new_rvalue_from_array (location *loc, + type *type, + const auto_vec &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 . */ @@ -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_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_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 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 @@ -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 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 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 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 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 (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 +#include + +#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