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\\)" } } */