From: Antoni Boucher <[email protected]>

gcc/jit/ChangeLog:

        * docs/topics/compatibility.rst (LIBGCCJIT_ABI_36): New ABI tag.
        * docs/topics/types.rst: Document gcc_jit_type_add_attribute and
        gcc_jit_type_attribute.
        * dummy-frontend.cc (attr_aligned_exclusions): New variable.
        (handle_packed_attribute): New function.
        * jit-playback.cc: Handle type attributes.
        (type_attribute_to_string): New function.
        * jit-playback.h: New parameter attributes to new_compound_type.
        (gcc_jit_type_attribute): New function.
        * jit-recording.cc (recording::type::add_attribute): New method.
        (type_attribute_reproducer_strings): New variable.
        * jit-recording.h (recording::type::add_attribute): New method.
        (m_attributes): New field.
        * libgccjit.cc (gcc_jit_type_add_attribute): New function.
        * libgccjit.h (gcc_jit_type_add_attribute): New function.
        (gcc_jit_type_attribute): New enum.
        * libgccjit.map (LIBGCCJIT_ABI_36): New ABI tag.

gcc/testsuite/ChangeLog:

        * jit.dg/all-non-failing-tests.h: Mention new test.
        * jit.dg/test-packed-struct.c: New test.
---
 gcc/jit/docs/topics/compatibility.rst        |  8 +++
 gcc/jit/docs/topics/types.rst                | 31 ++++++++
 gcc/jit/dummy-frontend.cc                    | 58 +++++++++++++++
 gcc/jit/jit-playback.cc                      | 37 +++++++++-
 gcc/jit/jit-playback.h                       |  4 +-
 gcc/jit/jit-recording.cc                     | 48 +++++++++++--
 gcc/jit/jit-recording.h                      |  4 ++
 gcc/jit/libgccjit.cc                         | 13 ++++
 gcc/jit/libgccjit.h                          | 16 +++++
 gcc/jit/libgccjit.map                        |  5 ++
 gcc/testsuite/jit.dg/all-non-failing-tests.h |  3 +
 gcc/testsuite/jit.dg/test-packed-struct.c    | 74 ++++++++++++++++++++
 12 files changed, 295 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-packed-struct.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 19be33eb4b50..271edca3f2c8 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -466,3 +466,11 @@ information:
   * :func:`gcc_jit_target_info_cpu_supports`
   * :func:`gcc_jit_target_info_arch`
   * :func:`gcc_jit_target_info_supports_target_dependent_type`
+
+.. _LIBGCCJIT_ABI_36:
+
+``LIBGCCJIT_ABI_36``
+--------------------
+``LIBGCCJIT_ABI_36`` covers the addition of
+
+ * :func:`gcc_jit_type_add_attribute`
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index e699ee5301b5..84f45ab18d28 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -555,3 +555,34 @@ Reflection API
    .. code-block:: c
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
+
+Type attributes
+---------------
+
+.. function::  void \
+               gcc_jit_type_add_attribute (gcc_jit_type *type, \
+                    enum gcc_jit_type_attribute attribute)
+
+     Add an attribute ``attribute`` to a ``type``.
+
+   .. enum:: gcc_jit_type_attribute
+
+   This enum specifies the type attribute, and has the following values:
+
+      .. macro:: GCC_JIT_TYPE_ATTRIBUTE_PACKED
+
+         Packed structures or union have each of its members placed to
+         minimize the memory required.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__((packed))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_36`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_type_add_attribute
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 3ffca92903f0..5f7c40bbdfc4 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -40,6 +40,9 @@ along with GCC; see the file COPYING3.  If not see
 
 using namespace gcc::jit;
 
+#define DECL_C_BIT_FIELD(NODE) \
+  (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
+
 /* Attribute handling.  */
 
 static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
@@ -57,6 +60,7 @@ static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
 static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
 static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
@@ -76,6 +80,16 @@ static tree ignore_attribute (tree *, tree, tree, int, bool *);
 #define ATTR_EXCL(name, function, type, variable)	\
   { name, function, type, variable }
 
+/* Define attributes that are mutually exclusive with one another.  */
+extern const struct attribute_spec::exclusions attr_aligned_exclusions[] =
+{
+  /* Attribute name     exclusion applies to:
+			function, type, variable.  */
+  ATTR_EXCL ("aligned", true, false, false),
+  ATTR_EXCL ("packed", true, false, false),
+  ATTR_EXCL (NULL, false, false, false)
+};
+
 /* Define attributes that are mutually exclusive with one another.  */
 static const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
 {
@@ -187,6 +201,9 @@ static const attribute_spec jit_gnu_attributes[] =
 			      handle_nonnull_attribute, NULL },
   { "nothrow",		      0, 0, true,  false, false, false,
 			      handle_nothrow_attribute, NULL },
+  { "packed",		      0, 0, false, false, false, false,
+			      handle_packed_attribute,
+			      attr_aligned_exclusions },
   { "patchable_function_entry", 1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
@@ -454,6 +471,47 @@ handle_nothrow_attribute (tree *node, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle a "packed" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_packed_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			 int flags, bool *no_add_attrs)
+{
+  if (TYPE_P (*node))
+    {
+      if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
+	{
+	  warning (OPT_Wattributes,
+		   "%qE attribute ignored for type %qT", name, *node);
+	  *no_add_attrs = true;
+	}
+      else
+	TYPE_PACKED (*node) = 1;
+    }
+  else if (TREE_CODE (*node) == FIELD_DECL)
+    {
+      if (TYPE_ALIGN (TREE_TYPE (*node)) <= BITS_PER_UNIT
+	  /* Still pack bitfields.  */
+	  && ! DECL_C_BIT_FIELD (*node))
+	warning (OPT_Wattributes,
+		 "%qE attribute ignored for field of type %qT",
+		 name, TREE_TYPE (*node));
+      else
+	DECL_PACKED (*node) = 1;
+    }
+  /* We can't set DECL_PACKED for a VAR_DECL, because the bit is
+     used for DECL_REGISTER.  It wouldn't mean anything anyway.
+     We can't set DECL_PACKED on the type of a TYPE_DECL, because
+     that changes what the typedef is typing.  */
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
 
 /* Handle a "sentinel" attribute.  */
 
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 291ddeb2cca2..8198d92fa575 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -425,7 +425,8 @@ playback::compound_type *
 playback::context::
 new_compound_type (location *loc,
 		   const char *name,
-		   bool is_struct) /* else is union */
+		   bool is_struct, /* else is union */
+		   const std::vector<gcc_jit_type_attribute> &attributes)
 {
   gcc_assert (name);
 
@@ -435,6 +436,22 @@ new_compound_type (location *loc,
   TYPE_NAME (t) = get_identifier (name);
   TYPE_SIZE (t) = 0;
 
+  tree type_attributes = NULL_TREE;
+
+  /* All attributes need to be declared in `dummy-frontend.cc` and more
+     specifically in `jit_attribute_table`. */
+  for (auto attr: attributes)
+  {
+    const char* attribute = type_attribute_to_string (attr);
+    if (attribute)
+    {
+      tree ident = get_identifier (attribute);
+      type_attributes = tree_cons (ident, NULL_TREE, type_attributes);
+    }
+  }
+
+  decl_attributes (&t, type_attributes, ATTR_FLAG_TYPE_IN_PLACE);
+
   if (loc)
     set_tree_location (t, loc);
 
@@ -459,6 +476,12 @@ playback::compound_type::set_fields (const auto_vec<playback::field *> *fields)
 	  DECL_SIZE (x) = bitsize_int (width);
 	  DECL_BIT_FIELD (x) = 1;
 	}
+
+      if (TYPE_PACKED (t)
+	  && (DECL_BIT_FIELD (x)
+	      || TYPE_ALIGN (TREE_TYPE (x)) > BITS_PER_UNIT))
+	DECL_PACKED (x) = 1;
+
       fieldlist = chainon (x, fieldlist);
     }
   fieldlist = nreverse (fieldlist);
@@ -553,6 +576,18 @@ const char* fn_attribute_to_string (gcc_jit_fn_attribute attr)
   return NULL;
 }
 
+const char* type_attribute_to_string (gcc_jit_type_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_TYPE_ATTRIBUTE_PACKED:
+      return "packed";
+    case GCC_JIT_TYPE_ATTRIBUTE_MAX:
+      return NULL;
+  }
+  return NULL;
+}
+
 const char* variable_attribute_to_string (gcc_jit_variable_attribute attr)
 {
   switch (attr)
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 07dab6fa3895..87f43ea0024a 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -43,6 +43,7 @@ namespace jit {
 
 const char* fn_attribute_to_string (gcc_jit_fn_attribute attr);
 const char* variable_attribute_to_string (gcc_jit_variable_attribute attr);
+const char* type_attribute_to_string (gcc_jit_type_attribute attr);
 
 /**********************************************************************
  Playback.
@@ -104,7 +105,8 @@ public:
   compound_type *
   new_compound_type (location *loc,
 		     const char *name,
-		     bool is_struct); /* else is union */
+		     bool is_struct, /* else is union */
+		     const std::vector<gcc_jit_type_attribute> &attributes);
 
   type *
   new_function_type (type *return_type,
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index 2f4ecc318251..fc38d0b4afb2 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -2542,6 +2542,12 @@ recording::type::get_aligned (size_t alignment_in_bytes)
   return result;
 }
 
+void
+recording::type::add_attribute (gcc_jit_type_attribute attribute)
+{
+  m_attributes.push_back (attribute);
+}
+
 /* Given a type, get a vector version of the type.
 
    Implements the post-error-checking part of
@@ -3783,7 +3789,8 @@ recording::struct_::replay_into (replayer *r)
   set_playback_obj (
     r->new_compound_type (playback_location (r, get_loc ()),
 			  get_name ()->c_str (),
-			  true /* is_struct */));
+			  true, /* is_struct */
+			  m_attributes));
 }
 
 const char *
@@ -3799,10 +3806,23 @@ recording::struct_::access_as_type (reproducer &r)
 recording::string *
 recording::struct_::make_debug_string ()
 {
+  std::string attributes;
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = type_attribute_to_string (attr);
+    if (attribute)
+      attributes.append ("__attribute (%s)__ ", attribute);
+  }
   return string::from_printf (m_ctxt,
-			      "struct %s", get_name ()->c_str ());
+			      "struct %s %s", attributes.c_str (),
+			      get_name ()->c_str ());
 }
 
+static const char * const type_attribute_reproducer_strings[] =
+{
+  "GCC_JIT_TYPE_ATTRIBUTE_PACKED",
+};
+
 void
 recording::struct_::write_reproducer (reproducer &r)
 {
@@ -3815,6 +3835,12 @@ recording::struct_::write_reproducer (reproducer &r)
 	   r.get_identifier (get_context ()),
 	   r.get_identifier (get_loc ()),
 	   get_name ()->get_debug_string ());
+
+  for (auto attribute : m_attributes)
+    r.write ("  gcc_jit_type_add_attribute ("
+	     "gcc_jit_struct_as_type (%s), %s);\n",
+	     id,
+	     type_attribute_reproducer_strings[attribute]);
 }
 
 /* The implementation of class gcc::jit::recording::union_.  */
@@ -3837,7 +3863,8 @@ recording::union_::replay_into (replayer *r)
   set_playback_obj (
     r->new_compound_type (playback_location (r, get_loc ()),
 			  get_name ()->c_str (),
-			  false /* is_struct */));
+			  false, /* is_struct */
+			  m_attributes));
 }
 
 /* Implementation of recording::memento::make_debug_string for
@@ -3846,8 +3873,16 @@ recording::union_::replay_into (replayer *r)
 recording::string *
 recording::union_::make_debug_string ()
 {
+  std::string attributes;
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = type_attribute_to_string (attr);
+    if (attribute)
+      attributes.append ("__attribute (%s)__ ", attribute);
+  }
   return string::from_printf (m_ctxt,
-			      "union %s", get_name ()->c_str ());
+			      "union %s %s", attributes.c_str (),
+			      get_name ()->c_str ());
 }
 
 /* Implementation of recording::memento::write_reproducer for unions.  */
@@ -3877,6 +3912,11 @@ recording::union_::write_reproducer (reproducer &r)
 	   get_name ()->get_debug_string (),
 	   get_fields ()->length (),
 	   fields_id);
+
+  for (auto attribute : m_attributes)
+    r.write ("  gcc_jit_type_add_attribute (%s, %s);\n",
+	    id,
+	    type_attribute_reproducer_strings[attribute]);
 }
 
 /* The implementation of class gcc::jit::recording::fields.  */
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 4d41faa0446a..c6f2a6b812fb 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -619,6 +619,8 @@ public:
      these types.  */
   virtual size_t get_size () { gcc_unreachable (); }
 
+  void add_attribute (gcc_jit_type_attribute attribute);
+
   virtual type* copy (context* ctxt) = 0;
 
   /* Dynamic casts.  */
@@ -691,6 +693,8 @@ protected:
     m_pointer_to_this_type (NULL)
   {}
 
+  std::vector<gcc_jit_type_attribute> m_attributes;
+
 private:
   type *m_pointer_to_this_type;
 };
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 33369447074b..296bc8e4d3e2 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -4303,6 +4303,19 @@ gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
   variable->add_string_attribute (attribute, value);
 }
 
+void
+gcc_jit_type_add_attribute (gcc_jit_type *type,
+			    gcc_jit_type_attribute attribute)
+{
+  RETURN_IF_FAIL (type, NULL, NULL, "NULL type");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_TYPE_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_type_attribute` enum value");
+
+  type->add_attribute (attribute);
+}
+
 /* 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 a58551007931..7296110e233b 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -2216,6 +2216,22 @@ gcc_jit_context_set_output_ident (gcc_jit_context *ctxt,
 
 #define LIBGCCJIT_HAVE_gcc_jit_context_set_output_ident
 
+/* Function attributes.  */
+enum gcc_jit_type_attribute
+{
+  GCC_JIT_TYPE_ATTRIBUTE_PACKED,
+
+  /* Maximum value of this enum, should always be last.  */
+  GCC_JIT_TYPE_ATTRIBUTE_MAX,
+};
+
+/* Add an attribute to a type.  */
+extern void
+gcc_jit_type_add_attribute (gcc_jit_type *type,
+			    enum gcc_jit_type_attribute attribute);
+
+#define LIBGCCJIT_HAVE_gcc_jit_type_add_attribute
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 4662ca4b3c02..cc2897c5044e 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -334,3 +334,8 @@ LIBGCCJIT_ABI_35 {
     gcc_jit_target_info_arch;
     gcc_jit_target_info_supports_target_dependent_type;
 } LIBGCCJIT_ABI_34;
+
+LIBGCCJIT_ABI_36 {
+  global:
+    gcc_jit_type_add_attribute;
+} LIBGCCJIT_ABI_35;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 4aa18e3b7678..dce835c73f03 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -306,6 +306,9 @@
 /* test-nonnull-attribute.c: This can't be in the testcases array as it needs
    the `-O2` flag.  */
 
+/* test-packed-struct.c: This can't be in the testcases array as it
+   is target-specific.  */
+
 /* test-popcount.c */
 #define create_code create_code_popcount
 #define verify_code verify_code_popcount
diff --git a/gcc/testsuite/jit.dg/test-packed-struct.c b/gcc/testsuite/jit.dg/test-packed-struct.c
new file mode 100644
index 000000000000..6ab7f802fdfa
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-packed-struct.c
@@ -0,0 +1,74 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-packed-struct.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+/* Let's try to inject the equivalent of:
+struct __attribute__ ((__packed__)) my_packed_struct
+{
+  char c;
+  int  i;
+};
+
+void t(struct my_packed_struct *my_struct) {
+  my_struct->i = 1;
+}
+*/
+
+  gcc_jit_type *char_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR);
+
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  gcc_jit_field *field1 =
+    gcc_jit_context_new_field (ctxt, NULL, char_type, "c");
+  gcc_jit_field *field2 =
+    gcc_jit_context_new_field (ctxt, NULL, int_type, "i");
+
+  gcc_jit_field *fields[] = {
+    field1,
+    field2,
+  };
+
+  gcc_jit_struct *struct_type =
+    gcc_jit_context_new_struct_type (
+      ctxt, NULL, "my_packed_struct", 2, fields);
+  gcc_jit_type *type_struct = gcc_jit_struct_as_type (struct_type);
+  gcc_jit_type_add_attribute (type_struct, GCC_JIT_TYPE_ATTRIBUTE_PACKED);
+  gcc_jit_type *param_type = gcc_jit_type_get_pointer (type_struct);
+  gcc_jit_param *my_struct =
+    gcc_jit_context_new_param (ctxt, NULL,
+      param_type, "my_struct");
+  gcc_jit_param *params[1] = {my_struct};
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+      GCC_JIT_FUNCTION_EXPORTED, void_type, "t",
+      1, params, 0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+
+  gcc_jit_rvalue *one = gcc_jit_context_one (ctxt, int_type);
+  gcc_jit_lvalue *field = gcc_jit_rvalue_dereference_field (
+    gcc_jit_param_as_rvalue (my_struct), NULL, field2);
+  gcc_jit_block_add_assignment (block, NULL, field, one);
+
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "movl\\s+\\\$1,\\s+1\\(%rdi\\)" } } */

Reply via email to