[PATCH V2] libgccjit: Add new gcc_jit_context_new_blob entry point

Andrea Corallo andrea.corallo@arm.com
Wed Aug 19 07:17:05 GMT 2020


David Malcolm <dmalcolm@redhat.com> writes:

> Thanks for the updated patch.  Comments inline below.

Hi Dave,

sorry for the late reply.

> [...]
>
>> diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst
>> index d783ceea51a8..7699dcfd27be 100644
>> --- a/gcc/jit/docs/topics/expressions.rst
>> +++ b/gcc/jit/docs/topics/expressions.rst
>> @@ -582,6 +582,27 @@ Global variables
>>        referring to it.  Analogous to using an "extern" global from a
>>        header file.
>>
>> +.. function:: gcc_jit_lvalue *\
>> +              gcc_jit_global_set_initializer (gcc_jit_lvalue *global,\
>> +                                              const void *blob,\
>> +                                              size_t num_bytes)
>> +
>> +   Set an initializer for an object using the memory content pointed
>> +   by ``blob`` for ``num_bytes``.  ``global`` must be an arrays of an
>
> Typo: "arrays" -> "array"

Fixed

>> +   integral type.
>
> Why the non-void return type?  Looking at libgccjit.c I see it returns
> "global" if it succeeds, or NULL if it fails.  Wouldn't it be better to
> simply have void return type, and rely on the normaly error-handling
> mechanisms?
> Or is this inspired by the inline asm patch? (for PR 87291)

The idea is that this way the user could also create the global,
initialize it and directly use it like:

foo (gcc_jit_global_set_initializer (gcc_jit_context_new_global (...), ...))

I left it that way, let me know if you prefer otherwise.

[...]
>
>> --- a/gcc/jit/jit-playback.h
>> +++ b/gcc/jit/jit-playback.h
>> @@ -111,6 +111,15 @@ public:
>>  	      type *type,
>>  	      const char *name);
>>
>> +  lvalue *
>> +  new_global_initialized (location *loc,
>> +                          enum gcc_jit_global_kind kind,
>> +                          type *type,
>> +                          size_t element_size,
>> +                          size_t initializer_num_elem,
>> +                          const void *initializer,
>> +                          const char *name);
>> +
>>    template <typename HOST_TYPE>
>>    rvalue *
>>    new_rvalue_from_const (type *type,
>> @@ -266,6 +275,14 @@ private:
>>    const char * get_path_s_file () const;
>>    const char * get_path_so_file () const;
>>
>> +  tree
>> +  global_new_decl (location *loc,
>> +                   enum gcc_jit_global_kind kind,
>> +                   type *type,
>> +                   const char *name);
>> +  lvalue *
>> +  global_finalize_lvalue (tree inner);
>> +
>>  private:
>>
>>    /* Functions for implementing "compile".  */
>> diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
>> index 0fddf04da873..52fc92f5928c 100644
>> --- a/gcc/jit/jit-playback.c
>> +++ b/gcc/jit/jit-playback.c
>> @@ -510,14 +510,14 @@ new_function (location *loc,
>>    return func;
>>  }
>>
>> -/* Construct a playback::lvalue instance (wrapping a tree).  */
>> +/* In use by new_global and new_global_initialized.  */
>>
>> -playback::lvalue *
>> +tree
>>  playback::context::
>> -new_global (location *loc,
>> -	    enum gcc_jit_global_kind kind,
>> -	    type *type,
>> -	    const char *name)
>> +global_new_decl (location *loc,
>> +		 enum gcc_jit_global_kind kind,
>> +		 type *type,
>> +		 const char *name)
>>  {
>>    gcc_assert (type);
>>    gcc_assert (name);
>> @@ -547,6 +547,15 @@ new_global (location *loc,
>>    if (loc)
>>      set_tree_location (inner, loc);
>>
>> +  return inner;
>> +}
>> +
>> +/* In use by new_global and new_global_initialized.  */
>> +
>> +playback::lvalue *
>> +playback::context::
>> +global_finalize_lvalue (tree inner)
>> +{
>>    varpool_node::get_create (inner);
>>
>>    varpool_node::finalize_decl (inner);
>> @@ -556,6 +565,90 @@ new_global (location *loc,
>>    return new lvalue (this, inner);
>>  }
>>
>> +/* Construct a playback::lvalue instance (wrapping a tree).  */
>> +
>> +playback::lvalue *
>> +playback::context::
>> +new_global (location *loc,
>> +	    enum gcc_jit_global_kind kind,
>> +	    type *type,
>> +	    const char *name)
>> +{
>> +  tree inner = global_new_decl (loc, kind, type, name);
>> +
>> +  return global_finalize_lvalue (inner);
>> +}
>> +
>> +/* Fill 'constructor_elements' with the memory content of
>> +   'initializer'.  Each element of the initializer is of the size of
>> +   type T.  In use by new_global_initialized.*/
>> +
>> +template<typename T>
>> +static void
>> +load_blob_in_ctor (vec<constructor_elt, va_gc> *&constructor_elements,
>> +		   size_t num_elem,
>> +		   const void *initializer)
>> +{
>> +  /* Loosely based on 'output_init_element' c-typeck.c:9691.  */
>> +  const T *p = (const T *)initializer;
>> +  tree node = make_unsigned_type (BITS_PER_UNIT * sizeof (T));
>> +  for (size_t i = 0; i < num_elem; i++)
>> +    {
>> +      constructor_elt celt =
>> +	{ build_int_cst (long_unsigned_type_node, i),
>> +	  build_int_cst (node, p[i]) };
>> +      vec_safe_push (constructor_elements, celt);
>> +    }
>> +}
>> +
>> +/* Construct an initialized playback::lvalue instance (wrapping a
>> +   tree).  */
>> +
>> +playback::lvalue *
>> +playback::context::
>> +new_global_initialized (location *loc,
>> +			enum gcc_jit_global_kind kind,
>> +			type *type,
>> +                        size_t element_size,
>> +			size_t initializer_num_elem,
>> +			const void *initializer,
>> +			const char *name)
>> +{
>> +  tree inner = global_new_decl (loc, kind, type, name);
>> +
>> +  static vec<constructor_elt, va_gc> *constructor_elements;
>
> Why the use of a function-level static variable here, and why va_gc?
> Wouldn't an auto_vec<constructor_elt> be cleaner?
> Ah: is it because of the call to build_constructor?
> If so, can the "static" be removed and replaced with an "= NULL;"
> initializer?
>
> (I'm very wary of function-level static variables, as they're a place
> when state can "hide" between multiple in-process invocations of the
> compiler)

Sorry that's due to my ignorance on how GCC GC works, I thought GC roots
had to be static, fixed.

[...]

>>  private:
>>    string * make_debug_string () FINAL OVERRIDE { return m_name; }
>> +  template <typename T>
>> +  void write_initializer_reproducer (const char *id, reproducer &r);
>>    void write_reproducer (reproducer &r) FINAL OVERRIDE;
>>    enum precedence get_precedence () const FINAL OVERRIDE
>>    {
>> @@ -1346,6 +1370,8 @@ private:
>>  private:
>>    enum gcc_jit_global_kind m_kind;
>>    string *m_name;
>> +  void *m_initializer = NULL;
>> +  size_t m_initializer_num_bytes = 0;
>>  };
>
> Are we still targetting C++98, and are these initializers compatible
> with it?

Apparently C++03 but AFAIU this should be compatible only with C++11 on,
moved the zeroing into the ctor.

>>  template <typename HOST_TYPE>
>> diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
>> index b73cd76a0a02..f97de00f63c3 100644
>> --- a/gcc/jit/jit-recording.c
>> +++ b/gcc/jit/jit-recording.c
>
> [...]
>
>> @@ -4440,9 +4510,26 @@ recording::global::write_to_dump (dump &d)
>>        d.write ("extern ");
>>        break;
>>      }
>> -  d.write ("%s %s;\n",
>> +
>> +  d.write ("%s %s",
>>  	   m_type->get_debug_string (),
>>  	   get_debug_string ());
>> +
>> +  if (!m_initializer)
>> +    {
>> +      d.write (";\n");
>> +      return;
>> +    }
>> +
>> +  d.write ("=\n  { ");
>> +  const char *p = (const char *)m_initializer;
>
> Should be "const unsigned char"?

Probably better, fixed.

>> +  for (size_t i = 0; i < m_initializer_num_bytes; i++)
>> +    {
>> +      d.write ("0x%x, ", p[i]);
>> +      if (i && !(i % 64))
>> +	d.write ("\n    ");
>> +    }
>> +  d.write ("};\n");
>>  }
>>
>>  /* A table of enum gcc_jit_global_kind values expressed in string
>> @@ -4454,6 +4541,27 @@ static const char * const global_kind_reproducer_strings[] = {
>>    "GCC_JIT_GLOBAL_IMPORTED"
>>  };

[...]

>
> Thanks again for the patch; hope this is constructive
> Dave

Sure it is, thanks for reviewing.

Attached the updated version of the patch.

make check-jit is clean plus I tested the new entry point with the
modified Emacs.

Thanks

  Andrea

-------------- next part --------------
>From 74970a5ac988e8a641967dee39ec8fa711228a91 Mon Sep 17 00:00:00 2001
From: Andrea Corallo <andrea.corallo@arm.com>
Date: Sat, 30 May 2020 10:33:08 +0100
Subject: [PATCH] Add new gcc_jit_global_set_initializer entry point

gcc/jit/ChangeLog

2020-08-01  Andrea Corallo  <andrea.corallo@arm.com>

	* docs/topics/compatibility.rst (LIBGCCJIT_ABI_14): New ABI tag.
	* docs/topics/expressions.rst (gcc_jit_global_set_initializer):
	Document new entry point in section 'Global variables'.
	* jit-playback.c (global_new_decl, global_finalize_lvalue): New
	method.
	(playback::context::new_global): Make use of global_new_decl,
	global_finalize_lvalue.
	(load_blob_in_ctor): New template function in use by the
	following.
	(playback::context::new_global_initialized): New method.
	* jit-playback.h (class context): Decl 'new_global_initialized',
	'global_new_decl', 'global_finalize_lvalue'.
	(lvalue::set_initializer): Add implementation.
	* jit-recording.c (recording::memento_of_get_pointer::get_size)
	(recording::memento_of_get_type::get_size): Add implementation.
	(recording::global::write_initializer_reproducer): New function in
	use by 'recording::global::write_reproducer'.
	(recording::global::replay_into)
	(recording::global::write_to_dump)
	(recording::global::write_reproducer): Handle
	initialized case.
	* jit-recording.h (class type): Decl 'get_size' and
	'num_elements'.
	* libgccjit++.h (class lvalue): Declare new 'set_initializer'
	method.
	(class lvalue): Decl 'is_global' and 'set_initializer'.
	(class global) Decl 'write_initializer_reproducer'. Add
	'm_initializer', 'm_initializer_num_bytes' fields.  Implement
	'set_initializer'. Add a destructor to free 'm_initializer'.
	* libgccjit.c (gcc_jit_global_set_initializer): New function.
	* libgccjit.h (gcc_jit_global_set_initializer): New function
	declaration.
	* libgccjit.map (LIBGCCJIT_ABI_14): New ABI tag.

gcc/testsuite/ChangeLog

2020-08-01  Andrea Corallo  <andrea.corallo@arm.com>

	* jit.dg/all-non-failing-tests.h: Add test-blob.c.
	* jit.dg/test-global-set-initializer.c: New testcase.
---
 gcc/jit/docs/topics/compatibility.rst         |   7 +
 gcc/jit/docs/topics/expressions.rst           |  21 +++
 gcc/jit/jit-playback.c                        | 105 +++++++++++++-
 gcc/jit/jit-playback.h                        |  17 +++
 gcc/jit/jit-recording.c                       | 137 +++++++++++++++++-
 gcc/jit/jit-recording.h                       |  38 ++++-
 gcc/jit/libgccjit++.h                         |  10 ++
 gcc/jit/libgccjit.c                           |  38 +++++
 gcc/jit/libgccjit.h                           |  14 ++
 gcc/jit/libgccjit.map                         |   7 +-
 gcc/testsuite/jit.dg/all-non-failing-tests.h  |   7 +
 .../jit.dg/test-global-set-initializer.c      |  78 ++++++++++
 12 files changed, 466 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-global-set-initializer.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index bb3387fa583..6bfa101ed71 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -219,3 +219,10 @@ entrypoints:
   * :func:`gcc_jit_version_minor`
 
   * :func:`gcc_jit_version_patchlevel`
+
+.. _LIBGCCJIT_ABI_14:
+
+``LIBGCCJIT_ABI_14``
+--------------------
+``LIBGCCJIT_ABI_14`` covers the addition of
+:func:`gcc_jit_global_set_initializer`
diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst
index d783ceea51a..28f81be0060 100644
--- a/gcc/jit/docs/topics/expressions.rst
+++ b/gcc/jit/docs/topics/expressions.rst
@@ -582,6 +582,27 @@ Global variables
       referring to it.  Analogous to using an "extern" global from a
       header file.
 
+.. function:: gcc_jit_lvalue *\
+              gcc_jit_global_set_initializer (gcc_jit_lvalue *global,\
+                                              const void *blob,\
+                                              size_t num_bytes)
+
+   Set an initializer for an object using the memory content pointed
+   by ``blob`` for ``num_bytes``.  ``global`` must be an array of an
+   integral type.
+
+   The parameter ``blob`` must be non-NULL. The call copies the memory
+   pointed by ``blob`` for ``num_bytes`` bytes, so it is valid to pass
+   in a pointer to an on-stack buffer.  The content will be stored in
+   the compilation unit and used as initialization value of the array.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_14`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_global_set_initializer
+
 Working with pointers, structs and unions
 -----------------------------------------
 
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 0fddf04da87..b68e18278e3 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -510,14 +510,14 @@ new_function (location *loc,
   return func;
 }
 
-/* Construct a playback::lvalue instance (wrapping a tree).  */
+/* In use by new_global and new_global_initialized.  */
 
-playback::lvalue *
+tree
 playback::context::
-new_global (location *loc,
-	    enum gcc_jit_global_kind kind,
-	    type *type,
-	    const char *name)
+global_new_decl (location *loc,
+		 enum gcc_jit_global_kind kind,
+		 type *type,
+		 const char *name)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -547,6 +547,15 @@ new_global (location *loc,
   if (loc)
     set_tree_location (inner, loc);
 
+  return inner;
+}
+
+/* In use by new_global and new_global_initialized.  */
+
+playback::lvalue *
+playback::context::
+global_finalize_lvalue (tree inner)
+{
   varpool_node::get_create (inner);
 
   varpool_node::finalize_decl (inner);
@@ -556,6 +565,90 @@ new_global (location *loc,
   return new lvalue (this, inner);
 }
 
+/* Construct a playback::lvalue instance (wrapping a tree).  */
+
+playback::lvalue *
+playback::context::
+new_global (location *loc,
+	    enum gcc_jit_global_kind kind,
+	    type *type,
+	    const char *name)
+{
+  tree inner = global_new_decl (loc, kind, type, name);
+
+  return global_finalize_lvalue (inner);
+}
+
+/* Fill 'constructor_elements' with the memory content of
+   'initializer'.  Each element of the initializer is of the size of
+   type T.  In use by new_global_initialized.*/
+
+template<typename T>
+static void
+load_blob_in_ctor (vec<constructor_elt, va_gc> *&constructor_elements,
+		   size_t num_elem,
+		   const void *initializer)
+{
+  /* Loosely based on 'output_init_element' c-typeck.c:9691.  */
+  const T *p = (const T *)initializer;
+  tree node = make_unsigned_type (BITS_PER_UNIT * sizeof (T));
+  for (size_t i = 0; i < num_elem; i++)
+    {
+      constructor_elt celt =
+	{ build_int_cst (long_unsigned_type_node, i),
+	  build_int_cst (node, p[i]) };
+      vec_safe_push (constructor_elements, celt);
+    }
+}
+
+/* Construct an initialized playback::lvalue instance (wrapping a
+   tree).  */
+
+playback::lvalue *
+playback::context::
+new_global_initialized (location *loc,
+			enum gcc_jit_global_kind kind,
+			type *type,
+                        size_t element_size,
+			size_t initializer_num_elem,
+			const void *initializer,
+			const char *name)
+{
+  tree inner = global_new_decl (loc, kind, type, name);
+
+  vec<constructor_elt, va_gc> *constructor_elements = NULL;
+
+  switch (element_size)
+    {
+    case 1:
+      load_blob_in_ctor<uint8_t> (constructor_elements, initializer_num_elem,
+				  initializer);
+      break;
+    case 2:
+      load_blob_in_ctor<uint16_t> (constructor_elements, initializer_num_elem,
+				   initializer);
+      break;
+    case 4:
+      load_blob_in_ctor<uint32_t> (constructor_elements, initializer_num_elem,
+				   initializer);
+      break;
+    case 8:
+      load_blob_in_ctor<uint64_t> (constructor_elements, initializer_num_elem,
+				   initializer);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  /* Compare with 'pop_init_level' c-typeck.c:8780.  */
+  tree ctor = build_constructor (type->as_tree (), constructor_elements);
+  constructor_elements = NULL;
+
+  /* Compare with 'store_init_value' c-typeck.c:7555.  */
+  DECL_INITIAL (inner) = ctor;
+
+  return global_finalize_lvalue (inner);
+}
+
 /* Implementation of the various
       gcc::jit::playback::context::new_rvalue_from_const <HOST_TYPE>
    methods.
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index f9b3e675368..50b69753bb4 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -111,6 +111,15 @@ public:
 	      type *type,
 	      const char *name);
 
+  lvalue *
+  new_global_initialized (location *loc,
+                          enum gcc_jit_global_kind kind,
+                          type *type,
+                          size_t element_size,
+                          size_t initializer_num_elem,
+                          const void *initializer,
+                          const char *name);
+
   template <typename HOST_TYPE>
   rvalue *
   new_rvalue_from_const (type *type,
@@ -266,6 +275,14 @@ private:
   const char * get_path_s_file () const;
   const char * get_path_so_file () const;
 
+  tree
+  global_new_decl (location *loc,
+                   enum gcc_jit_global_kind kind,
+                   type *type,
+                   const char *name);
+  lvalue *
+  global_finalize_lvalue (tree inner);
+
 private:
 
   /* Functions for implementing "compile".  */
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index b73cd76a0a0..39510097c6f 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -2175,6 +2175,57 @@ recording::type::access_as_type (reproducer &r)
   return r.get_identifier (this);
 }
 
+/* Override of default implementation of
+   recording::type::get_size.
+
+   Return the size in bytes.  This is in use for global
+   initialization.  */
+
+size_t
+recording::memento_of_get_type::get_size ()
+{
+  int size;
+  switch (m_kind)
+    {
+    case GCC_JIT_TYPE_VOID:
+      return 0;
+    case GCC_JIT_TYPE_BOOL:
+    case GCC_JIT_TYPE_CHAR:
+    case GCC_JIT_TYPE_SIGNED_CHAR:
+    case GCC_JIT_TYPE_UNSIGNED_CHAR:
+      return 1;
+    case GCC_JIT_TYPE_SHORT:
+    case GCC_JIT_TYPE_UNSIGNED_SHORT:
+      size = SHORT_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_INT:
+    case GCC_JIT_TYPE_UNSIGNED_INT:
+      size = INT_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_LONG:
+    case GCC_JIT_TYPE_UNSIGNED_LONG:
+      size = LONG_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_LONG_LONG:
+    case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
+      size = LONG_LONG_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_FLOAT:
+      size = FLOAT_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_DOUBLE:
+      size = DOUBLE_TYPE_SIZE;
+      break;
+    case GCC_JIT_TYPE_LONG_DOUBLE:
+      size = LONG_DOUBLE_TYPE_SIZE;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  return size / BITS_PER_UNIT;
+}
+
 /* Implementation of pure virtual hook recording::type::dereference for
    recording::memento_of_get_type.  */
 
@@ -2482,6 +2533,15 @@ recording::memento_of_get_type::write_reproducer (reproducer &r)
 
 /* The implementation of class gcc::jit::recording::memento_of_get_pointer.  */
 
+/* Override of default implementation of
+   recording::type::get_size for get_pointer.  */
+
+size_t
+recording::memento_of_get_pointer::get_size ()
+{
+  return POINTER_SIZE / BITS_PER_UNIT;
+}
+
 /* Override of default implementation of
    recording::type::accepts_writes_from for get_pointer.
 
@@ -4393,10 +4453,20 @@ recording::block::dump_edges_to_dot (pretty_printer *pp)
 void
 recording::global::replay_into (replayer *r)
 {
-  set_playback_obj (r->new_global (playback_location (r, m_loc),
-				   m_kind,
-				   m_type->playback_type (),
-				   playback_string (m_name)));
+  set_playback_obj (
+    m_initializer
+    ? 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),
+		     m_kind,
+		     m_type->playback_type (),
+		     playback_string (m_name)));
 }
 
 /* Override the default implementation of
@@ -4440,9 +4510,26 @@ recording::global::write_to_dump (dump &d)
       d.write ("extern ");
       break;
     }
-  d.write ("%s %s;\n",
+
+  d.write ("%s %s",
 	   m_type->get_debug_string (),
 	   get_debug_string ());
+
+  if (!m_initializer)
+    {
+      d.write (";\n");
+      return;
+    }
+
+  d.write ("=\n  { ");
+  const unsigned char *p = (const unsigned char *)m_initializer;
+  for (size_t i = 0; i < m_initializer_num_bytes; i++)
+    {
+      d.write ("0x%x, ", p[i]);
+      if (i && !(i % 64))
+	d.write ("\n    ");
+    }
+  d.write ("};\n");
 }
 
 /* A table of enum gcc_jit_global_kind values expressed in string
@@ -4454,6 +4541,27 @@ static const char * const global_kind_reproducer_strings[] = {
   "GCC_JIT_GLOBAL_IMPORTED"
 };
 
+template <typename T>
+void
+recording::global::write_initializer_reproducer (const char *id, reproducer &r)
+{
+  const char *init_id = r.make_tmp_identifier ("init_for", this);
+  r.write ("  %s %s[] =\n    {",
+	   m_type->dereference ()->get_debug_string (),
+	   init_id);
+
+  const T *p = (const T *)m_initializer;
+  for (size_t i = 0; i < m_initializer_num_bytes / sizeof (T); i++)
+    {
+      r.write ("%" PRIu64 ", ", (uint64_t)p[i]);
+      if (i && !(i % 64))
+	r.write ("\n    ");
+    }
+  r.write ("};\n");
+  r.write ("  gcc_jit_global_set_initializer (%s, %s, sizeof (%s));\n",
+	   id, init_id, init_id);
+}
+
 /* Implementation of recording::memento::write_reproducer for globals. */
 
 void
@@ -4472,6 +4580,25 @@ recording::global::write_reproducer (reproducer &r)
     global_kind_reproducer_strings[m_kind],
     r.get_identifier_as_type (get_type ()),
     m_name->get_debug_string ());
+
+  if (m_initializer)
+    switch (m_type->dereference ()->get_size ())
+      {
+      case 1:
+	write_initializer_reproducer<uint8_t> (id, r);
+	break;
+      case 2:
+	write_initializer_reproducer<uint16_t> (id, r);
+	break;
+      case 4:
+	write_initializer_reproducer<uint32_t> (id, r);
+	break;
+      case 8:
+	write_initializer_reproducer<uint64_t> (id, r);
+	break;
+      default:
+	gcc_unreachable ();
+      }
 }
 
 /* The implementation of the various const-handling classes:
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 726b9c4b837..aa38e606a8d 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -502,6 +502,12 @@ public:
      This will return NULL if it's not valid to dereference this type.
      The caller is responsible for setting an error.  */
   virtual type *dereference () = 0;
+  /* Get the type size in bytes.
+
+     This is implemented only for memento_of_get_type and
+     memento_of_get_pointer as is use for initializing globals of
+     these types.  */
+  virtual size_t get_size () { gcc_unreachable (); }
 
   /* Dynamic casts.  */
   virtual function_type *dyn_cast_function_type () { return NULL; }
@@ -569,6 +575,8 @@ public:
 
   type *dereference () FINAL OVERRIDE;
 
+  size_t get_size () FINAL OVERRIDE;
+
   bool accepts_writes_from (type *rtype) FINAL OVERRIDE
   {
     if (m_kind == GCC_JIT_TYPE_VOID_PTR)
@@ -610,6 +618,8 @@ public:
 
   type *dereference () FINAL OVERRIDE { return m_other_type; }
 
+  size_t get_size () FINAL OVERRIDE;
+
   bool accepts_writes_from (type *rtype) FINAL OVERRIDE;
 
   void replay_into (replayer *r) FINAL OVERRIDE;
@@ -755,6 +765,7 @@ class array_type : public type
   bool is_bool () const FINAL OVERRIDE { return false; }
   type *is_pointer () FINAL OVERRIDE { return NULL; }
   type *is_array () FINAL OVERRIDE { return m_element_type; }
+  int num_elements () { return m_num_elements; }
 
   void replay_into (replayer *) FINAL OVERRIDE;
 
@@ -1107,6 +1118,7 @@ public:
 
   const char *access_as_rvalue (reproducer &r) OVERRIDE;
   virtual const char *access_as_lvalue (reproducer &r);
+  virtual bool is_global () const { return false; }
 };
 
 class param : public lvalue
@@ -1327,7 +1339,14 @@ public:
   : lvalue (ctxt, loc, type),
     m_kind (kind),
     m_name (name)
-  {}
+  {
+    m_initializer = NULL;
+    m_initializer_num_bytes = 0;
+  }
+  ~global ()
+  {
+    free (m_initializer);
+  }
 
   void replay_into (replayer *) FINAL OVERRIDE;
 
@@ -1335,8 +1354,23 @@ public:
 
   void write_to_dump (dump &d) FINAL OVERRIDE;
 
+  bool is_global () const FINAL OVERRIDE { return true; }
+
+  void
+  set_initializer (const void *initializer,
+                   size_t num_bytes)
+  {
+    if (m_initializer)
+      free (m_initializer);
+    m_initializer = xmalloc (num_bytes);
+    memcpy (m_initializer, initializer, num_bytes);
+    m_initializer_num_bytes = num_bytes;
+  }
+
 private:
   string * make_debug_string () FINAL OVERRIDE { return m_name; }
+  template <typename T>
+  void write_initializer_reproducer (const char *id, reproducer &r);
   void write_reproducer (reproducer &r) FINAL OVERRIDE;
   enum precedence get_precedence () const FINAL OVERRIDE
   {
@@ -1346,6 +1380,8 @@ private:
 private:
   enum gcc_jit_global_kind m_kind;
   string *m_name;
+  void *m_initializer;
+  size_t m_initializer_num_bytes;
 };
 
 template <typename HOST_TYPE>
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 69e67766640..1b9ef1a5db9 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -488,6 +488,7 @@ namespace gccjit
 			 location loc = location ());
 
     rvalue get_address (location loc = location ());
+    lvalue set_initializer (const void *blob, size_t num_bytes);
   };
 
   class param : public lvalue
@@ -1737,6 +1738,15 @@ lvalue::get_address (location loc)
 					     loc.get_inner_location ()));
 }
 
+inline lvalue
+lvalue::set_initializer (const void *blob, size_t num_bytes)
+{
+  gcc_jit_global_set_initializer (get_inner_lvalue (),
+                                  blob,
+                                  num_bytes);
+  return *this;
+}
+
 // class param : public lvalue
 inline param::param () : lvalue () {}
 inline param::param (gcc_jit_param *inner)
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 3d04f6db3af..bc45bd80b2c 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -1117,6 +1117,44 @@ gcc_jit_context_new_global (gcc_jit_context *ctxt,
   return (gcc_jit_lvalue *)ctxt->new_global (loc, kind, type, name);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::global::set_initializer method, in
+   jit-recording.c.  */
+
+extern gcc_jit_lvalue *
+gcc_jit_global_set_initializer (gcc_jit_lvalue *global,
+				const void *blob,
+				size_t num_bytes)
+{
+  RETURN_NULL_IF_FAIL (global, NULL, NULL, "NULL global");
+  RETURN_NULL_IF_FAIL (blob, NULL, NULL, "NULL blob");
+  RETURN_NULL_IF_FAIL_PRINTF1 (global->is_global (), NULL, NULL,
+			       "lvalue \"%s\" not a global",
+			       global->get_debug_string ());
+
+  gcc::jit::recording::type *lval_type = global->get_type ();
+  RETURN_NULL_IF_FAIL_PRINTF1 (lval_type->is_array (), NULL, NULL,
+			       "global \"%s\" is not an array",
+			       global->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF1 (lval_type->dereference ()->is_int (), NULL, NULL,
+			       "global \"%s\" is not an array of integral type",
+			       global->get_debug_string ());
+  size_t lvalue_size =
+    lval_type->dereference ()->get_size ()
+    * static_cast <gcc::jit::recording::array_type *> (lval_type)->num_elements ();
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    lvalue_size == num_bytes, NULL, NULL,
+    "global \"%s\" of size %zu don't match initializer of size %zu",
+    global->get_debug_string (), lvalue_size, num_bytes);
+
+  reinterpret_cast <gcc::jit::recording::global *> (global)
+    ->set_initializer (blob, num_bytes);
+
+  return global;
+}
+
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, this calls the trivial
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 1c5a12e9c01..3cbcbef2693 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -788,6 +788,20 @@ gcc_jit_context_new_global (gcc_jit_context *ctxt,
 			    gcc_jit_type *type,
 			    const char *name);
 
+#define LIBGCCJIT_HAVE_gcc_jit_global_set_initializer
+
+/* Set a static initializer for a global return the global itself.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_14; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_gcc_jit_global_set_initializer
+*/
+
+extern gcc_jit_lvalue *
+gcc_jit_global_set_initializer (gcc_jit_lvalue *global,
+				const void *blob,
+				size_t num_bytes);
+
 /* Upcasting.  */
 extern gcc_jit_object *
 gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue);
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 6137dd4b4b0..a6e67e781a4 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -186,4 +186,9 @@ LIBGCCJIT_ABI_13 {
     gcc_jit_version_major;
     gcc_jit_version_minor;
     gcc_jit_version_patchlevel;
-} LIBGCCJIT_ABI_12;
\ No newline at end of file
+} LIBGCCJIT_ABI_12;
+
+LIBGCCJIT_ABI_14 {
+  global:
+    gcc_jit_global_set_initializer;
+} LIBGCCJIT_ABI_13;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 632ab8cfb2e..4202eb7798b 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -174,6 +174,13 @@
 #undef create_code
 #undef verify_code
 
+/* test-global-set-initializer.c */
+#define create_code create_code_global_set_initializer
+#define verify_code verify_code_global_set_initializer
+#include "test-global-set-initializer.c"
+#undef create_code
+#undef verify_code
+
 /* test-hello-world.c */
 #define create_code create_code_hello_world
 #define verify_code verify_code_hello_world
diff --git a/gcc/testsuite/jit.dg/test-global-set-initializer.c b/gcc/testsuite/jit.dg/test-global-set-initializer.c
new file mode 100644
index 00000000000..d38aba7d73f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-global-set-initializer.c
@@ -0,0 +1,78 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+#define BIG_BLOB_SIZE (1 << 12) /* 4KB.  */
+
+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];
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     signed char bin_blob1[] = { 0xc, 0xa, 0xf, 0xf, 0xe };
+     unsigned bin_blob2[] = { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 };
+     unsigned char bin_blob3[4096]...
+  */
+  gcc_jit_type *unsigned_char_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_UNSIGNED_CHAR);
+  gcc_jit_type *signed_char_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_SIGNED_CHAR);
+  gcc_jit_type *unsigned_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_UNSIGNED_INT);
+
+  gcc_jit_lvalue *glob =
+    gcc_jit_context_new_global (
+      ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED,
+      gcc_jit_context_new_array_type (ctxt, NULL, signed_char_type,
+				      sizeof (test_blob1)),
+      "bin_blob1");
+  gcc_jit_global_set_initializer (glob, test_blob1, sizeof (test_blob1));
+
+  glob =
+    gcc_jit_context_new_global (
+      ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED,
+      gcc_jit_context_new_array_type (
+	ctxt, NULL, unsigned_type,
+	sizeof (test_blob2) / sizeof (*test_blob2)),
+      "bin_blob2");
+  gcc_jit_global_set_initializer (glob, test_blob2,
+				  sizeof (test_blob2));
+
+  for (size_t i = 0; i < BIG_BLOB_SIZE; i++)
+    test_blob3[i] = i * i + i;
+  glob =
+    gcc_jit_context_new_global (
+      ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED,
+      gcc_jit_context_new_array_type (ctxt, NULL, unsigned_char_type,
+				      sizeof (test_blob3)),
+      "bin_blob3");
+  gcc_jit_global_set_initializer (glob, test_blob3, sizeof (test_blob3));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_NON_NULL (result);
+  void *glob = gcc_jit_result_get_global (result, "bin_blob1");
+  CHECK_NON_NULL (glob);
+  CHECK_VALUE (memcmp (test_blob1, glob, sizeof (test_blob1)), 0);
+
+  glob = gcc_jit_result_get_global (result, "bin_blob2");
+  CHECK_NON_NULL (glob);
+  CHECK_VALUE (memcmp (test_blob2, glob,
+		       sizeof (test_blob2)), 0);
+
+  glob = gcc_jit_result_get_global (result, "bin_blob3");
+  CHECK_NON_NULL (glob);
+  CHECK_VALUE (memcmp (test_blob3, glob, sizeof (test_blob3)), 0);
+
+}
-- 
2.20.1



More information about the Gcc-patches mailing list