public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Add support for function attributes and variable attributes
@ 2023-11-15 16:53 Guillaume Gomez
  2023-11-15 16:56 ` Antoni Boucher
  2024-01-09 19:59 ` David Malcolm
  0 siblings, 2 replies; 16+ messages in thread
From: Guillaume Gomez @ 2023-11-15 16:53 UTC (permalink / raw)
  To: jit, Antoni, gcc-patches, David Malcolm

[-- Attachment #1: Type: text/plain, Size: 545 bytes --]

Hi,

This patch adds the (incomplete) support for function and variable
attributes. The added attributes are the ones we're using in
rustc_codegen_gcc but all the groundwork is done to add more (and we
will very likely add more as we didn't add all the ones we use in
rustc_codegen_gcc yet).

The only big question with this patch is about `inline`. We currently
handle it as an attribute because it is more convenient for us but is
it ok or should we create a separate function to mark a function as
inlined?

Thanks in advance for the review.

[-- Attachment #2: 0001-PATCH-Add-support-for-function-attributes-and-variab.patch --]
[-- Type: text/x-patch, Size: 87042 bytes --]

From df75f0eb8aacba249b6e791603752e35778951a4 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 20 Jun 2022 14:34:39 -0400
Subject: [PATCH] [PATCH] Add support for function attributes and variable
 attributes.

gcc/jit/ChangeLog:

	* dummy-frontend.cc (handle_alias_attribute): New function.
	(handle_always_inline_attribute): New function.
	(handle_cold_attribute): New function.
	(handle_fnspec_attribute): New function.
	(handle_format_arg_attribute): New function.
	(handle_format_attribute): New function.
	(handle_noinline_attribute): New function.
	(handle_target_attribute): New function.
	(handle_used_attribute): New function.
	(handle_visibility_attribute): New function.
	(handle_weak_attribute): New function.
	(handle_alias_ifunc_attribute): New function.
	* jit-playback.cc (fn_attribute_to_string): New function.
	(variable_attribute_to_string): New function.
	(global_new_decl): Add attributes support.
	(set_variable_attribute): New function.
	(new_global): Add attributes support.
	(new_global_initialized): Add attributes support.
	(new_local): Add attributes support.
	* jit-playback.h (fn_attribute_to_string): New function.
	(set_variable_attribute): New function.
	* jit-recording.cc (recording::lvalue::add_attribute): New function.
	(recording::function::function): New function.
	(recording::function::write_to_dump): Add attributes support.
	(recording::function::add_attribute): New function.
	(recording::function::add_string_attribute): New function.
	(recording::function::add_integer_array_attribute): New function.
	(recording::global::replay_into): Add attributes support.
	(recording::local::replay_into): Add attributes support.
	* libgccjit.cc (gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(gcc_jit_lvalue_add_attribute): New function.
	* libgccjit.h (enum gcc_jit_fn_attribute): New enum.
	(gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(enum gcc_jit_variable_attribute): New function.
	(gcc_jit_lvalue_add_string_attribute): New function.
	* libgccjit.map: Declare new functions.

gcc/testsuite/ChangeLog:

	* jit.dg/jit.exp: Add `jit-verify-assembler-output-not` test command.
	* jit.dg/test-restrict.c: New test.
	* jit.dg/test-restrict-attribute.c: New test.
	* jit.dg/test-alias-attribute.c: New test.
	* jit.dg/test-always_inline-attribute.c: New test.
	* jit.dg/test-cold-attribute.c: New test.
	* jit.dg/test-const-attribute.c: New test.
	* jit.dg/test-noinline-attribute.c: New test.
	* jit.dg/test-nonnull-attribute.c: New test.
	* jit.dg/test-pure-attribute.c: New test.
	* jit.dg/test-used-attribute.c: New test.
	* jit.dg/test-variable-attribute.c: New test.
	* jit.dg/test-weak-attribute.c: New test.

gcc/jit/ChangeLog:
	* docs/topics/compatibility.rst: Add documentation for LIBGCCJIT_ABI_26.
	* docs/topics/types.rst: Add documentation for new functions.

Co-authored-by: Antoni Boucher <bouanto@zoho.com>
Signed-off-by: Guillaume Gomez <guillaume1.gomez@gmail.com>
---
 gcc/jit/docs/topics/compatibility.rst         |  12 +
 gcc/jit/docs/topics/types.rst                 |  77 +++
 gcc/jit/dummy-frontend.cc                     | 504 ++++++++++++++++--
 gcc/jit/jit-playback.cc                       | 165 +++++-
 gcc/jit/jit-playback.h                        |  37 +-
 gcc/jit/jit-recording.cc                      | 166 +++++-
 gcc/jit/jit-recording.h                       |  19 +-
 gcc/jit/libgccjit.cc                          |  45 ++
 gcc/jit/libgccjit.h                           |  49 ++
 gcc/jit/libgccjit.map                         |   8 +
 gcc/testsuite/jit.dg/jit.exp                  |  33 ++
 gcc/testsuite/jit.dg/test-alias-attribute.c   |  50 ++
 .../jit.dg/test-always_inline-attribute.c     | 153 ++++++
 gcc/testsuite/jit.dg/test-cold-attribute.c    |  54 ++
 gcc/testsuite/jit.dg/test-const-attribute.c   | 134 +++++
 .../jit.dg/test-noinline-attribute.c          | 114 ++++
 gcc/testsuite/jit.dg/test-nonnull-attribute.c |  94 ++++
 gcc/testsuite/jit.dg/test-pure-attribute.c    | 134 +++++
 ...t-restrict.c => test-restrict-attribute.c} |   4 +-
 gcc/testsuite/jit.dg/test-used-attribute.c    | 112 ++++
 .../jit.dg/test-variable-attribute.c          |  46 ++
 gcc/testsuite/jit.dg/test-weak-attribute.c    |  41 ++
 22 files changed, 1986 insertions(+), 65 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-alias-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-always_inline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-cold-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-const-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-noinline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-nonnull-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-pure-attribute.c
 rename gcc/testsuite/jit.dg/{test-restrict.c => test-restrict-attribute.c} (95%)
 create mode 100644 gcc/testsuite/jit.dg/test-used-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-variable-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-weak-attribute.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index ebede440ee4..b4a6e997941 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -378,3 +378,15 @@ alignment of a variable:
 --------------------
 ``LIBGCCJIT_ABI_25`` covers the addition of
 :func:`gcc_jit_type_get_restrict`
+
+.. _LIBGCCJIT_ABI_26:
+
+``LIBGCCJIT_ABI_26``
+--------------------
+``LIBGCCJIT_ABI_26`` covers the addition of functions to set attributes
+on functions and variables:
+
+  * :func:`gcc_jit_function_add_attribute`
+  * :func:`gcc_jit_function_add_string_attribute`
+  * :func:`gcc_jit_function_add_integer_array_attribute`
+  * :func:`gcc_jit_lvalue_add_string_attribute`
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index d8c1d15d69d..6c72c99cbd9 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -553,3 +553,80 @@ Reflection API
    .. code-block:: c
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
+
+.. function::  void\
+               gcc_jit_function_add_attribute (gcc_jit_function *func,
+                                               enum gcc_jit_fn_attribute attribute)
+
+     Add an attribute ``attribute`` to a function ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__((always_inline))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+                                                      enum gcc_jit_fn_attribute attribute,
+                                                      const char *value)
+
+     Add a string attribute ``attribute`` with value ``value`` to a function
+     ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((alias ("xxx")))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+                                                             enum gcc_jit_fn_attribute attribute,
+                                                             const int *value,
+                                                             size_t length)
+
+     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
+     function ``func``. The integer values must be the same as you would write
+     them in a C code.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((nonnull (1, 2)))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+                                                    enum gcc_jit_fn_attribute attribute,
+                                                    const char *value)
+
+     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index a729086bafb..898b4d6e7f8 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -29,30 +29,42 @@ along with GCC; see the file COPYING3.  If not see
 #include "options.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "cgraph.h"
+#include "target.h"
 
 #include <mpfr.h>
 
 /* Attribute handling.  */
 
-static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
-static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
+static tree handle_always_inline_attribute (tree *, tree, tree, int,
+					    bool *);
+static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_const_attribute (tree *, tree, tree, int, bool *);
+static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_attribute (tree *, tree, tree, int, bool *);
+static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
-static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 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_sentinel_attribute (tree *, tree, tree, int, bool *);
-static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
-static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
-static tree ignore_attribute (tree *, tree, tree, int, bool *);
+static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
+static tree handle_target_attribute (tree *, tree, tree, int, bool *);
+static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
+static tree handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree handle_visibility_attribute (tree *, tree, tree, int,
+					 bool *);
+static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 
-static tree handle_format_attribute (tree *, tree, tree, int, bool *);
-static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
-static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -81,55 +93,98 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
 static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 {
   ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("alloc_align", true, true, true),
+  ATTR_EXCL ("alloc_size", true, true, true),
+  ATTR_EXCL ("malloc", true, true, true),
   ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("pure", true, true, true),
   ATTR_EXCL (NULL, false, false, false)
 };
 
+static const struct attribute_spec::exclusions attr_inline_exclusions[] =
+{
+  ATTR_EXCL ("noinline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+{
+  ATTR_EXCL ("cold", true, true, true),
+  ATTR_EXCL ("hot", true, true, true),
+  ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_noinline_exclusions[] =
+{
+  ATTR_EXCL ("always_inline", true, true, true),
+  ATTR_EXCL ("gnu_inline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 /* Table of machine-independent attributes supported in libgccjit.  */
 const struct attribute_spec jit_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "noreturn",               0, 0, true,  false, false, false,
-			      handle_noreturn_attribute,
-			      attr_noreturn_exclusions },
-  { "leaf",		      0, 0, true,  false, false, false,
-			      handle_leaf_attribute, NULL },
+  { "alias",		      1, 1, true,  false, false, false,
+			      handle_alias_attribute, NULL },
+  { "always_inline",	      0, 0, true,  false, false, false,
+			      handle_always_inline_attribute,
+			      attr_inline_exclusions },
+  { "cold",		      0, 0, true,  false, false, false,
+			      handle_cold_attribute,
+			      attr_cold_hot_exclusions },
   /* The same comments as for noreturn attributes apply to const ones.  */
-  { "const",                  0, 0, true,  false, false, false,
+  { "const",		      0, 0, true,  false, false, false,
 			      handle_const_attribute,
 			      attr_const_pure_exclusions },
-  { "malloc",                 0, 0, true,  false, false, false,
+  { "fn spec",		      1, 1, false, true, true, false,
+			      handle_fnspec_attribute, NULL },
+
+  { "leaf",		      0, 0, true,  false, false, false,
+			      handle_leaf_attribute, NULL },
+  { "malloc",		      0, 0, true,  false, false, false,
 			      handle_malloc_attribute, NULL },
-  { "pure",                   0, 0, true,  false, false, false,
-			      handle_pure_attribute,
-			      attr_const_pure_exclusions },
-  { "no vops",                0, 0, true,  false, false, false,
+  { "noreturn",		      0, 0, true,  false, false, false,
+			      handle_noreturn_attribute,
+			      attr_noreturn_exclusions },
+  { "no vops",		      0, 0, true,  false, false, false,
 			      handle_novops_attribute, NULL },
-  { "nonnull",                0, -1, false, true, true, false,
+  { "noinline",		      0, 0, true,  false, false, false,
+			      handle_noinline_attribute,
+			      attr_noinline_exclusions },
+  { "nonnull",		      0, -1, false, true, true, false,
 			      handle_nonnull_attribute, NULL },
-  { "nothrow",                0, 0, true,  false, false, false,
+  { "nothrow",		      0, 0, true,  false, false, false,
 			      handle_nothrow_attribute, NULL },
   { "patchable_function_entry", 1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
-  { "returns_twice",          0, 0, true,  false, false, false,
+  { "pure",		      0, 0, true,  false, false, false,
+			      handle_pure_attribute,
+			      attr_const_pure_exclusions },
+  { "returns_twice",	      0, 0, true,  false, false, false,
 			      handle_returns_twice_attribute,
 			      attr_returns_twice_exclusions },
-  { "sentinel",               0, 1, false, true, true, false,
+  { "sentinel",		      0, 1, false, true, true, false,
 			      handle_sentinel_attribute, NULL },
-  { "type generic",           0, 0, false, true, true, false,
+  { "target",		      1, -1, true, false, false, false,
+			      handle_target_attribute, NULL },
+  { "type generic",	      0, 0, false, true, true, false,
 			      handle_type_generic_attribute, NULL },
-  { "fn spec",	 	      1, 1, false, true, true, false,
-			      handle_fnspec_attribute, NULL },
   { "transaction_pure",	      0, 0, false, true, true, false,
 			      handle_transaction_pure_attribute, NULL },
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
-  { "*tm regparm",            0, 0, false, true, true, false,
+  { "*tm regparm",	      0, 0, false, true, true, false,
 			      ignore_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+  { "used",		      0, 0, true,  false, false, false,
+			      handle_used_attribute, NULL },
+  { "visibility",	      1, 1, false, false, false, false,
+			      handle_visibility_attribute, NULL },
+  { "weak",		      0, 0, true,  false, false, false,
+			      handle_weak_attribute, NULL },
+  { NULL,		      0, 0, false, false, false, false, NULL, NULL }
 };
 
 /* Give the specifications for the format attributes, used by C and all
@@ -139,11 +194,11 @@ const struct attribute_spec jit_format_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "format",                 3, 3, false, true,  true, false,
+  { "format",		      3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
-  { "format_arg",             1, 1, false, true,  true, false,
+  { "format_arg",	      1, 1, false, true,  true, false,
 			      handle_format_arg_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+  { NULL,		      0, 0, false, false, false, false, NULL, NULL }
 };
 
 /* Attribute handlers.  */
@@ -480,6 +535,385 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle an "visibility" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_visibility_attribute (tree *node, tree name, tree args,
+			     int ARG_UNUSED (flags),
+			     bool *ARG_UNUSED (no_add_attrs))
+{
+  tree decl = *node;
+  tree id = TREE_VALUE (args);
+  enum symbol_visibility vis;
+
+  if (TYPE_P (*node))
+    {
+      if (TREE_CODE (*node) == ENUMERAL_TYPE)
+	/* OK.  */;
+      else if (!RECORD_OR_UNION_TYPE_P (*node))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
+		   name);
+	  return NULL_TREE;
+	}
+      else if (TYPE_FIELDS (*node))
+	{
+	  error ("%qE attribute ignored because %qT is already defined",
+		 name, *node);
+	  return NULL_TREE;
+	}
+    }
+  else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("visibility argument not a string");
+      return NULL_TREE;
+    }
+
+  /*  If this is a type, set the visibility on the type decl.  */
+  if (TYPE_P (decl))
+    {
+      decl = TYPE_NAME (decl);
+      if (!decl)
+	return NULL_TREE;
+      if (TREE_CODE (decl) == IDENTIFIER_NODE)
+	{
+	   warning (OPT_Wattributes, "%qE attribute ignored on types",
+		    name);
+	   return NULL_TREE;
+	}
+    }
+
+  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
+    vis = VISIBILITY_DEFAULT;
+  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
+    vis = VISIBILITY_INTERNAL;
+  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
+    vis = VISIBILITY_HIDDEN;
+  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
+    vis = VISIBILITY_PROTECTED;
+  else
+    {
+      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
+	     name, "default", "hidden", "protected", "internal");
+      vis = VISIBILITY_DEFAULT;
+    }
+
+  if (DECL_VISIBILITY_SPECIFIED (decl)
+      && vis != DECL_VISIBILITY (decl))
+    {
+      tree attributes = (TYPE_P (*node)
+			 ? TYPE_ATTRIBUTES (*node)
+			 : DECL_ATTRIBUTES (decl));
+      if (lookup_attribute ("visibility", attributes))
+	error ("%qD redeclared with different visibility", decl);
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllimport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllimport");
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllexport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllexport");
+    }
+
+  DECL_VISIBILITY (decl) = vis;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+
+  /* Go ahead and attach the attribute to the node as well.  This is needed
+     so we can determine whether we have VISIBILITY_DEFAULT because the
+     visibility was not specified, or because it was explicitly overridden
+     from the containing scope.  */
+
+  return NULL_TREE;
+}
+
+/* Handle a "always_inline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_always_inline_attribute (tree *node, tree name,
+				tree ARG_UNUSED (args),
+				int ARG_UNUSED (flags),
+				bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      if (lookup_attribute ("noinline", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "noinline");
+	  *no_add_attrs = true;
+	}
+      else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "target_clones");
+	  *no_add_attrs = true;
+	}
+      else
+	/* Set the attribute and mark it for disregarding inline
+	   limits.  */
+	DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "cold" and attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      || TREE_CODE (*node) == LABEL_DECL)
+    {
+      /* Attribute cold processing is done later with lookup_attribute.  */
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "noinline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_noinline_attribute (tree *node, tree name,
+			   tree ARG_UNUSED (args),
+			   int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with attribute %qs", name, "always_inline");
+	  *no_add_attrs = true;
+	}
+      else
+	DECL_UNINLINABLE (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "weak" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_weak_attribute (tree *node, tree name,
+		       tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags),
+		       bool * ARG_UNUSED (no_add_attrs))
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && DECL_DECLARED_INLINE_P (*node))
+    {
+      warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weak", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (VAR_OR_FUNCTION_DECL_P (*node))
+    declare_weak (*node);
+  else
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+
+  return NULL_TREE;
+}
+
+/* Handle a "target" attribute.  */
+
+static tree
+handle_target_attribute (tree *node, tree name, tree args, int flags,
+			 bool *no_add_attrs)
+{
+  /* Ensure we have a function type.  */
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "target_clones");
+      *no_add_attrs = true;
+    }
+  else if (! targetm.target_option.valid_attribute_p (*node, name, args,
+						      flags))
+    *no_add_attrs = true;
+
+  /* Check that there's no empty string in values of the attribute.  */
+  for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
+    {
+      tree value = TREE_VALUE (t);
+      if (TREE_CODE (value) == STRING_CST
+	  && TREE_STRING_LENGTH (value) == 1
+	  && TREE_STRING_POINTER (value)[0] == '\0')
+	{
+	  warning (OPT_Wattributes, "empty string in attribute %<target%>");
+	  *no_add_attrs = true;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "used" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  if (TREE_CODE (node) == FUNCTION_DECL
+      || (VAR_P (node) && TREE_STATIC (node))
+      || (TREE_CODE (node) == TYPE_DECL))
+    {
+      TREE_USED (node) = 1;
+      DECL_PRESERVE_P (node) = 1;
+      if (VAR_P (node))
+	DECL_READ_P (node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler, except that IS_ALIAS tells us
+   whether this is an alias as opposed to ifunc attribute.  */
+
+static tree
+handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
+			      bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (!is_alias || !VAR_P (decl)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
+      /* A static variable declaration is always a tentative definition,
+	 but the alias is a non-tentative definition which overrides.  */
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
+    {
+      error ("%q+D defined both normally and as %qE attribute", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (!is_alias
+	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
+	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
+    {
+      error ("weak %q+D cannot be defined %qE", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Note that the very first time we process a nested declaration,
+     decl_function_context will not be set.  Indeed, *would* never
+     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
+     we do below.  After such frobbery, pushdecl would set the context.
+     In any case, this is never what we want.  */
+  else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
+    {
+      tree id;
+
+      id = TREE_VALUE (args);
+      if (TREE_CODE (id) != STRING_CST)
+	{
+	  error ("attribute %qE argument not a string", name);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+      id = get_identifier (TREE_STRING_POINTER (id));
+      /* This counts as a use of the object pointed to.  */
+      TREE_USED (id) = 1;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	DECL_INITIAL (decl) = error_mark_node;
+      else
+	TREE_STATIC (decl) = 1;
+
+      if (!is_alias)
+	{
+	  /* ifuncs are also aliases, so set that attribute too.  */
+	  DECL_ATTRIBUTES (decl)
+	    = tree_cons (get_identifier ("alias"), args,
+			 DECL_ATTRIBUTES (decl));
+	  DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
+					      NULL, DECL_ATTRIBUTES (decl));
+	}
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  if (decl_in_symtab_p (*node))
+    {
+      struct symtab_node *n = symtab_node::get (decl);
+      if (n && n->refuse_visibility_changes)
+	error ("%+qD declared %qs after being used",
+	       decl, is_alias ? "alias" : "ifunc");
+    }
+
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_alias_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
+}
+
 /* (end of attribute-handling).  */
 
 /* Language-dependent contents of a type.  */
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 18cc4da25b8..d8dbc5aac61 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #define INCLUDE_MUTEX
+#include "libgccjit.h"
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -499,6 +500,50 @@ new_param (location *loc,
   return new param (this, inner);
 }
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_FN_ATTRIBUTE_ALIAS:
+      return "alias";
+    case GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE:
+      return "always_inline";
+    case GCC_JIT_FN_ATTRIBUTE_INLINE:
+      return NULL;
+    case GCC_JIT_FN_ATTRIBUTE_NOINLINE:
+      return "noinline";
+    case GCC_JIT_FN_ATTRIBUTE_TARGET:
+      return "target";
+    case GCC_JIT_FN_ATTRIBUTE_USED:
+      return "used";
+    case GCC_JIT_FN_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_FN_ATTRIBUTE_COLD:
+      return "cold";
+    case GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE:
+      return "returns_twice";
+    case GCC_JIT_FN_ATTRIBUTE_PURE:
+      return "pure";
+    case GCC_JIT_FN_ATTRIBUTE_CONST:
+      return "const";
+    case GCC_JIT_FN_ATTRIBUTE_WEAK:
+      return "weak";
+    case GCC_JIT_FN_ATTRIBUTE_NONNULL:
+      return "nonnull";
+  }
+  return NULL;
+}
+
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+  }
+  return NULL;
+}
+
 /* Construct a playback::function instance.  */
 
 playback::function *
@@ -509,7 +554,13 @@ new_function (location *loc,
 	      const char *name,
 	      const auto_vec<param *> *params,
 	      int is_variadic,
-	      enum built_in_function builtin_id)
+	      enum built_in_function builtin_id,
+	      const std::vector<gcc_jit_fn_attribute> &attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::string>> &string_attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::vector<int>>>
+					  &int_array_attributes)
 {
   int i;
   param *param;
@@ -543,6 +594,8 @@ new_function (location *loc,
   DECL_RESULT (fndecl) = resdecl;
   DECL_CONTEXT (resdecl) = fndecl;
 
+  tree fn_attributes = NULL_TREE;
+
   if (builtin_id)
     {
       gcc_assert (loc == NULL);
@@ -588,12 +641,62 @@ new_function (location *loc,
       DECL_DECLARED_INLINE_P (fndecl) = 1;
 
       /* Add attribute "always_inline": */
-      DECL_ATTRIBUTES (fndecl) =
-	tree_cons (get_identifier ("always_inline"),
-		   NULL,
-		   DECL_ATTRIBUTES (fndecl));
+      fn_attributes = tree_cons (get_identifier ("always_inline"),
+				 NULL,
+				 fn_attributes);
+    }
+
+  /* All attributes need to be declared in `dummy-frontend.cc` and more
+     specifically in `jit_attribute_table`. */
+  for (auto attr: attributes)
+  {
+    if (attr == GCC_JIT_FN_ATTRIBUTE_INLINE)
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+    {
+      tree ident = get_identifier (attribute);
+      fn_attributes = tree_cons (ident, NULL_TREE, fn_attributes);
     }
+  }
 
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (ident)
+      fn_attributes = tree_cons (ident, attribute_value, fn_attributes);
+  }
+
+  for (auto attr: int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (!ident)
+      continue;
+
+    tree tree_list = NULL_TREE;
+    tree *p_tree_list = &tree_list;
+    for (auto value : values)
+    {
+      tree int_value = build_int_cst (integer_type_node, value);
+      *p_tree_list = build_tree_list (NULL, int_value);
+      p_tree_list = &TREE_CHAIN (*p_tree_list);
+    }
+    fn_attributes = tree_cons (ident, tree_list, fn_attributes);
+  }
+
+  decl_attributes (&fndecl, fn_attributes, 0);
   function *func = new function (this, fndecl, kind);
   m_functions.safe_push (func);
   return func;
@@ -607,7 +710,9 @@ global_new_decl (location *loc,
 		 enum gcc_jit_global_kind kind,
 		 type *type,
 		 const char *name,
-		 enum global_var_flags flags)
+		 enum global_var_flags flags,
+		 const std::vector<std::pair<gcc_jit_variable_attribute,
+					     std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -652,9 +757,32 @@ global_new_decl (location *loc,
   if (loc)
     set_tree_location (inner, loc);
 
+  set_variable_string_attribute (attributes, inner);
+
   return inner;
 }
 
+void
+playback::
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+  			      std::string>> &string_attributes,
+  tree decl)
+{
+  tree var_attributes = NULL_TREE;
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    tree ident = get_identifier (variable_attribute_to_string (name));
+    if (ident)
+      var_attributes = tree_cons (ident, attribute_value, var_attributes);
+  }
+  decl_attributes (&decl, var_attributes, 0);
+}
+
 /* In use by new_global and new_global_initialized.  */
 
 playback::lvalue *
@@ -674,10 +802,12 @@ new_global (location *loc,
 	    enum gcc_jit_global_kind kind,
 	    type *type,
 	    const char *name,
-	    enum global_var_flags flags)
+	    enum global_var_flags flags,
+	    const std::vector<std::pair<gcc_jit_variable_attribute,
+					std::string>> &attributes)
 {
   tree inner =
-    global_new_decl (loc, kind, type, name, flags);
+    global_new_decl (loc, kind, type, name, flags, attributes);
 
   return global_finalize_lvalue (inner);
 }
@@ -818,13 +948,15 @@ playback::context::
 new_global_initialized (location *loc,
 			enum gcc_jit_global_kind kind,
 			type *type,
-                        size_t element_size,
+			size_t element_size,
 			size_t initializer_num_elem,
 			const void *initializer,
 			const char *name,
-			enum global_var_flags flags)
+			enum global_var_flags flags,
+			const std::vector<std::pair<gcc_jit_variable_attribute,
+						    std::string>> &attributes)
 {
-  tree inner = global_new_decl (loc, kind, type, name, flags);
+  tree inner = global_new_decl (loc, kind, type, name, flags, attributes);
 
   vec<constructor_elt, va_gc> *constructor_elements = NULL;
 
@@ -1812,7 +1944,9 @@ playback::lvalue *
 playback::function::
 new_local (location *loc,
 	   type *type,
-	   const char *name)
+	   const char *name,
+	   const std::vector<std::pair<gcc_jit_variable_attribute,
+				       std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -1825,6 +1959,8 @@ new_local (location *loc,
   DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
   BIND_EXPR_VARS (m_inner_bind_expr) = inner;
 
+  set_variable_string_attribute (attributes, inner);
+
   if (loc)
     set_tree_location (inner, loc);
   return new lvalue (m_ctxt, inner);
@@ -1947,6 +2083,9 @@ postprocess ()
 
       current_function_decl = NULL;
     }
+    else
+      /* Add to cgraph to output aliases: */
+      rest_of_decl_compilation (m_inner_fndecl, true, 0);
 }
 
 /* Don't leak vec's internal buffer (in non-GC heap) when we are
@@ -3365,7 +3504,7 @@ void
 playback::context::
 init_types ()
 {
-  /* See lto_init() in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc 
+  /* See lto_init () in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc
      for reference. If TYPE_NAME is not set, debug info will not contain types */
 #define NAME_TYPE(t,n) \
 if (t) \
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index f9e29d0baec..a0e56520702 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -21,7 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef JIT_PLAYBACK_H
 #define JIT_PLAYBACK_H
 
+#include <string>
 #include <utility> // for std::pair
+#include <vector>
 
 #include "timevar.h"
 #include "varasm.h"
@@ -35,12 +37,21 @@ namespace gcc {
 
 namespace jit {
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr);
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr);
+
 /**********************************************************************
  Playback.
  **********************************************************************/
 
 namespace playback {
 
+void
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			      std::string>> &attributes,
+  tree decl);
+
 /* playback::context is an abstract base class.
 
    The two concrete subclasses are:
@@ -104,14 +115,22 @@ public:
 		const char *name,
 		const auto_vec<param *> *params,
 		int is_variadic,
-		enum built_in_function builtin_id);
+		enum built_in_function builtin_id,
+		const std::vector<gcc_jit_fn_attribute> &attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::string>> &string_attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::vector<int>>>
+					    &int_array_attributes);
 
   lvalue *
   new_global (location *loc,
 	      enum gcc_jit_global_kind kind,
 	      type *type,
 	      const char *name,
-	      enum global_var_flags flags);
+	      enum global_var_flags flags,
+	      const std::vector<std::pair<gcc_jit_variable_attribute,
+					  std::string>> &attributes);
 
   lvalue *
   new_global_initialized (location *loc,
@@ -121,7 +140,11 @@ public:
                           size_t initializer_num_elem,
                           const void *initializer,
 			  const char *name,
-			  enum global_var_flags flags);
+			  enum global_var_flags flags,
+			  const std::vector<std::pair<
+					    gcc_jit_variable_attribute,
+					    std::string>>
+					    &attributes);
 
   rvalue *
   new_ctor (location *log,
@@ -306,7 +329,9 @@ private:
                    enum gcc_jit_global_kind kind,
                    type *type,
 		   const char *name,
-		   enum global_var_flags flags);
+		   enum global_var_flags flags,
+		   const std::vector<std::pair<gcc_jit_variable_attribute,
+					       std::string>> &attributes);
   lvalue *
   global_finalize_lvalue (tree inner);
 
@@ -500,7 +525,9 @@ public:
   lvalue *
   new_local (location *loc,
 	     type *type,
-	     const char *name);
+	     const char *name,
+	     const std::vector<std::pair<gcc_jit_variable_attribute,
+					 std::string>> &attributes);
 
   block*
   new_block (const char *name);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index 9b5b8005ebe..64301a329ec 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-builtins.h"
 #include "jit-recording.h"
 #include "jit-playback.h"
+#include <sstream>
 
 namespace gcc {
 namespace jit {
@@ -2068,7 +2069,7 @@ recording::memento::get_debug_string ()
 void
 recording::memento::write_to_dump (dump &d)
 {
-  d.write("  %s\n", get_debug_string ());
+  d.write ("  %s\n", get_debug_string ());
 }
 
 /* The implementation of class gcc::jit::recording::string.  */
@@ -4026,6 +4027,13 @@ void recording::lvalue::set_alignment (unsigned bytes)
   m_alignment = bytes;
 }
 
+void recording::lvalue::add_string_attribute (
+	gcc_jit_variable_attribute attribute,
+	const char* value)
+{
+  m_string_attributes.push_back (std::make_pair (attribute, std::string (value)));
+}
+
 /* The implementation of class gcc::jit::recording::param.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -4102,7 +4110,10 @@ recording::function::function (context *ctxt,
   m_builtin_id (builtin_id),
   m_locals (),
   m_blocks (),
-  m_fn_ptr_type (NULL)
+  m_fn_ptr_type (NULL),
+  m_attributes (),
+  m_string_attributes (),
+  m_int_array_attributes ()
 {
   for (int i = 0; i< num_params; i++)
     {
@@ -4161,7 +4172,10 @@ recording::function::replay_into (replayer *r)
 				     m_name->c_str (),
 				     &params,
 				     m_is_variadic,
-				     m_builtin_id));
+				     m_builtin_id,
+				     m_attributes,
+				     m_string_attributes,
+				     m_int_array_attributes));
 }
 
 /* Create a recording::local instance and add it to
@@ -4210,6 +4224,40 @@ recording::function::new_block (const char *name)
 void
 recording::function::write_to_dump (dump &d)
 {
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+      d.write ("__attribute(%s)__\n", attribute);
+  }
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
+  for (auto attr: m_int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+    if (attribute)
+    {
+      d.write ("__attribute(%s(", attribute);
+      for (size_t i = 0; i < values.size(); ++i)
+      {
+	if (i > 0)
+	  d.write (", %d", values[i]);
+	else
+	  d.write ("%d", values[i]);
+      }
+      d.write ("))__\n");
+    }
+  }
+
   switch (m_kind)
     {
     default: gcc_unreachable ();
@@ -4404,6 +4452,31 @@ recording::function::get_address (recording::location *loc)
   return result;
 }
 
+void
+recording::function::add_attribute (gcc_jit_fn_attribute attribute)
+{
+  m_attributes.push_back (attribute);
+}
+
+void
+recording::function::add_string_attribute (gcc_jit_fn_attribute attribute,
+					   const char* value)
+{
+  m_string_attributes.push_back (
+	std::make_pair (attribute, std::string (value)));
+}
+
+void
+recording::function::add_integer_array_attribute (
+	gcc_jit_fn_attribute attribute,
+	const int* value,
+	size_t length)
+{
+  m_int_array_attributes.push_back (std::make_pair (
+    attribute,
+    std::vector<int> (value, value + length)));
+}
+
 /* Implementation of recording::memento::make_debug_string for
    functions.  */
 
@@ -4425,6 +4498,39 @@ static const char * const names_of_function_kinds[] = {
 
 /* Implementation of recording::memento::write_reproducer for functions. */
 
+static const char * const fn_attribute_reproducer_strings[] =
+{
+  "GCC_JIT_FN_ATTRIBUTE_ALIAS",
+  "GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_NOINLINE",
+  "GCC_JIT_FN_ATTRIBUTE_TARGET",
+  "GCC_JIT_FN_ATTRIBUTE_USED",
+  "GCC_JIT_FN_ATTRIBUTE_VISIBILITY",
+  "GCC_JIT_FN_ATTRIBUTE_COLD",
+  "GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE",
+  "GCC_JIT_FN_ATTRIBUTE_PURE",
+  "GCC_JIT_FN_ATTRIBUTE_CONST",
+  "GCC_JIT_FN_ATTRIBUTE_WEAK",
+  "GCC_JIT_FN_ATTRIBUTE_NONNULL",
+};
+
+std::string
+get_vector_int_debug (std::vector<int> &values)
+{
+  std::stringstream s;
+
+  s << "{";
+  for(auto it = values.begin(); it != values.end(); ++it)
+    {
+      if (it != values.begin() )
+        s << ", ";
+      s << *it;
+    }
+  s << "}";
+  return s.str();
+}
+
 void
 recording::function::write_reproducer (reproducer &r)
 {
@@ -4467,6 +4573,25 @@ recording::function::write_reproducer (reproducer &r)
 	   m_params.length (),
 	   params_id,
 	   m_is_variadic);
+  for (auto attribute : m_attributes)
+    r.write("  gcc_jit_function_add_attribute (%s, %s);\n",
+	    id,
+	    fn_attribute_reproducer_strings[attribute]);
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_function_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+  for (auto attribute : m_int_array_attributes) {
+    r.write("  gcc_jit_function_add_integer_array_attribute (%s,\n"
+	    "                                                %s,\n"
+	    "                                                (int[])%s,\n"
+	    "                                                %lu);\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    get_vector_int_debug (std::get<1>(attribute)).c_str(),
+	    std::get<1>(attribute).size ());
+  }
 }
 
 
@@ -4879,12 +5004,14 @@ recording::global::replay_into (replayer *r)
 				 / m_type->dereference ()->get_size (),
 				 m_initializer,
 				 playback_string (m_name),
-				 m_flags)
+				 m_flags,
+				 m_string_attributes)
     : r->new_global (playback_location (r, m_loc),
 		     m_kind,
 		     m_type->playback_type (),
 		     playback_string (m_name),
-		     m_flags);
+		     m_flags,
+		     m_string_attributes);
 
   if (m_tls_model != GCC_JIT_TLS_MODEL_NONE)
     global->set_tls_model (recording::tls_models[m_tls_model]);
@@ -4943,6 +5070,15 @@ recording::global::write_to_dump (dump &d)
       break;
     }
 
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = variable_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
   d.write ("%s %s",
 	   m_type->get_debug_string (),
 	   get_debug_string ());
@@ -5013,6 +5149,10 @@ static const char * const tls_model_enum_strings[] = {
   "GCC_JIT_TLS_MODEL_LOCAL_EXEC",
 };
 
+static const char * const gcc_jit_variable_attribute_enum_strings[] = {
+  "GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY",
+};
+
 void
 recording::global::write_reproducer (reproducer &r)
 {
@@ -5042,6 +5182,13 @@ recording::global::write_reproducer (reproducer &r)
      id,
      m_link_section->c_str ());
 
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_lvalue_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+
+
   if (m_initializer)
     switch (m_type->dereference ()->get_size ())
       {
@@ -6622,7 +6769,8 @@ recording::local::replay_into (replayer *r)
   playback::lvalue *obj = m_func->playback_function ()
       ->new_local (playback_location (r, m_loc),
 		   m_type->playback_type (),
-		   playback_string (m_name));
+		   playback_string (m_name),
+		   m_string_attributes);
 
   if (m_reg_name != NULL)
     obj->set_register_name (m_reg_name->c_str ());
@@ -6644,9 +6792,9 @@ recording::local::write_to_dump (dump &d)
 {
   if (d.update_locations ())
     m_loc = d.make_location ();
-  d.write("  %s %s;\n",
-	  m_type->get_debug_string (),
-	  get_debug_string ());
+  d.write ("  %s %s;\n",
+	   m_type->get_debug_string (),
+	   get_debug_string ());
 }
 
 void
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 4a8082991fb..44694f610fa 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -23,6 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "jit-common.h"
 #include "jit-logging.h"
+#include "libgccjit.h"
+
+#include <string>
+#include <vector>
 
 class timer;
 
@@ -1216,7 +1220,8 @@ public:
     m_link_section (NULL),
     m_reg_name (NULL),
     m_tls_model (GCC_JIT_TLS_MODEL_NONE),
-    m_alignment (0)
+    m_alignment (0),
+    m_string_attributes ()
   {}
 
   playback::lvalue *
@@ -1236,6 +1241,9 @@ public:
   as_rvalue () { return this; }
 
   const char *access_as_rvalue (reproducer &r) override;
+
+  void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value);
+
   virtual const char *access_as_lvalue (reproducer &r);
   virtual bool is_global () const { return false; }
   void set_tls_model (enum gcc_jit_tls_model model);
@@ -1249,6 +1257,8 @@ protected:
   string *m_reg_name;
   enum gcc_jit_tls_model m_tls_model;
   unsigned m_alignment;
+  std::vector<std::pair<gcc_jit_variable_attribute,
+	      std::string>> m_string_attributes;
 };
 
 class param : public lvalue
@@ -1342,6 +1352,10 @@ public:
 
   rvalue *get_address (location *loc);
 
+  void add_attribute (gcc_jit_fn_attribute attribute);
+  void add_string_attribute (gcc_jit_fn_attribute attribute, const char* value);
+  void add_integer_array_attribute (gcc_jit_fn_attribute attribute, const int* value, size_t length);
+
 private:
   string * make_debug_string () final override;
   void write_reproducer (reproducer &r) final override;
@@ -1357,6 +1371,9 @@ private:
   auto_vec<local *> m_locals;
   auto_vec<block *> m_blocks;
   type *m_fn_ptr_type;
+  std::vector<gcc_jit_fn_attribute> m_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::string>> m_string_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::vector<int>>> m_int_array_attributes;
 };
 
 class block : public memento
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 0451b4df7f9..337d4ea3b95 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -3965,6 +3965,51 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
   return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
 }
 
+void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				gcc_jit_fn_attribute attribute)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+
+  func->add_attribute (attribute);
+}
+
+void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       gcc_jit_fn_attribute attribute,
+				       const char* value)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+
+  func->add_string_attribute (attribute, value);
+}
+
+/* This function adds an attribute with multiple integer values.  For example
+   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
+   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
+   and `2` in `values` (and set `length` to `2`). */
+void
+gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+					      gcc_jit_fn_attribute attribute,
+					      const int* values,
+					      size_t length)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
+
+  func->add_integer_array_attribute (attribute, values, length);
+}
+
+void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     gcc_jit_variable_attribute attribute,
+				     const char* value)
+{
+  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");
+
+  variable->add_string_attribute (attribute, value);
+}
+
 /* 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 749f6c24177..77aad2e8f20 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1999,6 +1999,55 @@ gcc_jit_vector_type_get_element_type (gcc_jit_vector_type *vector_type);
 extern gcc_jit_type *
 gcc_jit_type_unqualified (gcc_jit_type *type);
 
+#define LIBGCCJIT_HAVE_ATTRIBUTES
+
+/* Function attributes.  */
+enum gcc_jit_fn_attribute
+{
+  GCC_JIT_FN_ATTRIBUTE_ALIAS,
+  GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_NOINLINE,
+  GCC_JIT_FN_ATTRIBUTE_TARGET,
+  GCC_JIT_FN_ATTRIBUTE_USED,
+  GCC_JIT_FN_ATTRIBUTE_VISIBILITY,
+  GCC_JIT_FN_ATTRIBUTE_COLD,
+  GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE,
+  GCC_JIT_FN_ATTRIBUTE_PURE,
+  GCC_JIT_FN_ATTRIBUTE_CONST,
+  GCC_JIT_FN_ATTRIBUTE_WEAK,
+  GCC_JIT_FN_ATTRIBUTE_NONNULL,
+};
+
+/* Add an attribute to a function.  */
+extern void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				enum gcc_jit_fn_attribute attribute);
+
+extern void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       enum gcc_jit_fn_attribute attribute,
+				       const char* value);
+
+extern void
+gcc_jit_function_add_integer_array_attribute (
+  gcc_jit_function *func,
+  enum gcc_jit_fn_attribute attribute,
+  const int* value,
+  size_t length);
+
+/* Variable attributes.  */
+enum gcc_jit_variable_attribute
+{
+  GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY,
+};
+
+/* Add a string attribute to a variable.  */
+extern void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     enum gcc_jit_variable_attribute attribute,
+				     const char* value);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 8b90a0e2ff3..3bf8bb5d9b9 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -276,3 +276,11 @@ LIBGCCJIT_ABI_25 {
   global:
     gcc_jit_type_get_restrict;
 } LIBGCCJIT_ABI_24;
+
+LIBGCCJIT_ABI_26 {
+  global:
+    gcc_jit_function_add_attribute;
+    gcc_jit_function_add_string_attribute;
+    gcc_jit_lvalue_add_string_attribute;
+    gcc_jit_function_add_integer_array_attribute;
+} LIBGCCJIT_ABI_25;
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 8bf7e51c24f..657b454a003 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
 	pass "${asm_filename} output pattern test, ${dg-output-text}"
 	verbose "Passed test for output pattern ${dg-output-text}" 3
     }
+}
+
+# Assuming that a .s file has been written out named
+# OUTPUT_FILENAME, check that the argument doesn't match
+# the output file.
+proc jit-verify-assembler-output-not { args } {
+    verbose "jit-verify-assembler: $args"
+
+    set dg-output-text [lindex $args 0]
+    verbose "dg-output-text: ${dg-output-text}"
+
+    upvar 2 name name
+    verbose "name: $name"
+
+    upvar 2 prog prog
+    verbose "prog: $prog"
+    set asm_filename [jit-get-output-filename $prog]
+    verbose "  asm_filename: ${asm_filename}"
 
+    # Read the assembly file.
+    set f [open $asm_filename r]
+    set content [read $f]
+    close $f
+
+    # Verify that the assembly matches the regex.
+    if { [regexp ${dg-output-text} $content] } {
+	fail "${asm_filename} output pattern test, is ${content}, should match ${dg-output-text}"
+	verbose "Failed test for output pattern ${dg-output-text}" 3
+    } else {
+	pass "${asm_filename} output pattern test, ${dg-output-text}"
+	verbose "Passed test for output pattern ${dg-output-text}" 3
+    }
 }
+
+
 # Assuming that a .o file has been written out named
 # OUTPUT_FILENAME, invoke the driver to try to turn it into
 # an executable, and try to run the result.
diff --git a/gcc/testsuite/jit.dg/test-alias-attribute.c b/gcc/testsuite/jit.dg/test-alias-attribute.c
new file mode 100644
index 00000000000..eb29003dfc9
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-alias-attribute.c
@@ -0,0 +1,50 @@
+/* { 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-alias-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+void xxx () {}
+void f () __attribute__ ((alias ("xxx")));
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_EXPORTED,
+          void_type,
+          "xxx",
+          0, NULL,
+          0);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_IMPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_string_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_ALIAS, "xxx");
+
+  /* void xxx () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".set\\s+f,xxx" } } */
diff --git a/gcc/testsuite/jit.dg/test-always_inline-attribute.c b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
new file mode 100644
index 00000000000..5c3f386663f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
@@ -0,0 +1,153 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O0".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 0);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-always_inline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 gcc_jit_type *pint_type)
+{
+  /* The `a` function argument */
+  gcc_jit_param *a = gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          1, &a,
+          0);
+
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((always_inline))
+static inline int removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+static int not_removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+int foo () {
+  int x = 0;
+  x += removed(NULL);
+  x += not_removed(NULL);
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer (int_type);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, pint_type);
+  /* This one is to declare the function as "inline" */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_INLINE);
+  /* __attribute__ ((always_inline)) */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, pint_type);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(NULL); */
+  gcc_jit_rvalue *null = gcc_jit_context_null (ctxt, pint_type);
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 1, &null));
+  
+  /* x += not_removed(NULL); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 1, &null));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
new file mode 100644
index 00000000000..8dc7ec5a34b
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
@@ -0,0 +1,54 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the cold
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-cold-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+int
+__attribute__ ((cold))
+t()
+{
+  return -1;
+}
+
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "t",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(func_t, GCC_JIT_FN_ATTRIBUTE_COLD);
+  gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+  gcc_jit_rvalue *ret = gcc_jit_context_new_rvalue_from_int (ctxt,
+    int_type,
+    -1);
+
+  gcc_jit_block_end_with_return (block, NULL, ret);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "orl" } } */
diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
new file mode 100644
index 00000000000..c06742d163f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-const-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the const
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-const-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((const))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_CONST);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
new file mode 100644
index 00000000000..84933e60010
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
@@ -0,0 +1,114 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-noinline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((noinline))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo () {
+  int x = 0;
+  x += removed();
+  x += not_removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((no_inline)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_NOINLINE);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
new file mode 100644
index 00000000000..3306f890657
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
@@ -0,0 +1,94 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-nonnull.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__((nonnull(1)))
+int t(int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+	  GCC_JIT_FUNCTION_EXPORTED,
+	  int_type,
+	  "t",
+	  1, &a,
+	  0);
+  /* Adding `nonnull(1)` attribute. */
+  int indexes[1] = {1};
+  gcc_jit_function_add_integer_array_attribute (
+    func_t,
+    GCC_JIT_FN_ATTRIBUTE_NONNULL,
+    indexes,
+    1
+  );
+
+  /* if (!a) {
+    return -1;
+  } */
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func_t, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func_t, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func_t, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "if block" was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "testq" } } */
+/* { dg-final { jit-verify-assembler-output-not "-1" } } */
diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
new file mode 100644
index 00000000000..0c9ba1366e0
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the pure
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-pure-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((pure))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_PURE);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-restrict.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
similarity index 95%
rename from gcc/testsuite/jit.dg/test-restrict.c
rename to gcc/testsuite/jit.dg/test-restrict-attribute.c
index a6ac96324d2..7d7444b624f 100644
--- a/gcc/testsuite/jit.dg/test-restrict.c
+++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
@@ -73,5 +73,5 @@ void t(int *__restrict__ a, int *__restrict__ b, char *__restrict__ c) {
 }
 
 /* { dg-final { jit-verify-output-file-was-created "" } } */
-/* { dg-final { jit-verify-assembler-output "addl	%eax, (%rdi)
-	addl	%eax, (%rsi)" } } */
+/* { dg-final { jit-verify-assembler-output "addl\\s+%eax,\\s+(%rdi)
+\\s+addl\\s+%eax,\\s+(%rsi)" } } */
diff --git a/gcc/testsuite/jit.dg/test-used-attribute.c b/gcc/testsuite/jit.dg/test-used-attribute.c
new file mode 100644
index 00000000000..cb20952c687
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-used-attribute.c
@@ -0,0 +1,112 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-used-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__((used))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo() {
+  int x = 0;
+  x += not_removed();
+  x += removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((used)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_USED);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-variable-attribute.c b/gcc/testsuite/jit.dg/test-variable-attribute.c
new file mode 100644
index 00000000000..ea854ff4a9f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-variable-attribute.c
@@ -0,0 +1,46 @@
+/* { 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-variable-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+int PRIVATE __attribute__ ((visibility ("hidden"))) = 42;
+int PUBLIC = 12;
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `PRIVATE` variable. */
+  gcc_jit_lvalue *private = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PRIVATE");
+  gcc_jit_lvalue_add_string_attribute (private,
+    GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, "hidden");
+  gcc_jit_rvalue *rval = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 42);
+  gcc_jit_global_set_initializer_rvalue (private, rval);
+
+  /* Creating the `PUBLIC` variable. */
+  gcc_jit_lvalue *public = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PUBLIC");
+  gcc_jit_rvalue *rval2 = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 12);
+  gcc_jit_global_set_initializer_rvalue (public, rval2);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".hidden\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output-not ".hidden\\s+PUBLIC" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PUBLIC" } } */
diff --git a/gcc/testsuite/jit.dg/test-weak-attribute.c b/gcc/testsuite/jit.dg/test-weak-attribute.c
new file mode 100644
index 00000000000..546ade1c3c4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-weak-attribute.c
@@ -0,0 +1,41 @@
+/* { 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-weak-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__ ((weak))
+void f () {}
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_WEAK);
+
+  /* void f () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (f_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".weak\\s+f" } } */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-15 16:53 [PATCH] Add support for function attributes and variable attributes Guillaume Gomez
@ 2023-11-15 16:56 ` Antoni Boucher
  2023-11-23 21:52   ` Guillaume Gomez
  2024-01-09 19:59 ` David Malcolm
  1 sibling, 1 reply; 16+ messages in thread
From: Antoni Boucher @ 2023-11-15 16:56 UTC (permalink / raw)
  To: Guillaume Gomez, jit, gcc-patches, David Malcolm

David: another thing I remember you mentioned when you reviewed an
earlier version of this patch is the usage of `std::pair`.
I can't find where you said that, but I remember you mentioned that we
should use a struct instead.
Can you please elaborate again?
Thanks.

On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> Hi,
> 
> This patch adds the (incomplete) support for function and variable
> attributes. The added attributes are the ones we're using in
> rustc_codegen_gcc but all the groundwork is done to add more (and we
> will very likely add more as we didn't add all the ones we use in
> rustc_codegen_gcc yet).
> 
> The only big question with this patch is about `inline`. We currently
> handle it as an attribute because it is more convenient for us but is
> it ok or should we create a separate function to mark a function as
> inlined?
> 
> Thanks in advance for the review.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-15 16:56 ` Antoni Boucher
@ 2023-11-23 21:52   ` Guillaume Gomez
  2023-11-23 21:59     ` Antoni Boucher
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2023-11-23 21:52 UTC (permalink / raw)
  To: Antoni Boucher; +Cc: jit, gcc-patches, David Malcolm

Ping David. :)

Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a écrit :
>
> David: another thing I remember you mentioned when you reviewed an
> earlier version of this patch is the usage of `std::pair`.
> I can't find where you said that, but I remember you mentioned that we
> should use a struct instead.
> Can you please elaborate again?
> Thanks.
>
> On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > Hi,
> >
> > This patch adds the (incomplete) support for function and variable
> > attributes. The added attributes are the ones we're using in
> > rustc_codegen_gcc but all the groundwork is done to add more (and we
> > will very likely add more as we didn't add all the ones we use in
> > rustc_codegen_gcc yet).
> >
> > The only big question with this patch is about `inline`. We currently
> > handle it as an attribute because it is more convenient for us but is
> > it ok or should we create a separate function to mark a function as
> > inlined?
> >
> > Thanks in advance for the review.
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-23 21:52   ` Guillaume Gomez
@ 2023-11-23 21:59     ` Antoni Boucher
  2023-11-30  9:55       ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: Antoni Boucher @ 2023-11-23 21:59 UTC (permalink / raw)
  To: Guillaume Gomez; +Cc: jit, gcc-patches, David Malcolm

David: I found back the comment you made. Here it is:

   I see you have patches to add function and variable attributes; I
   wonder if this would be cleaner internally if there was a
   recording::attribute class, rather than the std::pair currently in
   use
   (some attributes have int arguments rather than string, others have
   multiple args).
   
   I also wondered if a "gcc_jit_attribute" type could be exposed to
   the
   user, e.g.:
   
     attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
     attr2 = gcc_jit_context_new_attribute_with_string (ctxt, "alias",
   "__foo");
     gcc_jit_function_add_attribute (ctxt, attr1);
     gcc_jit_function_add_attribute (ctxt, attr2);
   
   or somesuch?  But I think the API you currently have is OK. 
   
On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> Ping David. :)
> 
> Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> écrit :
> > 
> > David: another thing I remember you mentioned when you reviewed an
> > earlier version of this patch is the usage of `std::pair`.
> > I can't find where you said that, but I remember you mentioned that
> > we
> > should use a struct instead.
> > Can you please elaborate again?
> > Thanks.
> > 
> > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > Hi,
> > > 
> > > This patch adds the (incomplete) support for function and
> > > variable
> > > attributes. The added attributes are the ones we're using in
> > > rustc_codegen_gcc but all the groundwork is done to add more (and
> > > we
> > > will very likely add more as we didn't add all the ones we use in
> > > rustc_codegen_gcc yet).
> > > 
> > > The only big question with this patch is about `inline`. We
> > > currently
> > > handle it as an attribute because it is more convenient for us
> > > but is
> > > it ok or should we create a separate function to mark a function
> > > as
> > > inlined?
> > > 
> > > Thanks in advance for the review.
> > 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-23 21:59     ` Antoni Boucher
@ 2023-11-30  9:55       ` Guillaume Gomez
  2023-12-07 17:13         ` Antoni Boucher
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2023-11-30  9:55 UTC (permalink / raw)
  To: Antoni Boucher; +Cc: jit, gcc-patches, David Malcolm

[-- Attachment #1: Type: text/plain, Size: 2206 bytes --]

Ping David. :)

Le jeu. 23 nov. 2023 à 22:59, Antoni Boucher <bouanto@zoho.com> a écrit :

> David: I found back the comment you made. Here it is:
>
>    I see you have patches to add function and variable attributes; I
>    wonder if this would be cleaner internally if there was a
>    recording::attribute class, rather than the std::pair currently in
>    use
>    (some attributes have int arguments rather than string, others have
>    multiple args).
>
>    I also wondered if a "gcc_jit_attribute" type could be exposed to
>    the
>    user, e.g.:
>
>      attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
>      attr2 = gcc_jit_context_new_attribute_with_string (ctxt, "alias",
>    "__foo");
>      gcc_jit_function_add_attribute (ctxt, attr1);
>      gcc_jit_function_add_attribute (ctxt, attr2);
>
>    or somesuch?  But I think the API you currently have is OK.
>
> On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> > Ping David. :)
> >
> > Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> > écrit :
> > >
> > > David: another thing I remember you mentioned when you reviewed an
> > > earlier version of this patch is the usage of `std::pair`.
> > > I can't find where you said that, but I remember you mentioned that
> > > we
> > > should use a struct instead.
> > > Can you please elaborate again?
> > > Thanks.
> > >
> > > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > > Hi,
> > > >
> > > > This patch adds the (incomplete) support for function and
> > > > variable
> > > > attributes. The added attributes are the ones we're using in
> > > > rustc_codegen_gcc but all the groundwork is done to add more (and
> > > > we
> > > > will very likely add more as we didn't add all the ones we use in
> > > > rustc_codegen_gcc yet).
> > > >
> > > > The only big question with this patch is about `inline`. We
> > > > currently
> > > > handle it as an attribute because it is more convenient for us
> > > > but is
> > > > it ok or should we create a separate function to mark a function
> > > > as
> > > > inlined?
> > > >
> > > > Thanks in advance for the review.
> > >
>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-30  9:55       ` Guillaume Gomez
@ 2023-12-07 17:13         ` Antoni Boucher
  2023-12-09 11:12           ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: Antoni Boucher @ 2023-12-07 17:13 UTC (permalink / raw)
  To: Guillaume Gomez; +Cc: jit, gcc-patches, David Malcolm

It seems like you forgot to prefix the commit message with "libgccjit:
".

On Thu, 2023-11-30 at 10:55 +0100, Guillaume Gomez wrote:
> Ping David. :)
> 
> Le jeu. 23 nov. 2023 à 22:59, Antoni Boucher <bouanto@zoho.com> a
> écrit :
> > David: I found back the comment you made. Here it is:
> > 
> >    I see you have patches to add function and variable attributes;
> > I
> >    wonder if this would be cleaner internally if there was a
> >    recording::attribute class, rather than the std::pair currently
> > in
> >    use
> >    (some attributes have int arguments rather than string, others
> > have
> >    multiple args).
> > 
> >    I also wondered if a "gcc_jit_attribute" type could be exposed
> > to
> >    the
> >    user, e.g.:
> > 
> >      attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
> >      attr2 = gcc_jit_context_new_attribute_with_string (ctxt,
> > "alias",
> >    "__foo");
> >      gcc_jit_function_add_attribute (ctxt, attr1);
> >      gcc_jit_function_add_attribute (ctxt, attr2);
> > 
> >    or somesuch?  But I think the API you currently have is OK. 
> > 
> > On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> > > Ping David. :)
> > > 
> > > Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> > > écrit :
> > > > 
> > > > David: another thing I remember you mentioned when you reviewed
> > > > an
> > > > earlier version of this patch is the usage of `std::pair`.
> > > > I can't find where you said that, but I remember you mentioned
> > > > that
> > > > we
> > > > should use a struct instead.
> > > > Can you please elaborate again?
> > > > Thanks.
> > > > 
> > > > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > > > Hi,
> > > > > 
> > > > > This patch adds the (incomplete) support for function and
> > > > > variable
> > > > > attributes. The added attributes are the ones we're using in
> > > > > rustc_codegen_gcc but all the groundwork is done to add more
> > > > > (and
> > > > > we
> > > > > will very likely add more as we didn't add all the ones we
> > > > > use in
> > > > > rustc_codegen_gcc yet).
> > > > > 
> > > > > The only big question with this patch is about `inline`. We
> > > > > currently
> > > > > handle it as an attribute because it is more convenient for
> > > > > us
> > > > > but is
> > > > > it ok or should we create a separate function to mark a
> > > > > function
> > > > > as
> > > > > inlined?
> > > > > 
> > > > > Thanks in advance for the review.
> > > > 
> > 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-12-07 17:13         ` Antoni Boucher
@ 2023-12-09 11:12           ` Guillaume Gomez
  2023-12-18 22:27             ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2023-12-09 11:12 UTC (permalink / raw)
  To: Antoni Boucher; +Cc: jit, gcc-patches, David Malcolm

[-- Attachment #1: Type: text/plain, Size: 2794 bytes --]

Added it.

Le jeu. 7 déc. 2023 à 18:13, Antoni Boucher <bouanto@zoho.com> a écrit :
>
> It seems like you forgot to prefix the commit message with "libgccjit:
> ".
>
> On Thu, 2023-11-30 at 10:55 +0100, Guillaume Gomez wrote:
> > Ping David. :)
> >
> > Le jeu. 23 nov. 2023 à 22:59, Antoni Boucher <bouanto@zoho.com> a
> > écrit :
> > > David: I found back the comment you made. Here it is:
> > >
> > >    I see you have patches to add function and variable attributes;
> > > I
> > >    wonder if this would be cleaner internally if there was a
> > >    recording::attribute class, rather than the std::pair currently
> > > in
> > >    use
> > >    (some attributes have int arguments rather than string, others
> > > have
> > >    multiple args).
> > >
> > >    I also wondered if a "gcc_jit_attribute" type could be exposed
> > > to
> > >    the
> > >    user, e.g.:
> > >
> > >      attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
> > >      attr2 = gcc_jit_context_new_attribute_with_string (ctxt,
> > > "alias",
> > >    "__foo");
> > >      gcc_jit_function_add_attribute (ctxt, attr1);
> > >      gcc_jit_function_add_attribute (ctxt, attr2);
> > >
> > >    or somesuch?  But I think the API you currently have is OK.
> > >
> > > On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> > > > Ping David. :)
> > > >
> > > > Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> > > > écrit :
> > > > >
> > > > > David: another thing I remember you mentioned when you reviewed
> > > > > an
> > > > > earlier version of this patch is the usage of `std::pair`.
> > > > > I can't find where you said that, but I remember you mentioned
> > > > > that
> > > > > we
> > > > > should use a struct instead.
> > > > > Can you please elaborate again?
> > > > > Thanks.
> > > > >
> > > > > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > > > > Hi,
> > > > > >
> > > > > > This patch adds the (incomplete) support for function and
> > > > > > variable
> > > > > > attributes. The added attributes are the ones we're using in
> > > > > > rustc_codegen_gcc but all the groundwork is done to add more
> > > > > > (and
> > > > > > we
> > > > > > will very likely add more as we didn't add all the ones we
> > > > > > use in
> > > > > > rustc_codegen_gcc yet).
> > > > > >
> > > > > > The only big question with this patch is about `inline`. We
> > > > > > currently
> > > > > > handle it as an attribute because it is more convenient for
> > > > > > us
> > > > > > but is
> > > > > > it ok or should we create a separate function to mark a
> > > > > > function
> > > > > > as
> > > > > > inlined?
> > > > > >
> > > > > > Thanks in advance for the review.
> > > > >
> > >
>

[-- Attachment #2: 0001-PATCH-Add-support-for-function-attributes-and-variab.patch --]
[-- Type: text/x-patch, Size: 87045 bytes --]

From df75f0eb8aacba249b6e791603752e35778951a4 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 20 Jun 2022 14:34:39 -0400
Subject: [PATCH] libgccjit: Add support for function attributes and variable
 attributes.

gcc/jit/ChangeLog:

	* dummy-frontend.cc (handle_alias_attribute): New function.
	(handle_always_inline_attribute): New function.
	(handle_cold_attribute): New function.
	(handle_fnspec_attribute): New function.
	(handle_format_arg_attribute): New function.
	(handle_format_attribute): New function.
	(handle_noinline_attribute): New function.
	(handle_target_attribute): New function.
	(handle_used_attribute): New function.
	(handle_visibility_attribute): New function.
	(handle_weak_attribute): New function.
	(handle_alias_ifunc_attribute): New function.
	* jit-playback.cc (fn_attribute_to_string): New function.
	(variable_attribute_to_string): New function.
	(global_new_decl): Add attributes support.
	(set_variable_attribute): New function.
	(new_global): Add attributes support.
	(new_global_initialized): Add attributes support.
	(new_local): Add attributes support.
	* jit-playback.h (fn_attribute_to_string): New function.
	(set_variable_attribute): New function.
	* jit-recording.cc (recording::lvalue::add_attribute): New function.
	(recording::function::function): New function.
	(recording::function::write_to_dump): Add attributes support.
	(recording::function::add_attribute): New function.
	(recording::function::add_string_attribute): New function.
	(recording::function::add_integer_array_attribute): New function.
	(recording::global::replay_into): Add attributes support.
	(recording::local::replay_into): Add attributes support.
	* libgccjit.cc (gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(gcc_jit_lvalue_add_attribute): New function.
	* libgccjit.h (enum gcc_jit_fn_attribute): New enum.
	(gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(enum gcc_jit_variable_attribute): New function.
	(gcc_jit_lvalue_add_string_attribute): New function.
	* libgccjit.map: Declare new functions.

gcc/testsuite/ChangeLog:

	* jit.dg/jit.exp: Add `jit-verify-assembler-output-not` test command.
	* jit.dg/test-restrict.c: New test.
	* jit.dg/test-restrict-attribute.c: New test.
	* jit.dg/test-alias-attribute.c: New test.
	* jit.dg/test-always_inline-attribute.c: New test.
	* jit.dg/test-cold-attribute.c: New test.
	* jit.dg/test-const-attribute.c: New test.
	* jit.dg/test-noinline-attribute.c: New test.
	* jit.dg/test-nonnull-attribute.c: New test.
	* jit.dg/test-pure-attribute.c: New test.
	* jit.dg/test-used-attribute.c: New test.
	* jit.dg/test-variable-attribute.c: New test.
	* jit.dg/test-weak-attribute.c: New test.

gcc/jit/ChangeLog:
	* docs/topics/compatibility.rst: Add documentation for LIBGCCJIT_ABI_26.
	* docs/topics/types.rst: Add documentation for new functions.

Co-authored-by: Antoni Boucher <bouanto@zoho.com>
Signed-off-by: Guillaume Gomez <guillaume1.gomez@gmail.com>
---
 gcc/jit/docs/topics/compatibility.rst         |  12 +
 gcc/jit/docs/topics/types.rst                 |  77 +++
 gcc/jit/dummy-frontend.cc                     | 504 ++++++++++++++++--
 gcc/jit/jit-playback.cc                       | 165 +++++-
 gcc/jit/jit-playback.h                        |  37 +-
 gcc/jit/jit-recording.cc                      | 166 +++++-
 gcc/jit/jit-recording.h                       |  19 +-
 gcc/jit/libgccjit.cc                          |  45 ++
 gcc/jit/libgccjit.h                           |  49 ++
 gcc/jit/libgccjit.map                         |   8 +
 gcc/testsuite/jit.dg/jit.exp                  |  33 ++
 gcc/testsuite/jit.dg/test-alias-attribute.c   |  50 ++
 .../jit.dg/test-always_inline-attribute.c     | 153 ++++++
 gcc/testsuite/jit.dg/test-cold-attribute.c    |  54 ++
 gcc/testsuite/jit.dg/test-const-attribute.c   | 134 +++++
 .../jit.dg/test-noinline-attribute.c          | 114 ++++
 gcc/testsuite/jit.dg/test-nonnull-attribute.c |  94 ++++
 gcc/testsuite/jit.dg/test-pure-attribute.c    | 134 +++++
 ...t-restrict.c => test-restrict-attribute.c} |   4 +-
 gcc/testsuite/jit.dg/test-used-attribute.c    | 112 ++++
 .../jit.dg/test-variable-attribute.c          |  46 ++
 gcc/testsuite/jit.dg/test-weak-attribute.c    |  41 ++
 22 files changed, 1986 insertions(+), 65 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-alias-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-always_inline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-cold-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-const-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-noinline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-nonnull-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-pure-attribute.c
 rename gcc/testsuite/jit.dg/{test-restrict.c => test-restrict-attribute.c} (95%)
 create mode 100644 gcc/testsuite/jit.dg/test-used-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-variable-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-weak-attribute.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index ebede440ee4..b4a6e997941 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -378,3 +378,15 @@ alignment of a variable:
 --------------------
 ``LIBGCCJIT_ABI_25`` covers the addition of
 :func:`gcc_jit_type_get_restrict`
+
+.. _LIBGCCJIT_ABI_26:
+
+``LIBGCCJIT_ABI_26``
+--------------------
+``LIBGCCJIT_ABI_26`` covers the addition of functions to set attributes
+on functions and variables:
+
+  * :func:`gcc_jit_function_add_attribute`
+  * :func:`gcc_jit_function_add_string_attribute`
+  * :func:`gcc_jit_function_add_integer_array_attribute`
+  * :func:`gcc_jit_lvalue_add_string_attribute`
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index d8c1d15d69d..6c72c99cbd9 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -553,3 +553,80 @@ Reflection API
    .. code-block:: c
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
+
+.. function::  void\
+               gcc_jit_function_add_attribute (gcc_jit_function *func,
+                                               enum gcc_jit_fn_attribute attribute)
+
+     Add an attribute ``attribute`` to a function ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__((always_inline))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+                                                      enum gcc_jit_fn_attribute attribute,
+                                                      const char *value)
+
+     Add a string attribute ``attribute`` with value ``value`` to a function
+     ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((alias ("xxx")))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+                                                             enum gcc_jit_fn_attribute attribute,
+                                                             const int *value,
+                                                             size_t length)
+
+     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
+     function ``func``. The integer values must be the same as you would write
+     them in a C code.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((nonnull (1, 2)))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+                                                    enum gcc_jit_fn_attribute attribute,
+                                                    const char *value)
+
+     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index a729086bafb..898b4d6e7f8 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -29,30 +29,42 @@ along with GCC; see the file COPYING3.  If not see
 #include "options.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "cgraph.h"
+#include "target.h"
 
 #include <mpfr.h>
 
 /* Attribute handling.  */
 
-static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
-static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
+static tree handle_always_inline_attribute (tree *, tree, tree, int,
+					    bool *);
+static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_const_attribute (tree *, tree, tree, int, bool *);
+static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_attribute (tree *, tree, tree, int, bool *);
+static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
-static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 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_sentinel_attribute (tree *, tree, tree, int, bool *);
-static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
-static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
-static tree ignore_attribute (tree *, tree, tree, int, bool *);
+static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
+static tree handle_target_attribute (tree *, tree, tree, int, bool *);
+static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
+static tree handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree handle_visibility_attribute (tree *, tree, tree, int,
+					 bool *);
+static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 
-static tree handle_format_attribute (tree *, tree, tree, int, bool *);
-static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
-static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -81,55 +93,98 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
 static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 {
   ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("alloc_align", true, true, true),
+  ATTR_EXCL ("alloc_size", true, true, true),
+  ATTR_EXCL ("malloc", true, true, true),
   ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("pure", true, true, true),
   ATTR_EXCL (NULL, false, false, false)
 };
 
+static const struct attribute_spec::exclusions attr_inline_exclusions[] =
+{
+  ATTR_EXCL ("noinline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+{
+  ATTR_EXCL ("cold", true, true, true),
+  ATTR_EXCL ("hot", true, true, true),
+  ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_noinline_exclusions[] =
+{
+  ATTR_EXCL ("always_inline", true, true, true),
+  ATTR_EXCL ("gnu_inline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 /* Table of machine-independent attributes supported in libgccjit.  */
 const struct attribute_spec jit_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "noreturn",               0, 0, true,  false, false, false,
-			      handle_noreturn_attribute,
-			      attr_noreturn_exclusions },
-  { "leaf",		      0, 0, true,  false, false, false,
-			      handle_leaf_attribute, NULL },
+  { "alias",		      1, 1, true,  false, false, false,
+			      handle_alias_attribute, NULL },
+  { "always_inline",	      0, 0, true,  false, false, false,
+			      handle_always_inline_attribute,
+			      attr_inline_exclusions },
+  { "cold",		      0, 0, true,  false, false, false,
+			      handle_cold_attribute,
+			      attr_cold_hot_exclusions },
   /* The same comments as for noreturn attributes apply to const ones.  */
-  { "const",                  0, 0, true,  false, false, false,
+  { "const",		      0, 0, true,  false, false, false,
 			      handle_const_attribute,
 			      attr_const_pure_exclusions },
-  { "malloc",                 0, 0, true,  false, false, false,
+  { "fn spec",		      1, 1, false, true, true, false,
+			      handle_fnspec_attribute, NULL },
+
+  { "leaf",		      0, 0, true,  false, false, false,
+			      handle_leaf_attribute, NULL },
+  { "malloc",		      0, 0, true,  false, false, false,
 			      handle_malloc_attribute, NULL },
-  { "pure",                   0, 0, true,  false, false, false,
-			      handle_pure_attribute,
-			      attr_const_pure_exclusions },
-  { "no vops",                0, 0, true,  false, false, false,
+  { "noreturn",		      0, 0, true,  false, false, false,
+			      handle_noreturn_attribute,
+			      attr_noreturn_exclusions },
+  { "no vops",		      0, 0, true,  false, false, false,
 			      handle_novops_attribute, NULL },
-  { "nonnull",                0, -1, false, true, true, false,
+  { "noinline",		      0, 0, true,  false, false, false,
+			      handle_noinline_attribute,
+			      attr_noinline_exclusions },
+  { "nonnull",		      0, -1, false, true, true, false,
 			      handle_nonnull_attribute, NULL },
-  { "nothrow",                0, 0, true,  false, false, false,
+  { "nothrow",		      0, 0, true,  false, false, false,
 			      handle_nothrow_attribute, NULL },
   { "patchable_function_entry", 1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
-  { "returns_twice",          0, 0, true,  false, false, false,
+  { "pure",		      0, 0, true,  false, false, false,
+			      handle_pure_attribute,
+			      attr_const_pure_exclusions },
+  { "returns_twice",	      0, 0, true,  false, false, false,
 			      handle_returns_twice_attribute,
 			      attr_returns_twice_exclusions },
-  { "sentinel",               0, 1, false, true, true, false,
+  { "sentinel",		      0, 1, false, true, true, false,
 			      handle_sentinel_attribute, NULL },
-  { "type generic",           0, 0, false, true, true, false,
+  { "target",		      1, -1, true, false, false, false,
+			      handle_target_attribute, NULL },
+  { "type generic",	      0, 0, false, true, true, false,
 			      handle_type_generic_attribute, NULL },
-  { "fn spec",	 	      1, 1, false, true, true, false,
-			      handle_fnspec_attribute, NULL },
   { "transaction_pure",	      0, 0, false, true, true, false,
 			      handle_transaction_pure_attribute, NULL },
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
-  { "*tm regparm",            0, 0, false, true, true, false,
+  { "*tm regparm",	      0, 0, false, true, true, false,
 			      ignore_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+  { "used",		      0, 0, true,  false, false, false,
+			      handle_used_attribute, NULL },
+  { "visibility",	      1, 1, false, false, false, false,
+			      handle_visibility_attribute, NULL },
+  { "weak",		      0, 0, true,  false, false, false,
+			      handle_weak_attribute, NULL },
+  { NULL,		      0, 0, false, false, false, false, NULL, NULL }
 };
 
 /* Give the specifications for the format attributes, used by C and all
@@ -139,11 +194,11 @@ const struct attribute_spec jit_format_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "format",                 3, 3, false, true,  true, false,
+  { "format",		      3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
-  { "format_arg",             1, 1, false, true,  true, false,
+  { "format_arg",	      1, 1, false, true,  true, false,
 			      handle_format_arg_attribute, NULL },
-  { NULL,                     0, 0, false, false, false, false, NULL, NULL }
+  { NULL,		      0, 0, false, false, false, false, NULL, NULL }
 };
 
 /* Attribute handlers.  */
@@ -480,6 +535,385 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle an "visibility" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_visibility_attribute (tree *node, tree name, tree args,
+			     int ARG_UNUSED (flags),
+			     bool *ARG_UNUSED (no_add_attrs))
+{
+  tree decl = *node;
+  tree id = TREE_VALUE (args);
+  enum symbol_visibility vis;
+
+  if (TYPE_P (*node))
+    {
+      if (TREE_CODE (*node) == ENUMERAL_TYPE)
+	/* OK.  */;
+      else if (!RECORD_OR_UNION_TYPE_P (*node))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
+		   name);
+	  return NULL_TREE;
+	}
+      else if (TYPE_FIELDS (*node))
+	{
+	  error ("%qE attribute ignored because %qT is already defined",
+		 name, *node);
+	  return NULL_TREE;
+	}
+    }
+  else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("visibility argument not a string");
+      return NULL_TREE;
+    }
+
+  /*  If this is a type, set the visibility on the type decl.  */
+  if (TYPE_P (decl))
+    {
+      decl = TYPE_NAME (decl);
+      if (!decl)
+	return NULL_TREE;
+      if (TREE_CODE (decl) == IDENTIFIER_NODE)
+	{
+	   warning (OPT_Wattributes, "%qE attribute ignored on types",
+		    name);
+	   return NULL_TREE;
+	}
+    }
+
+  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
+    vis = VISIBILITY_DEFAULT;
+  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
+    vis = VISIBILITY_INTERNAL;
+  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
+    vis = VISIBILITY_HIDDEN;
+  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
+    vis = VISIBILITY_PROTECTED;
+  else
+    {
+      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
+	     name, "default", "hidden", "protected", "internal");
+      vis = VISIBILITY_DEFAULT;
+    }
+
+  if (DECL_VISIBILITY_SPECIFIED (decl)
+      && vis != DECL_VISIBILITY (decl))
+    {
+      tree attributes = (TYPE_P (*node)
+			 ? TYPE_ATTRIBUTES (*node)
+			 : DECL_ATTRIBUTES (decl));
+      if (lookup_attribute ("visibility", attributes))
+	error ("%qD redeclared with different visibility", decl);
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllimport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllimport");
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllexport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllexport");
+    }
+
+  DECL_VISIBILITY (decl) = vis;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+
+  /* Go ahead and attach the attribute to the node as well.  This is needed
+     so we can determine whether we have VISIBILITY_DEFAULT because the
+     visibility was not specified, or because it was explicitly overridden
+     from the containing scope.  */
+
+  return NULL_TREE;
+}
+
+/* Handle a "always_inline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_always_inline_attribute (tree *node, tree name,
+				tree ARG_UNUSED (args),
+				int ARG_UNUSED (flags),
+				bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      if (lookup_attribute ("noinline", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "noinline");
+	  *no_add_attrs = true;
+	}
+      else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "target_clones");
+	  *no_add_attrs = true;
+	}
+      else
+	/* Set the attribute and mark it for disregarding inline
+	   limits.  */
+	DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "cold" and attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      || TREE_CODE (*node) == LABEL_DECL)
+    {
+      /* Attribute cold processing is done later with lookup_attribute.  */
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "noinline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_noinline_attribute (tree *node, tree name,
+			   tree ARG_UNUSED (args),
+			   int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with attribute %qs", name, "always_inline");
+	  *no_add_attrs = true;
+	}
+      else
+	DECL_UNINLINABLE (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "weak" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_weak_attribute (tree *node, tree name,
+		       tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags),
+		       bool * ARG_UNUSED (no_add_attrs))
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && DECL_DECLARED_INLINE_P (*node))
+    {
+      warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weak", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (VAR_OR_FUNCTION_DECL_P (*node))
+    declare_weak (*node);
+  else
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+
+  return NULL_TREE;
+}
+
+/* Handle a "target" attribute.  */
+
+static tree
+handle_target_attribute (tree *node, tree name, tree args, int flags,
+			 bool *no_add_attrs)
+{
+  /* Ensure we have a function type.  */
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
+		   "with %qs attribute", name, "target_clones");
+      *no_add_attrs = true;
+    }
+  else if (! targetm.target_option.valid_attribute_p (*node, name, args,
+						      flags))
+    *no_add_attrs = true;
+
+  /* Check that there's no empty string in values of the attribute.  */
+  for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
+    {
+      tree value = TREE_VALUE (t);
+      if (TREE_CODE (value) == STRING_CST
+	  && TREE_STRING_LENGTH (value) == 1
+	  && TREE_STRING_POINTER (value)[0] == '\0')
+	{
+	  warning (OPT_Wattributes, "empty string in attribute %<target%>");
+	  *no_add_attrs = true;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "used" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  if (TREE_CODE (node) == FUNCTION_DECL
+      || (VAR_P (node) && TREE_STATIC (node))
+      || (TREE_CODE (node) == TYPE_DECL))
+    {
+      TREE_USED (node) = 1;
+      DECL_PRESERVE_P (node) = 1;
+      if (VAR_P (node))
+	DECL_READ_P (node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler, except that IS_ALIAS tells us
+   whether this is an alias as opposed to ifunc attribute.  */
+
+static tree
+handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
+			      bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (!is_alias || !VAR_P (decl)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
+      /* A static variable declaration is always a tentative definition,
+	 but the alias is a non-tentative definition which overrides.  */
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
+    {
+      error ("%q+D defined both normally and as %qE attribute", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (!is_alias
+	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
+	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
+    {
+      error ("weak %q+D cannot be defined %qE", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Note that the very first time we process a nested declaration,
+     decl_function_context will not be set.  Indeed, *would* never
+     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
+     we do below.  After such frobbery, pushdecl would set the context.
+     In any case, this is never what we want.  */
+  else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
+    {
+      tree id;
+
+      id = TREE_VALUE (args);
+      if (TREE_CODE (id) != STRING_CST)
+	{
+	  error ("attribute %qE argument not a string", name);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+      id = get_identifier (TREE_STRING_POINTER (id));
+      /* This counts as a use of the object pointed to.  */
+      TREE_USED (id) = 1;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	DECL_INITIAL (decl) = error_mark_node;
+      else
+	TREE_STATIC (decl) = 1;
+
+      if (!is_alias)
+	{
+	  /* ifuncs are also aliases, so set that attribute too.  */
+	  DECL_ATTRIBUTES (decl)
+	    = tree_cons (get_identifier ("alias"), args,
+			 DECL_ATTRIBUTES (decl));
+	  DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
+					      NULL, DECL_ATTRIBUTES (decl));
+	}
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  if (decl_in_symtab_p (*node))
+    {
+      struct symtab_node *n = symtab_node::get (decl);
+      if (n && n->refuse_visibility_changes)
+	error ("%+qD declared %qs after being used",
+	       decl, is_alias ? "alias" : "ifunc");
+    }
+
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_alias_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
+}
+
 /* (end of attribute-handling).  */
 
 /* Language-dependent contents of a type.  */
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index 18cc4da25b8..d8dbc5aac61 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #define INCLUDE_MUTEX
+#include "libgccjit.h"
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -499,6 +500,50 @@ new_param (location *loc,
   return new param (this, inner);
 }
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_FN_ATTRIBUTE_ALIAS:
+      return "alias";
+    case GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE:
+      return "always_inline";
+    case GCC_JIT_FN_ATTRIBUTE_INLINE:
+      return NULL;
+    case GCC_JIT_FN_ATTRIBUTE_NOINLINE:
+      return "noinline";
+    case GCC_JIT_FN_ATTRIBUTE_TARGET:
+      return "target";
+    case GCC_JIT_FN_ATTRIBUTE_USED:
+      return "used";
+    case GCC_JIT_FN_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_FN_ATTRIBUTE_COLD:
+      return "cold";
+    case GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE:
+      return "returns_twice";
+    case GCC_JIT_FN_ATTRIBUTE_PURE:
+      return "pure";
+    case GCC_JIT_FN_ATTRIBUTE_CONST:
+      return "const";
+    case GCC_JIT_FN_ATTRIBUTE_WEAK:
+      return "weak";
+    case GCC_JIT_FN_ATTRIBUTE_NONNULL:
+      return "nonnull";
+  }
+  return NULL;
+}
+
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+  }
+  return NULL;
+}
+
 /* Construct a playback::function instance.  */
 
 playback::function *
@@ -509,7 +554,13 @@ new_function (location *loc,
 	      const char *name,
 	      const auto_vec<param *> *params,
 	      int is_variadic,
-	      enum built_in_function builtin_id)
+	      enum built_in_function builtin_id,
+	      const std::vector<gcc_jit_fn_attribute> &attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::string>> &string_attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::vector<int>>>
+					  &int_array_attributes)
 {
   int i;
   param *param;
@@ -543,6 +594,8 @@ new_function (location *loc,
   DECL_RESULT (fndecl) = resdecl;
   DECL_CONTEXT (resdecl) = fndecl;
 
+  tree fn_attributes = NULL_TREE;
+
   if (builtin_id)
     {
       gcc_assert (loc == NULL);
@@ -588,12 +641,62 @@ new_function (location *loc,
       DECL_DECLARED_INLINE_P (fndecl) = 1;
 
       /* Add attribute "always_inline": */
-      DECL_ATTRIBUTES (fndecl) =
-	tree_cons (get_identifier ("always_inline"),
-		   NULL,
-		   DECL_ATTRIBUTES (fndecl));
+      fn_attributes = tree_cons (get_identifier ("always_inline"),
+				 NULL,
+				 fn_attributes);
+    }
+
+  /* All attributes need to be declared in `dummy-frontend.cc` and more
+     specifically in `jit_attribute_table`. */
+  for (auto attr: attributes)
+  {
+    if (attr == GCC_JIT_FN_ATTRIBUTE_INLINE)
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+    {
+      tree ident = get_identifier (attribute);
+      fn_attributes = tree_cons (ident, NULL_TREE, fn_attributes);
     }
+  }
 
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (ident)
+      fn_attributes = tree_cons (ident, attribute_value, fn_attributes);
+  }
+
+  for (auto attr: int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (!ident)
+      continue;
+
+    tree tree_list = NULL_TREE;
+    tree *p_tree_list = &tree_list;
+    for (auto value : values)
+    {
+      tree int_value = build_int_cst (integer_type_node, value);
+      *p_tree_list = build_tree_list (NULL, int_value);
+      p_tree_list = &TREE_CHAIN (*p_tree_list);
+    }
+    fn_attributes = tree_cons (ident, tree_list, fn_attributes);
+  }
+
+  decl_attributes (&fndecl, fn_attributes, 0);
   function *func = new function (this, fndecl, kind);
   m_functions.safe_push (func);
   return func;
@@ -607,7 +710,9 @@ global_new_decl (location *loc,
 		 enum gcc_jit_global_kind kind,
 		 type *type,
 		 const char *name,
-		 enum global_var_flags flags)
+		 enum global_var_flags flags,
+		 const std::vector<std::pair<gcc_jit_variable_attribute,
+					     std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -652,9 +757,32 @@ global_new_decl (location *loc,
   if (loc)
     set_tree_location (inner, loc);
 
+  set_variable_string_attribute (attributes, inner);
+
   return inner;
 }
 
+void
+playback::
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+  			      std::string>> &string_attributes,
+  tree decl)
+{
+  tree var_attributes = NULL_TREE;
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    tree ident = get_identifier (variable_attribute_to_string (name));
+    if (ident)
+      var_attributes = tree_cons (ident, attribute_value, var_attributes);
+  }
+  decl_attributes (&decl, var_attributes, 0);
+}
+
 /* In use by new_global and new_global_initialized.  */
 
 playback::lvalue *
@@ -674,10 +802,12 @@ new_global (location *loc,
 	    enum gcc_jit_global_kind kind,
 	    type *type,
 	    const char *name,
-	    enum global_var_flags flags)
+	    enum global_var_flags flags,
+	    const std::vector<std::pair<gcc_jit_variable_attribute,
+					std::string>> &attributes)
 {
   tree inner =
-    global_new_decl (loc, kind, type, name, flags);
+    global_new_decl (loc, kind, type, name, flags, attributes);
 
   return global_finalize_lvalue (inner);
 }
@@ -818,13 +948,15 @@ playback::context::
 new_global_initialized (location *loc,
 			enum gcc_jit_global_kind kind,
 			type *type,
-                        size_t element_size,
+			size_t element_size,
 			size_t initializer_num_elem,
 			const void *initializer,
 			const char *name,
-			enum global_var_flags flags)
+			enum global_var_flags flags,
+			const std::vector<std::pair<gcc_jit_variable_attribute,
+						    std::string>> &attributes)
 {
-  tree inner = global_new_decl (loc, kind, type, name, flags);
+  tree inner = global_new_decl (loc, kind, type, name, flags, attributes);
 
   vec<constructor_elt, va_gc> *constructor_elements = NULL;
 
@@ -1812,7 +1944,9 @@ playback::lvalue *
 playback::function::
 new_local (location *loc,
 	   type *type,
-	   const char *name)
+	   const char *name,
+	   const std::vector<std::pair<gcc_jit_variable_attribute,
+				       std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -1825,6 +1959,8 @@ new_local (location *loc,
   DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
   BIND_EXPR_VARS (m_inner_bind_expr) = inner;
 
+  set_variable_string_attribute (attributes, inner);
+
   if (loc)
     set_tree_location (inner, loc);
   return new lvalue (m_ctxt, inner);
@@ -1947,6 +2083,9 @@ postprocess ()
 
       current_function_decl = NULL;
     }
+    else
+      /* Add to cgraph to output aliases: */
+      rest_of_decl_compilation (m_inner_fndecl, true, 0);
 }
 
 /* Don't leak vec's internal buffer (in non-GC heap) when we are
@@ -3365,7 +3504,7 @@ void
 playback::context::
 init_types ()
 {
-  /* See lto_init() in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc 
+  /* See lto_init () in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc
      for reference. If TYPE_NAME is not set, debug info will not contain types */
 #define NAME_TYPE(t,n) \
 if (t) \
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index f9e29d0baec..a0e56520702 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -21,7 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef JIT_PLAYBACK_H
 #define JIT_PLAYBACK_H
 
+#include <string>
 #include <utility> // for std::pair
+#include <vector>
 
 #include "timevar.h"
 #include "varasm.h"
@@ -35,12 +37,21 @@ namespace gcc {
 
 namespace jit {
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr);
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr);
+
 /**********************************************************************
  Playback.
  **********************************************************************/
 
 namespace playback {
 
+void
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			      std::string>> &attributes,
+  tree decl);
+
 /* playback::context is an abstract base class.
 
    The two concrete subclasses are:
@@ -104,14 +115,22 @@ public:
 		const char *name,
 		const auto_vec<param *> *params,
 		int is_variadic,
-		enum built_in_function builtin_id);
+		enum built_in_function builtin_id,
+		const std::vector<gcc_jit_fn_attribute> &attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::string>> &string_attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::vector<int>>>
+					    &int_array_attributes);
 
   lvalue *
   new_global (location *loc,
 	      enum gcc_jit_global_kind kind,
 	      type *type,
 	      const char *name,
-	      enum global_var_flags flags);
+	      enum global_var_flags flags,
+	      const std::vector<std::pair<gcc_jit_variable_attribute,
+					  std::string>> &attributes);
 
   lvalue *
   new_global_initialized (location *loc,
@@ -121,7 +140,11 @@ public:
                           size_t initializer_num_elem,
                           const void *initializer,
 			  const char *name,
-			  enum global_var_flags flags);
+			  enum global_var_flags flags,
+			  const std::vector<std::pair<
+					    gcc_jit_variable_attribute,
+					    std::string>>
+					    &attributes);
 
   rvalue *
   new_ctor (location *log,
@@ -306,7 +329,9 @@ private:
                    enum gcc_jit_global_kind kind,
                    type *type,
 		   const char *name,
-		   enum global_var_flags flags);
+		   enum global_var_flags flags,
+		   const std::vector<std::pair<gcc_jit_variable_attribute,
+					       std::string>> &attributes);
   lvalue *
   global_finalize_lvalue (tree inner);
 
@@ -500,7 +525,9 @@ public:
   lvalue *
   new_local (location *loc,
 	     type *type,
-	     const char *name);
+	     const char *name,
+	     const std::vector<std::pair<gcc_jit_variable_attribute,
+					 std::string>> &attributes);
 
   block*
   new_block (const char *name);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index 9b5b8005ebe..64301a329ec 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-builtins.h"
 #include "jit-recording.h"
 #include "jit-playback.h"
+#include <sstream>
 
 namespace gcc {
 namespace jit {
@@ -2068,7 +2069,7 @@ recording::memento::get_debug_string ()
 void
 recording::memento::write_to_dump (dump &d)
 {
-  d.write("  %s\n", get_debug_string ());
+  d.write ("  %s\n", get_debug_string ());
 }
 
 /* The implementation of class gcc::jit::recording::string.  */
@@ -4026,6 +4027,13 @@ void recording::lvalue::set_alignment (unsigned bytes)
   m_alignment = bytes;
 }
 
+void recording::lvalue::add_string_attribute (
+	gcc_jit_variable_attribute attribute,
+	const char* value)
+{
+  m_string_attributes.push_back (std::make_pair (attribute, std::string (value)));
+}
+
 /* The implementation of class gcc::jit::recording::param.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -4102,7 +4110,10 @@ recording::function::function (context *ctxt,
   m_builtin_id (builtin_id),
   m_locals (),
   m_blocks (),
-  m_fn_ptr_type (NULL)
+  m_fn_ptr_type (NULL),
+  m_attributes (),
+  m_string_attributes (),
+  m_int_array_attributes ()
 {
   for (int i = 0; i< num_params; i++)
     {
@@ -4161,7 +4172,10 @@ recording::function::replay_into (replayer *r)
 				     m_name->c_str (),
 				     &params,
 				     m_is_variadic,
-				     m_builtin_id));
+				     m_builtin_id,
+				     m_attributes,
+				     m_string_attributes,
+				     m_int_array_attributes));
 }
 
 /* Create a recording::local instance and add it to
@@ -4210,6 +4224,40 @@ recording::function::new_block (const char *name)
 void
 recording::function::write_to_dump (dump &d)
 {
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+      d.write ("__attribute(%s)__\n", attribute);
+  }
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
+  for (auto attr: m_int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+    if (attribute)
+    {
+      d.write ("__attribute(%s(", attribute);
+      for (size_t i = 0; i < values.size(); ++i)
+      {
+	if (i > 0)
+	  d.write (", %d", values[i]);
+	else
+	  d.write ("%d", values[i]);
+      }
+      d.write ("))__\n");
+    }
+  }
+
   switch (m_kind)
     {
     default: gcc_unreachable ();
@@ -4404,6 +4452,31 @@ recording::function::get_address (recording::location *loc)
   return result;
 }
 
+void
+recording::function::add_attribute (gcc_jit_fn_attribute attribute)
+{
+  m_attributes.push_back (attribute);
+}
+
+void
+recording::function::add_string_attribute (gcc_jit_fn_attribute attribute,
+					   const char* value)
+{
+  m_string_attributes.push_back (
+	std::make_pair (attribute, std::string (value)));
+}
+
+void
+recording::function::add_integer_array_attribute (
+	gcc_jit_fn_attribute attribute,
+	const int* value,
+	size_t length)
+{
+  m_int_array_attributes.push_back (std::make_pair (
+    attribute,
+    std::vector<int> (value, value + length)));
+}
+
 /* Implementation of recording::memento::make_debug_string for
    functions.  */
 
@@ -4425,6 +4498,39 @@ static const char * const names_of_function_kinds[] = {
 
 /* Implementation of recording::memento::write_reproducer for functions. */
 
+static const char * const fn_attribute_reproducer_strings[] =
+{
+  "GCC_JIT_FN_ATTRIBUTE_ALIAS",
+  "GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_NOINLINE",
+  "GCC_JIT_FN_ATTRIBUTE_TARGET",
+  "GCC_JIT_FN_ATTRIBUTE_USED",
+  "GCC_JIT_FN_ATTRIBUTE_VISIBILITY",
+  "GCC_JIT_FN_ATTRIBUTE_COLD",
+  "GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE",
+  "GCC_JIT_FN_ATTRIBUTE_PURE",
+  "GCC_JIT_FN_ATTRIBUTE_CONST",
+  "GCC_JIT_FN_ATTRIBUTE_WEAK",
+  "GCC_JIT_FN_ATTRIBUTE_NONNULL",
+};
+
+std::string
+get_vector_int_debug (std::vector<int> &values)
+{
+  std::stringstream s;
+
+  s << "{";
+  for(auto it = values.begin(); it != values.end(); ++it)
+    {
+      if (it != values.begin() )
+        s << ", ";
+      s << *it;
+    }
+  s << "}";
+  return s.str();
+}
+
 void
 recording::function::write_reproducer (reproducer &r)
 {
@@ -4467,6 +4573,25 @@ recording::function::write_reproducer (reproducer &r)
 	   m_params.length (),
 	   params_id,
 	   m_is_variadic);
+  for (auto attribute : m_attributes)
+    r.write("  gcc_jit_function_add_attribute (%s, %s);\n",
+	    id,
+	    fn_attribute_reproducer_strings[attribute]);
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_function_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+  for (auto attribute : m_int_array_attributes) {
+    r.write("  gcc_jit_function_add_integer_array_attribute (%s,\n"
+	    "                                                %s,\n"
+	    "                                                (int[])%s,\n"
+	    "                                                %lu);\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    get_vector_int_debug (std::get<1>(attribute)).c_str(),
+	    std::get<1>(attribute).size ());
+  }
 }
 
 
@@ -4879,12 +5004,14 @@ recording::global::replay_into (replayer *r)
 				 / m_type->dereference ()->get_size (),
 				 m_initializer,
 				 playback_string (m_name),
-				 m_flags)
+				 m_flags,
+				 m_string_attributes)
     : r->new_global (playback_location (r, m_loc),
 		     m_kind,
 		     m_type->playback_type (),
 		     playback_string (m_name),
-		     m_flags);
+		     m_flags,
+		     m_string_attributes);
 
   if (m_tls_model != GCC_JIT_TLS_MODEL_NONE)
     global->set_tls_model (recording::tls_models[m_tls_model]);
@@ -4943,6 +5070,15 @@ recording::global::write_to_dump (dump &d)
       break;
     }
 
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = variable_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
   d.write ("%s %s",
 	   m_type->get_debug_string (),
 	   get_debug_string ());
@@ -5013,6 +5149,10 @@ static const char * const tls_model_enum_strings[] = {
   "GCC_JIT_TLS_MODEL_LOCAL_EXEC",
 };
 
+static const char * const gcc_jit_variable_attribute_enum_strings[] = {
+  "GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY",
+};
+
 void
 recording::global::write_reproducer (reproducer &r)
 {
@@ -5042,6 +5182,13 @@ recording::global::write_reproducer (reproducer &r)
      id,
      m_link_section->c_str ());
 
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_lvalue_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+
+
   if (m_initializer)
     switch (m_type->dereference ()->get_size ())
       {
@@ -6622,7 +6769,8 @@ recording::local::replay_into (replayer *r)
   playback::lvalue *obj = m_func->playback_function ()
       ->new_local (playback_location (r, m_loc),
 		   m_type->playback_type (),
-		   playback_string (m_name));
+		   playback_string (m_name),
+		   m_string_attributes);
 
   if (m_reg_name != NULL)
     obj->set_register_name (m_reg_name->c_str ());
@@ -6644,9 +6792,9 @@ recording::local::write_to_dump (dump &d)
 {
   if (d.update_locations ())
     m_loc = d.make_location ();
-  d.write("  %s %s;\n",
-	  m_type->get_debug_string (),
-	  get_debug_string ());
+  d.write ("  %s %s;\n",
+	   m_type->get_debug_string (),
+	   get_debug_string ());
 }
 
 void
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 4a8082991fb..44694f610fa 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -23,6 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "jit-common.h"
 #include "jit-logging.h"
+#include "libgccjit.h"
+
+#include <string>
+#include <vector>
 
 class timer;
 
@@ -1216,7 +1220,8 @@ public:
     m_link_section (NULL),
     m_reg_name (NULL),
     m_tls_model (GCC_JIT_TLS_MODEL_NONE),
-    m_alignment (0)
+    m_alignment (0),
+    m_string_attributes ()
   {}
 
   playback::lvalue *
@@ -1236,6 +1241,9 @@ public:
   as_rvalue () { return this; }
 
   const char *access_as_rvalue (reproducer &r) override;
+
+  void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value);
+
   virtual const char *access_as_lvalue (reproducer &r);
   virtual bool is_global () const { return false; }
   void set_tls_model (enum gcc_jit_tls_model model);
@@ -1249,6 +1257,8 @@ protected:
   string *m_reg_name;
   enum gcc_jit_tls_model m_tls_model;
   unsigned m_alignment;
+  std::vector<std::pair<gcc_jit_variable_attribute,
+	      std::string>> m_string_attributes;
 };
 
 class param : public lvalue
@@ -1342,6 +1352,10 @@ public:
 
   rvalue *get_address (location *loc);
 
+  void add_attribute (gcc_jit_fn_attribute attribute);
+  void add_string_attribute (gcc_jit_fn_attribute attribute, const char* value);
+  void add_integer_array_attribute (gcc_jit_fn_attribute attribute, const int* value, size_t length);
+
 private:
   string * make_debug_string () final override;
   void write_reproducer (reproducer &r) final override;
@@ -1357,6 +1371,9 @@ private:
   auto_vec<local *> m_locals;
   auto_vec<block *> m_blocks;
   type *m_fn_ptr_type;
+  std::vector<gcc_jit_fn_attribute> m_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::string>> m_string_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::vector<int>>> m_int_array_attributes;
 };
 
 class block : public memento
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 0451b4df7f9..337d4ea3b95 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -3965,6 +3965,51 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
   return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
 }
 
+void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				gcc_jit_fn_attribute attribute)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+
+  func->add_attribute (attribute);
+}
+
+void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       gcc_jit_fn_attribute attribute,
+				       const char* value)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+
+  func->add_string_attribute (attribute, value);
+}
+
+/* This function adds an attribute with multiple integer values.  For example
+   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
+   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
+   and `2` in `values` (and set `length` to `2`). */
+void
+gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+					      gcc_jit_fn_attribute attribute,
+					      const int* values,
+					      size_t length)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
+
+  func->add_integer_array_attribute (attribute, values, length);
+}
+
+void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     gcc_jit_variable_attribute attribute,
+				     const char* value)
+{
+  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");
+
+  variable->add_string_attribute (attribute, value);
+}
+
 /* 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 749f6c24177..77aad2e8f20 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1999,6 +1999,55 @@ gcc_jit_vector_type_get_element_type (gcc_jit_vector_type *vector_type);
 extern gcc_jit_type *
 gcc_jit_type_unqualified (gcc_jit_type *type);
 
+#define LIBGCCJIT_HAVE_ATTRIBUTES
+
+/* Function attributes.  */
+enum gcc_jit_fn_attribute
+{
+  GCC_JIT_FN_ATTRIBUTE_ALIAS,
+  GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_NOINLINE,
+  GCC_JIT_FN_ATTRIBUTE_TARGET,
+  GCC_JIT_FN_ATTRIBUTE_USED,
+  GCC_JIT_FN_ATTRIBUTE_VISIBILITY,
+  GCC_JIT_FN_ATTRIBUTE_COLD,
+  GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE,
+  GCC_JIT_FN_ATTRIBUTE_PURE,
+  GCC_JIT_FN_ATTRIBUTE_CONST,
+  GCC_JIT_FN_ATTRIBUTE_WEAK,
+  GCC_JIT_FN_ATTRIBUTE_NONNULL,
+};
+
+/* Add an attribute to a function.  */
+extern void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				enum gcc_jit_fn_attribute attribute);
+
+extern void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       enum gcc_jit_fn_attribute attribute,
+				       const char* value);
+
+extern void
+gcc_jit_function_add_integer_array_attribute (
+  gcc_jit_function *func,
+  enum gcc_jit_fn_attribute attribute,
+  const int* value,
+  size_t length);
+
+/* Variable attributes.  */
+enum gcc_jit_variable_attribute
+{
+  GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY,
+};
+
+/* Add a string attribute to a variable.  */
+extern void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     enum gcc_jit_variable_attribute attribute,
+				     const char* value);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 8b90a0e2ff3..3bf8bb5d9b9 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -276,3 +276,11 @@ LIBGCCJIT_ABI_25 {
   global:
     gcc_jit_type_get_restrict;
 } LIBGCCJIT_ABI_24;
+
+LIBGCCJIT_ABI_26 {
+  global:
+    gcc_jit_function_add_attribute;
+    gcc_jit_function_add_string_attribute;
+    gcc_jit_lvalue_add_string_attribute;
+    gcc_jit_function_add_integer_array_attribute;
+} LIBGCCJIT_ABI_25;
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 8bf7e51c24f..657b454a003 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
 	pass "${asm_filename} output pattern test, ${dg-output-text}"
 	verbose "Passed test for output pattern ${dg-output-text}" 3
     }
+}
+
+# Assuming that a .s file has been written out named
+# OUTPUT_FILENAME, check that the argument doesn't match
+# the output file.
+proc jit-verify-assembler-output-not { args } {
+    verbose "jit-verify-assembler: $args"
+
+    set dg-output-text [lindex $args 0]
+    verbose "dg-output-text: ${dg-output-text}"
+
+    upvar 2 name name
+    verbose "name: $name"
+
+    upvar 2 prog prog
+    verbose "prog: $prog"
+    set asm_filename [jit-get-output-filename $prog]
+    verbose "  asm_filename: ${asm_filename}"
 
+    # Read the assembly file.
+    set f [open $asm_filename r]
+    set content [read $f]
+    close $f
+
+    # Verify that the assembly matches the regex.
+    if { [regexp ${dg-output-text} $content] } {
+	fail "${asm_filename} output pattern test, is ${content}, should match ${dg-output-text}"
+	verbose "Failed test for output pattern ${dg-output-text}" 3
+    } else {
+	pass "${asm_filename} output pattern test, ${dg-output-text}"
+	verbose "Passed test for output pattern ${dg-output-text}" 3
+    }
 }
+
+
 # Assuming that a .o file has been written out named
 # OUTPUT_FILENAME, invoke the driver to try to turn it into
 # an executable, and try to run the result.
diff --git a/gcc/testsuite/jit.dg/test-alias-attribute.c b/gcc/testsuite/jit.dg/test-alias-attribute.c
new file mode 100644
index 00000000000..eb29003dfc9
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-alias-attribute.c
@@ -0,0 +1,50 @@
+/* { 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-alias-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+void xxx () {}
+void f () __attribute__ ((alias ("xxx")));
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_EXPORTED,
+          void_type,
+          "xxx",
+          0, NULL,
+          0);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_IMPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_string_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_ALIAS, "xxx");
+
+  /* void xxx () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".set\\s+f,xxx" } } */
diff --git a/gcc/testsuite/jit.dg/test-always_inline-attribute.c b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
new file mode 100644
index 00000000000..5c3f386663f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
@@ -0,0 +1,153 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O0".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 0);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-always_inline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 gcc_jit_type *pint_type)
+{
+  /* The `a` function argument */
+  gcc_jit_param *a = gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          1, &a,
+          0);
+
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((always_inline))
+static inline int removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+static int not_removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+int foo () {
+  int x = 0;
+  x += removed(NULL);
+  x += not_removed(NULL);
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer (int_type);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, pint_type);
+  /* This one is to declare the function as "inline" */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_INLINE);
+  /* __attribute__ ((always_inline)) */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, pint_type);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(NULL); */
+  gcc_jit_rvalue *null = gcc_jit_context_null (ctxt, pint_type);
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 1, &null));
+  
+  /* x += not_removed(NULL); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 1, &null));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
new file mode 100644
index 00000000000..8dc7ec5a34b
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
@@ -0,0 +1,54 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the cold
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-cold-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+int
+__attribute__ ((cold))
+t()
+{
+  return -1;
+}
+
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "t",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(func_t, GCC_JIT_FN_ATTRIBUTE_COLD);
+  gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+  gcc_jit_rvalue *ret = gcc_jit_context_new_rvalue_from_int (ctxt,
+    int_type,
+    -1);
+
+  gcc_jit_block_end_with_return (block, NULL, ret);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "orl" } } */
diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
new file mode 100644
index 00000000000..c06742d163f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-const-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the const
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-const-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((const))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_CONST);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
new file mode 100644
index 00000000000..84933e60010
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
@@ -0,0 +1,114 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-noinline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((noinline))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo () {
+  int x = 0;
+  x += removed();
+  x += not_removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((no_inline)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_NOINLINE);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
new file mode 100644
index 00000000000..3306f890657
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
@@ -0,0 +1,94 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-nonnull.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__((nonnull(1)))
+int t(int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+	  GCC_JIT_FUNCTION_EXPORTED,
+	  int_type,
+	  "t",
+	  1, &a,
+	  0);
+  /* Adding `nonnull(1)` attribute. */
+  int indexes[1] = {1};
+  gcc_jit_function_add_integer_array_attribute (
+    func_t,
+    GCC_JIT_FN_ATTRIBUTE_NONNULL,
+    indexes,
+    1
+  );
+
+  /* if (!a) {
+    return -1;
+  } */
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func_t, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func_t, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func_t, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "if block" was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "testq" } } */
+/* { dg-final { jit-verify-assembler-output-not "-1" } } */
diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
new file mode 100644
index 00000000000..0c9ba1366e0
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the pure
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-pure-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((pure))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_PURE);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-restrict.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
similarity index 95%
rename from gcc/testsuite/jit.dg/test-restrict.c
rename to gcc/testsuite/jit.dg/test-restrict-attribute.c
index a6ac96324d2..7d7444b624f 100644
--- a/gcc/testsuite/jit.dg/test-restrict.c
+++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
@@ -73,5 +73,5 @@ void t(int *__restrict__ a, int *__restrict__ b, char *__restrict__ c) {
 }
 
 /* { dg-final { jit-verify-output-file-was-created "" } } */
-/* { dg-final { jit-verify-assembler-output "addl	%eax, (%rdi)
-	addl	%eax, (%rsi)" } } */
+/* { dg-final { jit-verify-assembler-output "addl\\s+%eax,\\s+(%rdi)
+\\s+addl\\s+%eax,\\s+(%rsi)" } } */
diff --git a/gcc/testsuite/jit.dg/test-used-attribute.c b/gcc/testsuite/jit.dg/test-used-attribute.c
new file mode 100644
index 00000000000..cb20952c687
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-used-attribute.c
@@ -0,0 +1,112 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-used-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__((used))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo() {
+  int x = 0;
+  x += not_removed();
+  x += removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((used)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_USED);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-variable-attribute.c b/gcc/testsuite/jit.dg/test-variable-attribute.c
new file mode 100644
index 00000000000..ea854ff4a9f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-variable-attribute.c
@@ -0,0 +1,46 @@
+/* { 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-variable-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+int PRIVATE __attribute__ ((visibility ("hidden"))) = 42;
+int PUBLIC = 12;
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `PRIVATE` variable. */
+  gcc_jit_lvalue *private = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PRIVATE");
+  gcc_jit_lvalue_add_string_attribute (private,
+    GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, "hidden");
+  gcc_jit_rvalue *rval = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 42);
+  gcc_jit_global_set_initializer_rvalue (private, rval);
+
+  /* Creating the `PUBLIC` variable. */
+  gcc_jit_lvalue *public = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PUBLIC");
+  gcc_jit_rvalue *rval2 = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 12);
+  gcc_jit_global_set_initializer_rvalue (public, rval2);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".hidden\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output-not ".hidden\\s+PUBLIC" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PUBLIC" } } */
diff --git a/gcc/testsuite/jit.dg/test-weak-attribute.c b/gcc/testsuite/jit.dg/test-weak-attribute.c
new file mode 100644
index 00000000000..546ade1c3c4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-weak-attribute.c
@@ -0,0 +1,41 @@
+/* { 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-weak-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__ ((weak))
+void f () {}
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_WEAK);
+
+  /* void f () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (f_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".weak\\s+f" } } */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-12-09 11:12           ` Guillaume Gomez
@ 2023-12-18 22:27             ` Guillaume Gomez
  2024-01-03 13:37               ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2023-12-18 22:27 UTC (permalink / raw)
  To: Antoni Boucher; +Cc: jit, gcc-patches, David Malcolm

Ping David. :)

Le sam. 9 déc. 2023 à 12:12, Guillaume Gomez
<guillaume1.gomez@gmail.com> a écrit :
>
> Added it.
>
> Le jeu. 7 déc. 2023 à 18:13, Antoni Boucher <bouanto@zoho.com> a écrit :
> >
> > It seems like you forgot to prefix the commit message with "libgccjit:
> > ".
> >
> > On Thu, 2023-11-30 at 10:55 +0100, Guillaume Gomez wrote:
> > > Ping David. :)
> > >
> > > Le jeu. 23 nov. 2023 à 22:59, Antoni Boucher <bouanto@zoho.com> a
> > > écrit :
> > > > David: I found back the comment you made. Here it is:
> > > >
> > > >    I see you have patches to add function and variable attributes;
> > > > I
> > > >    wonder if this would be cleaner internally if there was a
> > > >    recording::attribute class, rather than the std::pair currently
> > > > in
> > > >    use
> > > >    (some attributes have int arguments rather than string, others
> > > > have
> > > >    multiple args).
> > > >
> > > >    I also wondered if a "gcc_jit_attribute" type could be exposed
> > > > to
> > > >    the
> > > >    user, e.g.:
> > > >
> > > >      attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
> > > >      attr2 = gcc_jit_context_new_attribute_with_string (ctxt,
> > > > "alias",
> > > >    "__foo");
> > > >      gcc_jit_function_add_attribute (ctxt, attr1);
> > > >      gcc_jit_function_add_attribute (ctxt, attr2);
> > > >
> > > >    or somesuch?  But I think the API you currently have is OK.
> > > >
> > > > On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> > > > > Ping David. :)
> > > > >
> > > > > Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> > > > > écrit :
> > > > > >
> > > > > > David: another thing I remember you mentioned when you reviewed
> > > > > > an
> > > > > > earlier version of this patch is the usage of `std::pair`.
> > > > > > I can't find where you said that, but I remember you mentioned
> > > > > > that
> > > > > > we
> > > > > > should use a struct instead.
> > > > > > Can you please elaborate again?
> > > > > > Thanks.
> > > > > >
> > > > > > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > > > > > Hi,
> > > > > > >
> > > > > > > This patch adds the (incomplete) support for function and
> > > > > > > variable
> > > > > > > attributes. The added attributes are the ones we're using in
> > > > > > > rustc_codegen_gcc but all the groundwork is done to add more
> > > > > > > (and
> > > > > > > we
> > > > > > > will very likely add more as we didn't add all the ones we
> > > > > > > use in
> > > > > > > rustc_codegen_gcc yet).
> > > > > > >
> > > > > > > The only big question with this patch is about `inline`. We
> > > > > > > currently
> > > > > > > handle it as an attribute because it is more convenient for
> > > > > > > us
> > > > > > > but is
> > > > > > > it ok or should we create a separate function to mark a
> > > > > > > function
> > > > > > > as
> > > > > > > inlined?
> > > > > > >
> > > > > > > Thanks in advance for the review.
> > > > > >
> > > >
> >

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-12-18 22:27             ` Guillaume Gomez
@ 2024-01-03 13:37               ` Guillaume Gomez
  0 siblings, 0 replies; 16+ messages in thread
From: Guillaume Gomez @ 2024-01-03 13:37 UTC (permalink / raw)
  To: Antoni Boucher; +Cc: jit, gcc-patches, David Malcolm

Ping David. :)

Le lun. 18 déc. 2023 à 23:27, Guillaume Gomez
<guillaume1.gomez@gmail.com> a écrit :
>
> Ping David. :)
>
> Le sam. 9 déc. 2023 à 12:12, Guillaume Gomez
> <guillaume1.gomez@gmail.com> a écrit :
> >
> > Added it.
> >
> > Le jeu. 7 déc. 2023 à 18:13, Antoni Boucher <bouanto@zoho.com> a écrit :
> > >
> > > It seems like you forgot to prefix the commit message with "libgccjit:
> > > ".
> > >
> > > On Thu, 2023-11-30 at 10:55 +0100, Guillaume Gomez wrote:
> > > > Ping David. :)
> > > >
> > > > Le jeu. 23 nov. 2023 à 22:59, Antoni Boucher <bouanto@zoho.com> a
> > > > écrit :
> > > > > David: I found back the comment you made. Here it is:
> > > > >
> > > > >    I see you have patches to add function and variable attributes;
> > > > > I
> > > > >    wonder if this would be cleaner internally if there was a
> > > > >    recording::attribute class, rather than the std::pair currently
> > > > > in
> > > > >    use
> > > > >    (some attributes have int arguments rather than string, others
> > > > > have
> > > > >    multiple args).
> > > > >
> > > > >    I also wondered if a "gcc_jit_attribute" type could be exposed
> > > > > to
> > > > >    the
> > > > >    user, e.g.:
> > > > >
> > > > >      attr1 = gcc_jit_context_new_attribute (ctxt, "noreturn");
> > > > >      attr2 = gcc_jit_context_new_attribute_with_string (ctxt,
> > > > > "alias",
> > > > >    "__foo");
> > > > >      gcc_jit_function_add_attribute (ctxt, attr1);
> > > > >      gcc_jit_function_add_attribute (ctxt, attr2);
> > > > >
> > > > >    or somesuch?  But I think the API you currently have is OK.
> > > > >
> > > > > On Thu, 2023-11-23 at 22:52 +0100, Guillaume Gomez wrote:
> > > > > > Ping David. :)
> > > > > >
> > > > > > Le mer. 15 nov. 2023 à 17:56, Antoni Boucher <bouanto@zoho.com> a
> > > > > > écrit :
> > > > > > >
> > > > > > > David: another thing I remember you mentioned when you reviewed
> > > > > > > an
> > > > > > > earlier version of this patch is the usage of `std::pair`.
> > > > > > > I can't find where you said that, but I remember you mentioned
> > > > > > > that
> > > > > > > we
> > > > > > > should use a struct instead.
> > > > > > > Can you please elaborate again?
> > > > > > > Thanks.
> > > > > > >
> > > > > > > On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > > > > > > > Hi,
> > > > > > > >
> > > > > > > > This patch adds the (incomplete) support for function and
> > > > > > > > variable
> > > > > > > > attributes. The added attributes are the ones we're using in
> > > > > > > > rustc_codegen_gcc but all the groundwork is done to add more
> > > > > > > > (and
> > > > > > > > we
> > > > > > > > will very likely add more as we didn't add all the ones we
> > > > > > > > use in
> > > > > > > > rustc_codegen_gcc yet).
> > > > > > > >
> > > > > > > > The only big question with this patch is about `inline`. We
> > > > > > > > currently
> > > > > > > > handle it as an attribute because it is more convenient for
> > > > > > > > us
> > > > > > > > but is
> > > > > > > > it ok or should we create a separate function to mark a
> > > > > > > > function
> > > > > > > > as
> > > > > > > > inlined?
> > > > > > > >
> > > > > > > > Thanks in advance for the review.
> > > > > > >
> > > > >
> > >

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2023-11-15 16:53 [PATCH] Add support for function attributes and variable attributes Guillaume Gomez
  2023-11-15 16:56 ` Antoni Boucher
@ 2024-01-09 19:59 ` David Malcolm
  2024-01-11  0:00   ` Guillaume Gomez
  1 sibling, 1 reply; 16+ messages in thread
From: David Malcolm @ 2024-01-09 19:59 UTC (permalink / raw)
  To: Guillaume Gomez, jit, Antoni, gcc-patches

On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> Hi,
> 
> This patch adds the (incomplete) support for function and variable
> attributes. The added attributes are the ones we're using in
> rustc_codegen_gcc but all the groundwork is done to add more (and we
> will very likely add more as we didn't add all the ones we use in
> rustc_codegen_gcc yet).
> 
> The only big question with this patch is about `inline`. We currently
> handle it as an attribute because it is more convenient for us but is
> it ok or should we create a separate function to mark a function as
> inlined?
> 
> Thanks in advance for the review.

Thanks for the patch; sorry for the delay in reviewing.

At a high-level I think the API is OK as-is, but I have some nitpicks
with the implementation:

[...snip...]

> diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
> index d8c1d15d69d..6c72c99cbd9 100644
> --- a/gcc/jit/docs/topics/types.rst
> +++ b/gcc/jit/docs/topics/types.rst

[...snip...]

> +.. function::  void\
> +               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> +                                                    enum gcc_jit_fn_attribute attribute,
                                                                    ^^

This got out of sync with the declaration in the header file; it should
be
    enum gcc_jit_variable_attribute attribute

[...snip...]

> diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
> index a729086bafb..898b4d6e7f8 100644
> --- a/gcc/jit/dummy-frontend.cc
> +++ b/gcc/jit/dummy-frontend.cc

It's unfortunate that jit/dummy-frontend.cc has its own copy of the
material in c-common/c-attribs.cc.  I glanced through this code, and it
seems that there are already various differences between the two copies
in the existing code, and the patch adds more such differences.

Bother - but I think this part of the patch is inevitable (and OK)
given the existing state of attribute handling here.

[...snip...]

I took a brief look through the handler functions and with the above
caveat I didn't see anything obviously wrong.  I'm going to assume this
code is OK given that presumably you've been testing it within rustcc,
right?

[..snip...]

> diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
> index 0451b4df7f9..337d4ea3b95 100644
> --- a/gcc/jit/libgccjit.cc
> +++ b/gcc/jit/libgccjit.cc
> @@ -3965,6 +3965,51 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
>    return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
>  }
>  
> +void
> +gcc_jit_function_add_attribute (gcc_jit_function *func,
> +				gcc_jit_fn_attribute attribute)
> +{
> +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
> +
> +  func->add_attribute (attribute);

Ideally should validate parameter "attribute" here with a
RETURN_IF_FAIL.

> +}
> +
> +void
> +gcc_jit_function_add_string_attribute (gcc_jit_function *func,
> +				       gcc_jit_fn_attribute attribute,
> +				       const char* value)
> +{
> +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");

Likewise, ideally should validate parameter "attribute" here with a
RETURN_IF_FAIL.

Can "value" be NULL?  If not, then we should add a RETURN_IF_FAIL for
it here at the API boundary.

> +
> +  func->add_string_attribute (attribute, value);
> +}
> +
> +/* This function adds an attribute with multiple integer values.  For example
> +   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
> +   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
> +   and `2` in `values` (and set `length` to `2`). */
> +void
> +gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
> +					      gcc_jit_fn_attribute attribute,
> +					      const int* values,
> +					      size_t length)
> +{
> +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");

As before, ideally should validate parameter "attribute" here with a
RETURN_IF_FAIL.

> +  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
> +
> +  func->add_integer_array_attribute (attribute, values, length);
> +}
> +
> +void
> +gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> +				     gcc_jit_variable_attribute attribute,
> +				     const char* value)
> +{
> +  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");

As before, we should validate parameters "attribute" and "value" here
with RETURN_IF_FAILs.

We should also validate here that "variable" is indeed a variable, not
some arbitrary lvalue e.g. the address of the element of an array (or
whatever).


> +
> +  variable->add_string_attribute (attribute, value);
> +}
> +

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
> index 8bf7e51c24f..657b454a003 100644
> --- a/gcc/testsuite/jit.dg/jit.exp
> +++ b/gcc/testsuite/jit.dg/jit.exp
> @@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
>  	pass "${asm_filename} output pattern test, ${dg-output-text}"
>  	verbose "Passed test for output pattern ${dg-output-text}" 3
>      }
> +}
> +
> +# Assuming that a .s file has been written out named
> +# OUTPUT_FILENAME, check that the argument doesn't match
> +# the output file.
> +proc jit-verify-assembler-output-not { args } {
> +    verbose "jit-verify-assembler: $args"
> +
> +    set dg-output-text [lindex $args 0]
> +    verbose "dg-output-text: ${dg-output-text}"
> +
> +    upvar 2 name name
> +    verbose "name: $name"
> +
> +    upvar 2 prog prog
> +    verbose "prog: $prog"
> +    set asm_filename [jit-get-output-filename $prog]
> +    verbose "  asm_filename: ${asm_filename}"
>  
> +    # Read the assembly file.
> +    set f [open $asm_filename r]
> +    set content [read $f]
> +    close $f
> +
> +    # Verify that the assembly matches the regex.
> +    if { [regexp ${dg-output-text} $content] } {
> +	fail "${asm_filename} output pattern test, is ${content}, should match ${dg-output-text}"

The wording of the "fail" message seems wrong; presumably this should
read "should not match", rather than "should match".

> +	verbose "Failed test for output pattern ${dg-output-text}" 3
> +    } else {
> +	pass "${asm_filename} output pattern test, ${dg-output-text}"
> +	verbose "Passed test for output pattern ${dg-output-text}" 3
> +    }
>  }
> +
> +
>  # Assuming that a .o file has been written out named
>  # OUTPUT_FILENAME, invoke the driver to try to turn it into
>  # an executable, and try to run the result.

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
> new file mode 100644
> index 00000000000..8dc7ec5a34b
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> @@ -0,0 +1,54 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the cold
> +   attribute affects the optimizations. */

The above comment doesn't make sense to me; harness.h effectively sets
-O3; and -O2 is wanted by this test, right?

I think the comment can be omitted given that the intent below is
clear.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
> new file mode 100644
> index 00000000000..c06742d163f
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> @@ -0,0 +1,134 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O3 to see that the const
> +   attribute affects the optimizations. */

Again, this comment doesn't make sense to me, but I think it can be
removed.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O3".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> +}
> +

[...snip...]

> +
> +  /* if (x >>= 1) */
> +  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
> +     `if (x = x >> 1)` */

I think it does (in theory, at least), via:

  gcc_jit_block_add_assignment_op

with

  GCC_JIT_BINARY_OP_RSHIFT

But I haven't tried it, and there's no need to update the test to make
use of it.

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> new file mode 100644
> index 00000000000..84933e60010
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> @@ -0,0 +1,114 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
> +   attribute affects the optimizations. */

Again, please lose this comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> new file mode 100644
> index 00000000000..3306f890657
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> @@ -0,0 +1,94 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
> +   attribute affects the optimizations. */

Likewise.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}
> +

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
> new file mode 100644
> index 00000000000..0c9ba1366e0
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> @@ -0,0 +1,134 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O3 to see that the pure
> +   attribute affects the optimizations. */

Likewise.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O3".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> +}
> +

[...snip...]

Please update jit.dg/all-non-failing-tests.h for the new tests; it's
meant to list all of the (non failing) tests alphabetically.

I *think* all of the new tests aren't suitable to be run as part of a
shared context (e.g. due to touching the optimization level or
examining generated asm), so they should be listed in that header with
comments explaining why.

Thanks again for the patch.
Dave


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-09 19:59 ` David Malcolm
@ 2024-01-11  0:00   ` Guillaume Gomez
  2024-01-11 18:46     ` David Malcolm
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2024-01-11  0:00 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit, Antoni, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 14194 bytes --]

Hi David.

Thanks for the review!

> > +.. function::  void\
> > +               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> > +                                                    enum gcc_jit_fn_attribute attribute,
>                                                                    ^^
>
> This got out of sync with the declaration in the header file; it should
> be enum gcc_jit_variable_attribute attribute

Indeed, good catch!

> I took a brief look through the handler functions and with the above
> caveat I didn't see anything obviously wrong.  I'm going to assume this
> code is OK given that presumably you've been testing it within rustc,
> right?

Both in rustc and in the JIT tests we added.

[..snip...]

I added all the missing `RETURN_IF_FAIL` you mentioned. None of the
arguments should be `NULL` so it was a mistake not to check it.

[..snip...]

I removed the tests comments as you mentioned.

> Please update jit.dg/all-non-failing-tests.h for the new tests; it's
> meant to list all of the (non failing) tests alphabetically.

It's not always correctly sorted. Might be worth sending a patch after this
one gets merged to fix that.

> I *think* all of the new tests aren't suitable to be run as part of a
> shared context (e.g. due to touching the optimization level or
> examining generated asm), so they should be listed in that header with
> comments explaining why.

I added them with a comment on top of each of them.

I joined the new patch version.

Thanks again for the review!


Le mar. 9 janv. 2024 à 20:59, David Malcolm <dmalcolm@redhat.com> a écrit :
>
> On Wed, 2023-11-15 at 17:53 +0100, Guillaume Gomez wrote:
> > Hi,
> >
> > This patch adds the (incomplete) support for function and variable
> > attributes. The added attributes are the ones we're using in
> > rustc_codegen_gcc but all the groundwork is done to add more (and we
> > will very likely add more as we didn't add all the ones we use in
> > rustc_codegen_gcc yet).
> >
> > The only big question with this patch is about `inline`. We currently
> > handle it as an attribute because it is more convenient for us but is
> > it ok or should we create a separate function to mark a function as
> > inlined?
> >
> > Thanks in advance for the review.
>
> Thanks for the patch; sorry for the delay in reviewing.
>
> At a high-level I think the API is OK as-is, but I have some nitpicks
> with the implementation:
>
> [...snip...]
>
> > diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
> > index d8c1d15d69d..6c72c99cbd9 100644
> > --- a/gcc/jit/docs/topics/types.rst
> > +++ b/gcc/jit/docs/topics/types.rst
>
> [...snip...]
>
> > +.. function::  void\
> > +               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> > +                                                    enum gcc_jit_fn_attribute attribute,
>                                                                     ^^
>
> This got out of sync with the declaration in the header file; it should
> be
>     enum gcc_jit_variable_attribute attribute
>
> [...snip...]
>
> > diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
> > index a729086bafb..898b4d6e7f8 100644
> > --- a/gcc/jit/dummy-frontend.cc
> > +++ b/gcc/jit/dummy-frontend.cc
>
> It's unfortunate that jit/dummy-frontend.cc has its own copy of the
> material in c-common/c-attribs.cc.  I glanced through this code, and it
> seems that there are already various differences between the two copies
> in the existing code, and the patch adds more such differences.
>
> Bother - but I think this part of the patch is inevitable (and OK)
> given the existing state of attribute handling here.
>
> [...snip...]
>
> I took a brief look through the handler functions and with the above
> caveat I didn't see anything obviously wrong.  I'm going to assume this
> code is OK given that presumably you've been testing it within rustcc,
> right?
>
> [..snip...]
>
> > diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
> > index 0451b4df7f9..337d4ea3b95 100644
> > --- a/gcc/jit/libgccjit.cc
> > +++ b/gcc/jit/libgccjit.cc
> > @@ -3965,6 +3965,51 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
> >    return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
> >  }
> >
> > +void
> > +gcc_jit_function_add_attribute (gcc_jit_function *func,
> > +                             gcc_jit_fn_attribute attribute)
> > +{
> > +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
> > +
> > +  func->add_attribute (attribute);
>
> Ideally should validate parameter "attribute" here with a
> RETURN_IF_FAIL.
>
> > +}
> > +
> > +void
> > +gcc_jit_function_add_string_attribute (gcc_jit_function *func,
> > +                                    gcc_jit_fn_attribute attribute,
> > +                                    const char* value)
> > +{
> > +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
>
> Likewise, ideally should validate parameter "attribute" here with a
> RETURN_IF_FAIL.
>
> Can "value" be NULL?  If not, then we should add a RETURN_IF_FAIL for
> it here at the API boundary.
>
> > +
> > +  func->add_string_attribute (attribute, value);
> > +}
> > +
> > +/* This function adds an attribute with multiple integer values.  For example
> > +   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
> > +   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
> > +   and `2` in `values` (and set `length` to `2`). */
> > +void
> > +gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
> > +                                           gcc_jit_fn_attribute attribute,
> > +                                           const int* values,
> > +                                           size_t length)
> > +{
> > +  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
>
> As before, ideally should validate parameter "attribute" here with a
> RETURN_IF_FAIL.
>
> > +  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
> > +
> > +  func->add_integer_array_attribute (attribute, values, length);
> > +}
> > +
> > +void
> > +gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> > +                                  gcc_jit_variable_attribute attribute,
> > +                                  const char* value)
> > +{
> > +  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");
>
> As before, we should validate parameters "attribute" and "value" here
> with RETURN_IF_FAILs.
>
> We should also validate here that "variable" is indeed a variable, not
> some arbitrary lvalue e.g. the address of the element of an array (or
> whatever).
>
>
> > +
> > +  variable->add_string_attribute (attribute, value);
> > +}
> > +
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
> > index 8bf7e51c24f..657b454a003 100644
> > --- a/gcc/testsuite/jit.dg/jit.exp
> > +++ b/gcc/testsuite/jit.dg/jit.exp
> > @@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
> >       pass "${asm_filename} output pattern test, ${dg-output-text}"
> >       verbose "Passed test for output pattern ${dg-output-text}" 3
> >      }
> > +}
> > +
> > +# Assuming that a .s file has been written out named
> > +# OUTPUT_FILENAME, check that the argument doesn't match
> > +# the output file.
> > +proc jit-verify-assembler-output-not { args } {
> > +    verbose "jit-verify-assembler: $args"
> > +
> > +    set dg-output-text [lindex $args 0]
> > +    verbose "dg-output-text: ${dg-output-text}"
> > +
> > +    upvar 2 name name
> > +    verbose "name: $name"
> > +
> > +    upvar 2 prog prog
> > +    verbose "prog: $prog"
> > +    set asm_filename [jit-get-output-filename $prog]
> > +    verbose "  asm_filename: ${asm_filename}"
> >
> > +    # Read the assembly file.
> > +    set f [open $asm_filename r]
> > +    set content [read $f]
> > +    close $f
> > +
> > +    # Verify that the assembly matches the regex.
> > +    if { [regexp ${dg-output-text} $content] } {
> > +     fail "${asm_filename} output pattern test, is ${content}, should match ${dg-output-text}"
>
> The wording of the "fail" message seems wrong; presumably this should
> read "should not match", rather than "should match".
>
> > +     verbose "Failed test for output pattern ${dg-output-text}" 3
> > +    } else {
> > +     pass "${asm_filename} output pattern test, ${dg-output-text}"
> > +     verbose "Passed test for output pattern ${dg-output-text}" 3
> > +    }
> >  }
> > +
> > +
> >  # Assuming that a .o file has been written out named
> >  # OUTPUT_FILENAME, invoke the driver to try to turn it into
> >  # an executable, and try to run the result.
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > new file mode 100644
> > index 00000000000..8dc7ec5a34b
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > @@ -0,0 +1,54 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the cold
> > +   attribute affects the optimizations. */
>
> The above comment doesn't make sense to me; harness.h effectively sets
> -O3; and -O2 is wanted by this test, right?
>
> I think the comment can be omitted given that the intent below is
> clear.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
> > new file mode 100644
> > index 00000000000..c06742d163f
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> > @@ -0,0 +1,134 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O3 to see that the const
> > +   attribute affects the optimizations. */
>
> Again, this comment doesn't make sense to me, but I think it can be
> removed.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O3".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > +}
> > +
>
> [...snip...]
>
> > +
> > +  /* if (x >>= 1) */
> > +  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
> > +     `if (x = x >> 1)` */
>
> I think it does (in theory, at least), via:
>
>   gcc_jit_block_add_assignment_op
>
> with
>
>   GCC_JIT_BINARY_OP_RSHIFT
>
> But I haven't tried it, and there's no need to update the test to make
> use of it.
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > new file mode 100644
> > index 00000000000..84933e60010
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > @@ -0,0 +1,114 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
> > +   attribute affects the optimizations. */
>
> Again, please lose this comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > new file mode 100644
> > index 00000000000..3306f890657
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > @@ -0,0 +1,94 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
> > +   attribute affects the optimizations. */
>
> Likewise.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
> > +
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > new file mode 100644
> > index 00000000000..0c9ba1366e0
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > @@ -0,0 +1,134 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O3 to see that the pure
> > +   attribute affects the optimizations. */
>
> Likewise.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O3".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > +}
> > +
>
> [...snip...]
>
> Please update jit.dg/all-non-failing-tests.h for the new tests; it's
> meant to list all of the (non failing) tests alphabetically.
>
> I *think* all of the new tests aren't suitable to be run as part of a
> shared context (e.g. due to touching the optimization level or
> examining generated asm), so they should be listed in that header with
> comments explaining why.
>
> Thanks again for the patch.
> Dave
>

[-- Attachment #2: 0001-PATCH-libgccjit-Add-support-for-function-attributes-.patch --]
[-- Type: text/x-patch, Size: 95310 bytes --]

From dc27f66396ba9d05bc8007d8509620e5ae14d834 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Wed, 10 Jan 2024 15:23:37 +0100
Subject: [PATCH] [PATCH] libgccjit: Add support for function attributes and
 variable  attributes.

gcc/jit/ChangeLog:

	* dummy-frontend.cc (handle_alias_attribute): New function.
	(handle_always_inline_attribute): New function.
	(handle_cold_attribute): New function.
	(handle_fnspec_attribute): New function.
	(handle_format_arg_attribute): New function.
	(handle_format_attribute): New function.
	(handle_noinline_attribute): New function.
	(handle_target_attribute): New function.
	(handle_used_attribute): New function.
	(handle_visibility_attribute): New function.
	(handle_weak_attribute): New function.
	(handle_alias_ifunc_attribute): New function.
	* jit-playback.cc (fn_attribute_to_string): New function.
	(variable_attribute_to_string): New function.
	(global_new_decl): Add attributes support.
	(set_variable_attribute): New function.
	(new_global): Add attributes support.
	(new_global_initialized): Add attributes support.
	(new_local): Add attributes support.
	* jit-playback.h (fn_attribute_to_string): New function.
	(set_variable_attribute): New function.
	* jit-recording.cc (recording::lvalue::add_attribute): New function.
	(recording::function::function): New function.
	(recording::function::write_to_dump): Add attributes support.
	(recording::function::add_attribute): New function.
	(recording::function::add_string_attribute): New function.
	(recording::function::add_integer_array_attribute): New function.
	(recording::global::replay_into): Add attributes support.
	(recording::local::replay_into): Add attributes support.
	* libgccjit.cc (gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(gcc_jit_lvalue_add_attribute): New function.
	* libgccjit.h (enum gcc_jit_fn_attribute): New enum.
	(gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(enum gcc_jit_variable_attribute): New function.
	(gcc_jit_lvalue_add_string_attribute): New function.
	* libgccjit.map: Declare new functions.

gcc/testsuite/ChangeLog:

	* jit.dg/all-non-failing-tests.h: Add new attributes tests.
	* jit.dg/jit.exp: Add `jit-verify-assembler-output-not` test command.
	* jit.dg/test-restrict.c: New test.
	* jit.dg/test-restrict-attribute.c: New test.
	* jit.dg/test-alias-attribute.c: New test.
	* jit.dg/test-always_inline-attribute.c: New test.
	* jit.dg/test-cold-attribute.c: New test.
	* jit.dg/test-const-attribute.c: New test.
	* jit.dg/test-noinline-attribute.c: New test.
	* jit.dg/test-nonnull-attribute.c: New test.
	* jit.dg/test-pure-attribute.c: New test.
	* jit.dg/test-used-attribute.c: New test.
	* jit.dg/test-variable-attribute.c: New test.
	* jit.dg/test-weak-attribute.c: New test.

gcc/jit/ChangeLog:
	* docs/topics/compatibility.rst: Add documentation for LIBGCCJIT_ABI_26.
	* docs/topics/types.rst: Add documentation for new functions.

Co-authored-by: Antoni Boucher <bouanto@zoho.com>
Signed-off-by: Guillaume Gomez <guillaume1.gomez@gmail.com>
---
 gcc/jit/docs/topics/compatibility.rst         |  12 +
 gcc/jit/docs/topics/types.rst                 |  77 +++
 gcc/jit/dummy-frontend.cc                     | 512 ++++++++++++++++--
 gcc/jit/jit-playback.cc                       | 169 +++++-
 gcc/jit/jit-playback.h                        |  37 +-
 gcc/jit/jit-recording.cc                      | 166 +++++-
 gcc/jit/jit-recording.h                       |  22 +-
 gcc/jit/libgccjit.cc                          |  67 +++
 gcc/jit/libgccjit.h                           |  55 ++
 gcc/jit/libgccjit.map                         |   8 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h  |  32 +-
 gcc/testsuite/jit.dg/jit.exp                  |  33 ++
 gcc/testsuite/jit.dg/test-alias-attribute.c   |  50 ++
 .../jit.dg/test-always_inline-attribute.c     | 153 ++++++
 gcc/testsuite/jit.dg/test-cold-attribute.c    |  54 ++
 gcc/testsuite/jit.dg/test-const-attribute.c   | 134 +++++
 .../jit.dg/test-noinline-attribute.c          | 121 +++++
 gcc/testsuite/jit.dg/test-nonnull-attribute.c |  94 ++++
 gcc/testsuite/jit.dg/test-pure-attribute.c    | 134 +++++
 .../jit.dg/test-restrict-attribute.c          |  77 +++
 gcc/testsuite/jit.dg/test-used-attribute.c    | 112 ++++
 .../jit.dg/test-variable-attribute.c          |  46 ++
 gcc/testsuite/jit.dg/test-weak-attribute.c    |  41 ++
 23 files changed, 2132 insertions(+), 74 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-alias-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-always_inline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-cold-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-const-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-noinline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-nonnull-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-pure-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-restrict-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-used-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-variable-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-weak-attribute.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 704de1b51ed..cbf5b414d8c 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -378,3 +378,15 @@ alignment of a variable:
 --------------------
 ``LIBGCCJIT_ABI_25`` covers the addition of
 :func:`gcc_jit_type_get_restrict`
+
+.. _LIBGCCJIT_ABI_26:
+
+``LIBGCCJIT_ABI_26``
+--------------------
+``LIBGCCJIT_ABI_26`` covers the addition of functions to set attributes
+on functions and variables:
+
+  * :func:`gcc_jit_function_add_attribute`
+  * :func:`gcc_jit_function_add_string_attribute`
+  * :func:`gcc_jit_function_add_integer_array_attribute`
+  * :func:`gcc_jit_lvalue_add_string_attribute`
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index bb51f037b7e..b1aedc03787 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -553,3 +553,80 @@ Reflection API
    .. code-block:: c
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
+
+.. function::  void\
+               gcc_jit_function_add_attribute (gcc_jit_function *func,
+                                               enum gcc_jit_fn_attribute attribute)
+
+     Add an attribute ``attribute`` to a function ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__((always_inline))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+                                                      enum gcc_jit_fn_attribute attribute,
+                                                      const char *value)
+
+     Add a string attribute ``attribute`` with value ``value`` to a function
+     ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((alias ("xxx")))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+                                                             enum gcc_jit_fn_attribute attribute,
+                                                             const int *value,
+                                                             size_t length)
+
+     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
+     function ``func``. The integer values must be the same as you would write
+     them in a C code.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((nonnull (1, 2)))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+                                                    enum gcc_jit_variable_attribute attribute,
+                                                    const char *value)
+
+     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 211f1be98fa..dbeeacd17a8 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -29,30 +29,42 @@ along with GCC; see the file COPYING3.  If not see
 #include "options.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "cgraph.h"
+#include "target.h"
 
 #include <mpfr.h>
 
 /* Attribute handling.  */
 
-static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
-static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
+static tree handle_always_inline_attribute (tree *, tree, tree, int,
+					    bool *);
+static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_const_attribute (tree *, tree, tree, int, bool *);
+static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_attribute (tree *, tree, tree, int, bool *);
+static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
-static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 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_sentinel_attribute (tree *, tree, tree, int, bool *);
-static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
-static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
-static tree ignore_attribute (tree *, tree, tree, int, bool *);
+static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
+static tree handle_target_attribute (tree *, tree, tree, int, bool *);
+static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
+static tree handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree handle_visibility_attribute (tree *, tree, tree, int,
+					 bool *);
+static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 
-static tree handle_format_attribute (tree *, tree, tree, int, bool *);
-static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
-static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -61,7 +73,6 @@ static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
 /* Define attributes that are mutually exclusive with one another.  */
 static const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
 {
-  ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("alloc_align", true, true, true),
   ATTR_EXCL ("alloc_size", true, true, true),
   ATTR_EXCL ("const", true, true, true),
@@ -78,57 +89,117 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
   ATTR_EXCL (NULL, false, false, false),
 };
 
+/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc.  */
+static const struct attribute_spec::exclusions attr_alloc_exclusions[] =
+{
+  ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("noreturn", true, true, true),
+  ATTR_EXCL ("pure", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 {
   ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("alloc_align", true, true, true),
+  ATTR_EXCL ("alloc_size", true, true, true),
+  ATTR_EXCL ("malloc", true, true, true),
   ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("pure", true, true, true),
   ATTR_EXCL (NULL, false, false, false)
 };
 
+static const struct attribute_spec::exclusions attr_always_inline_exclusions[] =
+{
+  ATTR_EXCL ("noinline", true, true, true),
+  ATTR_EXCL ("target_clones", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+{
+  ATTR_EXCL ("cold", true, true, true),
+  ATTR_EXCL ("hot", true, true, true),
+  ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_noinline_exclusions[] =
+{
+  ATTR_EXCL ("always_inline", true, true, true),
+  ATTR_EXCL ("gnu_inline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_target_exclusions[] =
+{
+  ATTR_EXCL ("target_clones", TARGET_HAS_FMV_TARGET_ATTRIBUTE,
+             TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 /* Table of machine-independent attributes supported in libgccjit.  */
 static const attribute_spec jit_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "noreturn",               0, 0, true,  false, false, false,
-			      handle_noreturn_attribute,
-			      attr_noreturn_exclusions },
-  { "leaf",		      0, 0, true,  false, false, false,
-			      handle_leaf_attribute, NULL },
+  { "alias",		      1, 1, true,  false, false, false,
+			      handle_alias_attribute, NULL },
+  { "always_inline",	      0, 0, true,  false, false, false,
+			      handle_always_inline_attribute,
+			      attr_always_inline_exclusions },
+  { "cold",		      0, 0, true,  false, false, false,
+			      handle_cold_attribute,
+			      attr_cold_hot_exclusions },
   /* The same comments as for noreturn attributes apply to const ones.  */
-  { "const",                  0, 0, true,  false, false, false,
+  { "const",		      0, 0, true,  false, false, false,
 			      handle_const_attribute,
 			      attr_const_pure_exclusions },
-  { "malloc",                 0, 0, true,  false, false, false,
-			      handle_malloc_attribute, NULL },
-  { "pure",                   0, 0, true,  false, false, false,
-			      handle_pure_attribute,
-			      attr_const_pure_exclusions },
-  { "no vops",                0, 0, true,  false, false, false,
+  { "fn spec",		      1, 1, false, true, true, false,
+			      handle_fnspec_attribute, NULL },
+
+  { "leaf",		      0, 0, true,  false, false, false,
+			      handle_leaf_attribute, NULL },
+  { "malloc",		      0, 0, true,  false, false, false,
+			      handle_malloc_attribute, attr_alloc_exclusions },
+  { "noreturn",		      0, 0, true,  false, false, false,
+			      handle_noreturn_attribute,
+			      attr_noreturn_exclusions },
+  { "no vops",		      0, 0, true,  false, false, false,
 			      handle_novops_attribute, NULL },
-  { "nonnull",                0, -1, false, true, true, false,
+  { "noinline",		      0, 0, true,  false, false, false,
+			      handle_noinline_attribute,
+			      attr_noinline_exclusions },
+  { "nonnull",		      0, -1, false, true, true, false,
 			      handle_nonnull_attribute, NULL },
-  { "nothrow",                0, 0, true,  false, false, false,
+  { "nothrow",		      0, 0, true,  false, false, false,
 			      handle_nothrow_attribute, NULL },
   { "patchable_function_entry", 1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
-  { "returns_twice",          0, 0, true,  false, false, false,
+  { "pure",		      0, 0, true,  false, false, false,
+			      handle_pure_attribute,
+			      attr_const_pure_exclusions },
+  { "returns_twice",	      0, 0, true,  false, false, false,
 			      handle_returns_twice_attribute,
 			      attr_returns_twice_exclusions },
-  { "sentinel",               0, 1, false, true, true, false,
+  { "sentinel",		      0, 1, false, true, true, false,
 			      handle_sentinel_attribute, NULL },
-  { "type generic",           0, 0, false, true, true, false,
+  { "target",		      1, -1, true, false, false, false,
+			      handle_target_attribute, attr_target_exclusions },
+  { "type generic",	      0, 0, false, true, true, false,
 			      handle_type_generic_attribute, NULL },
-  { "fn spec",	 	      1, 1, false, true, true, false,
-			      handle_fnspec_attribute, NULL },
   { "transaction_pure",	      0, 0, false, true, true, false,
 			      handle_transaction_pure_attribute, NULL },
+  { "used",         0, 0, true,  false, false, false,
+            handle_used_attribute, NULL },
+  { "visibility",       1, 1, false, false, false, false,
+            handle_visibility_attribute, NULL },
+  { "weak",         0, 0, true,  false, false, false,
+            handle_weak_attribute, NULL },
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
   { "*tm regparm",            0, 0, false, true, true, false,
-			      ignore_attribute, NULL }
+			      ignore_attribute, NULL },
 };
 
 static const scoped_attribute_specs jit_gnu_attribute_table =
@@ -143,7 +214,7 @@ static const attribute_spec jit_format_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "format",                 3, 3, false, true,  true, false,
+  { "format",		      3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
   { "format_arg",             1, 1, false, true,  true, false,
 			      handle_format_arg_attribute, NULL }
@@ -212,14 +283,9 @@ handle_leaf_attribute (tree *node, tree name,
    struct attribute_spec.handler.  */
 
 static tree
-handle_const_attribute (tree *node, tree ARG_UNUSED (name),
-			tree ARG_UNUSED (args), int ARG_UNUSED (flags),
-			bool * ARG_UNUSED (no_add_attrs))
+handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  if (TREE_CODE (*node) != FUNCTION_DECL
-      || !fndecl_built_in_p (*node))
-    inform (UNKNOWN_LOCATION, "%s:%s: %E: %E", __FILE__, __func__, *node, name);
-
   tree type = TREE_TYPE (*node);
 
   /* See FIXME comment on noreturn in c_common_attribute_table.  */
@@ -228,11 +294,16 @@ handle_const_attribute (tree *node, tree ARG_UNUSED (name),
   else if (TREE_CODE (type) == POINTER_TYPE
 	   && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
     TREE_TYPE (*node)
-      = build_pointer_type
-	(build_type_variant (TREE_TYPE (type), 1,
-			     TREE_THIS_VOLATILE (TREE_TYPE (type))));
+      = (build_qualified_type
+	 (build_pointer_type
+	  (build_type_variant (TREE_TYPE (type), 1,
+			       TREE_THIS_VOLATILE (TREE_TYPE (type)))),
+	  TYPE_QUALS (type)));
   else
-    gcc_unreachable ();
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
 
   return NULL_TREE;
 }
@@ -494,6 +565,357 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle an "visibility" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_visibility_attribute (tree *node, tree name, tree args,
+			     int ARG_UNUSED (flags),
+			     bool *ARG_UNUSED (no_add_attrs))
+{
+  tree decl = *node;
+  tree id = TREE_VALUE (args);
+  enum symbol_visibility vis;
+
+  if (TYPE_P (*node))
+    {
+      if (TREE_CODE (*node) == ENUMERAL_TYPE)
+	/* OK.  */;
+      else if (!RECORD_OR_UNION_TYPE_P (*node))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
+		   name);
+	  return NULL_TREE;
+	}
+      else if (TYPE_FIELDS (*node))
+	{
+	  error ("%qE attribute ignored because %qT is already defined",
+		 name, *node);
+	  return NULL_TREE;
+	}
+    }
+  else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("visibility argument not a string");
+      return NULL_TREE;
+    }
+
+  /*  If this is a type, set the visibility on the type decl.  */
+  if (TYPE_P (decl))
+    {
+      decl = TYPE_NAME (decl);
+      if (!decl)
+	return NULL_TREE;
+      if (TREE_CODE (decl) == IDENTIFIER_NODE)
+	{
+	   warning (OPT_Wattributes, "%qE attribute ignored on types",
+		    name);
+	   return NULL_TREE;
+	}
+    }
+
+  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
+    vis = VISIBILITY_DEFAULT;
+  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
+    vis = VISIBILITY_INTERNAL;
+  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
+    vis = VISIBILITY_HIDDEN;
+  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
+    vis = VISIBILITY_PROTECTED;
+  else
+    {
+      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
+	     name, "default", "hidden", "protected", "internal");
+      vis = VISIBILITY_DEFAULT;
+    }
+
+  if (DECL_VISIBILITY_SPECIFIED (decl)
+      && vis != DECL_VISIBILITY (decl))
+    {
+      tree attributes = (TYPE_P (*node)
+			 ? TYPE_ATTRIBUTES (*node)
+			 : DECL_ATTRIBUTES (decl));
+      if (lookup_attribute ("visibility", attributes))
+	error ("%qD redeclared with different visibility", decl);
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllimport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllimport");
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllexport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllexport");
+    }
+
+  DECL_VISIBILITY (decl) = vis;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+
+  /* Go ahead and attach the attribute to the node as well.  This is needed
+     so we can determine whether we have VISIBILITY_DEFAULT because the
+     visibility was not specified, or because it was explicitly overridden
+     from the containing scope.  */
+
+  return NULL_TREE;
+}
+
+/* Handle a "always_inline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_always_inline_attribute (tree *node, tree name,
+				tree ARG_UNUSED (args),
+				int ARG_UNUSED (flags),
+				bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      /* Set the attribute and mark it for disregarding inline
+	 limits.  */
+      DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "cold" and attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      || TREE_CODE (*node) == LABEL_DECL)
+    {
+      /* Attribute cold processing is done later with lookup_attribute.  */
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "noinline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_noinline_attribute (tree *node, tree name,
+			   tree ARG_UNUSED (args),
+			   int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    DECL_UNINLINABLE (*node) = 1;
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "weak" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_weak_attribute (tree *node, tree name,
+		       tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags),
+		       bool * ARG_UNUSED (no_add_attrs))
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && DECL_DECLARED_INLINE_P (*node))
+    {
+      warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weak", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (VAR_OR_FUNCTION_DECL_P (*node))
+    declare_weak (*node);
+  else
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+
+  return NULL_TREE;
+}
+
+/* Handle a "target" attribute.  */
+
+static tree
+handle_target_attribute (tree *node, tree name, tree args, int flags,
+			 bool *no_add_attrs)
+{
+  /* Ensure we have a function declaration.  */
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (! targetm.target_option.valid_attribute_p (*node, name, args,
+						      flags))
+    *no_add_attrs = true;
+
+  /* Check that there's no empty string in values of the attribute.  */
+  for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
+    {
+      tree value = TREE_VALUE (t);
+      if (TREE_CODE (value) == STRING_CST
+	  && TREE_STRING_LENGTH (value) == 1
+	  && TREE_STRING_POINTER (value)[0] == '\0')
+	{
+	  warning (OPT_Wattributes, "empty string in attribute %<target%>");
+	  *no_add_attrs = true;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "used" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  if (TREE_CODE (node) == FUNCTION_DECL
+      || (VAR_P (node) && TREE_STATIC (node))
+      || (TREE_CODE (node) == TYPE_DECL))
+    {
+      TREE_USED (node) = 1;
+      DECL_PRESERVE_P (node) = 1;
+      if (VAR_P (node))
+	DECL_READ_P (node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler, except that IS_ALIAS tells us
+   whether this is an alias as opposed to ifunc attribute.  */
+
+static tree
+handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
+			      bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (!is_alias || !VAR_P (decl)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
+      /* A static variable declaration is always a tentative definition,
+	 but the alias is a non-tentative definition which overrides.  */
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
+    {
+      error ("%q+D defined both normally and as %qE attribute", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (!is_alias
+	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
+	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
+    {
+      error ("weak %q+D cannot be defined %qE", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Note that the very first time we process a nested declaration,
+     decl_function_context will not be set.  Indeed, *would* never
+     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
+     we do below.  After such frobbery, pushdecl would set the context.
+     In any case, this is never what we want.  */
+  else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
+    {
+      tree id;
+
+      id = TREE_VALUE (args);
+      if (TREE_CODE (id) != STRING_CST)
+	{
+	  error ("attribute %qE argument not a string", name);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+      id = get_identifier (TREE_STRING_POINTER (id));
+      /* This counts as a use of the object pointed to.  */
+      TREE_USED (id) = 1;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	DECL_INITIAL (decl) = error_mark_node;
+      else
+	TREE_STATIC (decl) = 1;
+
+      if (!is_alias)
+	{
+	  /* ifuncs are also aliases, so set that attribute too.  */
+	  DECL_ATTRIBUTES (decl)
+	    = tree_cons (get_identifier ("alias"), args,
+			 DECL_ATTRIBUTES (decl));
+	  DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
+					      NULL, DECL_ATTRIBUTES (decl));
+	}
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  if (decl_in_symtab_p (*node))
+    {
+      struct symtab_node *n = symtab_node::get (decl);
+      if (n && n->refuse_visibility_changes)
+	error ("%+qD declared %qs after being used",
+	       decl, is_alias ? "alias" : "ifunc");
+    }
+
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_alias_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
+}
+
 /* (end of attribute-handling).  */
 
 /* Language-dependent contents of a type.  */
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index f87351eaf82..84df6c100e6 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #define INCLUDE_MUTEX
+#include "libgccjit.h"
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -499,6 +500,54 @@ new_param (location *loc,
   return new param (this, inner);
 }
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_FN_ATTRIBUTE_ALIAS:
+      return "alias";
+    case GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE:
+      return "always_inline";
+    case GCC_JIT_FN_ATTRIBUTE_INLINE:
+      return NULL;
+    case GCC_JIT_FN_ATTRIBUTE_NOINLINE:
+      return "noinline";
+    case GCC_JIT_FN_ATTRIBUTE_TARGET:
+      return "target";
+    case GCC_JIT_FN_ATTRIBUTE_USED:
+      return "used";
+    case GCC_JIT_FN_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_FN_ATTRIBUTE_COLD:
+      return "cold";
+    case GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE:
+      return "returns_twice";
+    case GCC_JIT_FN_ATTRIBUTE_PURE:
+      return "pure";
+    case GCC_JIT_FN_ATTRIBUTE_CONST:
+      return "const";
+    case GCC_JIT_FN_ATTRIBUTE_WEAK:
+      return "weak";
+    case GCC_JIT_FN_ATTRIBUTE_NONNULL:
+      return "nonnull";
+    case GCC_JIT_FN_ATTRIBUTE_MAX:
+      return NULL;
+  }
+  return NULL;
+}
+
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_VARIABLE_ATTRIBUTE_MAX:
+      return NULL;
+  }
+  return NULL;
+}
+
 /* Construct a playback::function instance.  */
 
 playback::function *
@@ -509,7 +558,13 @@ new_function (location *loc,
 	      const char *name,
 	      const auto_vec<param *> *params,
 	      int is_variadic,
-	      enum built_in_function builtin_id)
+	      enum built_in_function builtin_id,
+	      const std::vector<gcc_jit_fn_attribute> &attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::string>> &string_attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::vector<int>>>
+					  &int_array_attributes)
 {
   int i;
   param *param;
@@ -543,6 +598,8 @@ new_function (location *loc,
   DECL_RESULT (fndecl) = resdecl;
   DECL_CONTEXT (resdecl) = fndecl;
 
+  tree fn_attributes = NULL_TREE;
+
   if (builtin_id)
     {
       gcc_assert (loc == NULL);
@@ -588,12 +645,62 @@ new_function (location *loc,
       DECL_DECLARED_INLINE_P (fndecl) = 1;
 
       /* Add attribute "always_inline": */
-      DECL_ATTRIBUTES (fndecl) =
-	tree_cons (get_identifier ("always_inline"),
-		   NULL,
-		   DECL_ATTRIBUTES (fndecl));
+      fn_attributes = tree_cons (get_identifier ("always_inline"),
+				 NULL,
+				 fn_attributes);
     }
 
+  /* All attributes need to be declared in `dummy-frontend.cc` and more
+     specifically in `jit_attribute_table`. */
+  for (auto attr: attributes)
+  {
+    if (attr == GCC_JIT_FN_ATTRIBUTE_INLINE)
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+    {
+      tree ident = get_identifier (attribute);
+      fn_attributes = tree_cons (ident, NULL_TREE, fn_attributes);
+    }
+  }
+
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (ident)
+      fn_attributes = tree_cons (ident, attribute_value, fn_attributes);
+  }
+
+  for (auto attr: int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (!ident)
+      continue;
+
+    tree tree_list = NULL_TREE;
+    tree *p_tree_list = &tree_list;
+    for (auto value : values)
+    {
+      tree int_value = build_int_cst (integer_type_node, value);
+      *p_tree_list = build_tree_list (NULL, int_value);
+      p_tree_list = &TREE_CHAIN (*p_tree_list);
+    }
+    fn_attributes = tree_cons (ident, tree_list, fn_attributes);
+  }
+
+  decl_attributes (&fndecl, fn_attributes, 0);
   function *func = new function (this, fndecl, kind);
   m_functions.safe_push (func);
   return func;
@@ -607,7 +714,9 @@ global_new_decl (location *loc,
 		 enum gcc_jit_global_kind kind,
 		 type *type,
 		 const char *name,
-		 enum global_var_flags flags)
+		 enum global_var_flags flags,
+		 const std::vector<std::pair<gcc_jit_variable_attribute,
+					     std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -652,9 +761,32 @@ global_new_decl (location *loc,
   if (loc)
     set_tree_location (inner, loc);
 
+  set_variable_string_attribute (attributes, inner);
+
   return inner;
 }
 
+void
+playback::
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			       std::string>> &string_attributes,
+  tree decl)
+{
+  tree var_attributes = NULL_TREE;
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    tree ident = get_identifier (variable_attribute_to_string (name));
+    if (ident)
+      var_attributes = tree_cons (ident, attribute_value, var_attributes);
+  }
+  decl_attributes (&decl, var_attributes, 0);
+}
+
 /* In use by new_global and new_global_initialized.  */
 
 playback::lvalue *
@@ -674,10 +806,12 @@ new_global (location *loc,
 	    enum gcc_jit_global_kind kind,
 	    type *type,
 	    const char *name,
-	    enum global_var_flags flags)
+	    enum global_var_flags flags,
+	    const std::vector<std::pair<gcc_jit_variable_attribute,
+					std::string>> &attributes)
 {
   tree inner =
-    global_new_decl (loc, kind, type, name, flags);
+    global_new_decl (loc, kind, type, name, flags, attributes);
 
   return global_finalize_lvalue (inner);
 }
@@ -818,13 +952,15 @@ playback::context::
 new_global_initialized (location *loc,
 			enum gcc_jit_global_kind kind,
 			type *type,
-                        size_t element_size,
+			size_t element_size,
 			size_t initializer_num_elem,
 			const void *initializer,
 			const char *name,
-			enum global_var_flags flags)
+			enum global_var_flags flags,
+			const std::vector<std::pair<gcc_jit_variable_attribute,
+						    std::string>> &attributes)
 {
-  tree inner = global_new_decl (loc, kind, type, name, flags);
+  tree inner = global_new_decl (loc, kind, type, name, flags, attributes);
 
   vec<constructor_elt, va_gc> *constructor_elements = NULL;
 
@@ -1812,7 +1948,9 @@ playback::lvalue *
 playback::function::
 new_local (location *loc,
 	   type *type,
-	   const char *name)
+	   const char *name,
+	   const std::vector<std::pair<gcc_jit_variable_attribute,
+				       std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -1825,6 +1963,8 @@ new_local (location *loc,
   DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
   BIND_EXPR_VARS (m_inner_bind_expr) = inner;
 
+  set_variable_string_attribute (attributes, inner);
+
   if (loc)
     set_tree_location (inner, loc);
   return new lvalue (m_ctxt, inner);
@@ -1947,6 +2087,9 @@ postprocess ()
 
       current_function_decl = NULL;
     }
+    else
+      /* Add to cgraph to output aliases: */
+      rest_of_decl_compilation (m_inner_fndecl, true, 0);
 }
 
 /* Don't leak vec's internal buffer (in non-GC heap) when we are
@@ -3365,7 +3508,7 @@ void
 playback::context::
 init_types ()
 {
-  /* See lto_init() in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc 
+  /* See lto_init () in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc
      for reference. If TYPE_NAME is not set, debug info will not contain types */
 #define NAME_TYPE(t,n) \
 if (t) \
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 9654507a34d..05bafcd21c4 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -21,7 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef JIT_PLAYBACK_H
 #define JIT_PLAYBACK_H
 
+#include <string>
 #include <utility> // for std::pair
+#include <vector>
 
 #include "timevar.h"
 #include "varasm.h"
@@ -35,12 +37,21 @@ namespace gcc {
 
 namespace jit {
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr);
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr);
+
 /**********************************************************************
  Playback.
  **********************************************************************/
 
 namespace playback {
 
+void
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			      std::string>> &attributes,
+  tree decl);
+
 /* playback::context is an abstract base class.
 
    The two concrete subclasses are:
@@ -104,14 +115,22 @@ public:
 		const char *name,
 		const auto_vec<param *> *params,
 		int is_variadic,
-		enum built_in_function builtin_id);
+		enum built_in_function builtin_id,
+		const std::vector<gcc_jit_fn_attribute> &attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::string>> &string_attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::vector<int>>>
+					    &int_array_attributes);
 
   lvalue *
   new_global (location *loc,
 	      enum gcc_jit_global_kind kind,
 	      type *type,
 	      const char *name,
-	      enum global_var_flags flags);
+	      enum global_var_flags flags,
+	      const std::vector<std::pair<gcc_jit_variable_attribute,
+					  std::string>> &attributes);
 
   lvalue *
   new_global_initialized (location *loc,
@@ -121,7 +140,11 @@ public:
                           size_t initializer_num_elem,
                           const void *initializer,
 			  const char *name,
-			  enum global_var_flags flags);
+			  enum global_var_flags flags,
+			  const std::vector<std::pair<
+					    gcc_jit_variable_attribute,
+					    std::string>>
+					    &attributes);
 
   rvalue *
   new_ctor (location *log,
@@ -306,7 +329,9 @@ private:
                    enum gcc_jit_global_kind kind,
                    type *type,
 		   const char *name,
-		   enum global_var_flags flags);
+		   enum global_var_flags flags,
+		   const std::vector<std::pair<gcc_jit_variable_attribute,
+					       std::string>> &attributes);
   lvalue *
   global_finalize_lvalue (tree inner);
 
@@ -500,7 +525,9 @@ public:
   lvalue *
   new_local (location *loc,
 	     type *type,
-	     const char *name);
+	     const char *name,
+	     const std::vector<std::pair<gcc_jit_variable_attribute,
+					 std::string>> &attributes);
 
   block*
   new_block (const char *name);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index 686c0587079..6ffadbea127 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-builtins.h"
 #include "jit-recording.h"
 #include "jit-playback.h"
+#include <sstream>
 
 namespace gcc {
 namespace jit {
@@ -2068,7 +2069,7 @@ recording::memento::get_debug_string ()
 void
 recording::memento::write_to_dump (dump &d)
 {
-  d.write("  %s\n", get_debug_string ());
+  d.write ("  %s\n", get_debug_string ());
 }
 
 /* The implementation of class gcc::jit::recording::string.  */
@@ -4026,6 +4027,13 @@ void recording::lvalue::set_alignment (unsigned bytes)
   m_alignment = bytes;
 }
 
+void recording::lvalue::add_string_attribute (
+	gcc_jit_variable_attribute attribute,
+	const char* value)
+{
+  m_string_attributes.push_back (std::make_pair (attribute, std::string (value)));
+}
+
 /* The implementation of class gcc::jit::recording::param.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -4102,7 +4110,10 @@ recording::function::function (context *ctxt,
   m_builtin_id (builtin_id),
   m_locals (),
   m_blocks (),
-  m_fn_ptr_type (NULL)
+  m_fn_ptr_type (NULL),
+  m_attributes (),
+  m_string_attributes (),
+  m_int_array_attributes ()
 {
   for (int i = 0; i< num_params; i++)
     {
@@ -4161,7 +4172,10 @@ recording::function::replay_into (replayer *r)
 				     m_name->c_str (),
 				     &params,
 				     m_is_variadic,
-				     m_builtin_id));
+				     m_builtin_id,
+				     m_attributes,
+				     m_string_attributes,
+				     m_int_array_attributes));
 }
 
 /* Create a recording::local instance and add it to
@@ -4210,6 +4224,40 @@ recording::function::new_block (const char *name)
 void
 recording::function::write_to_dump (dump &d)
 {
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+      d.write ("__attribute(%s)__\n", attribute);
+  }
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
+  for (auto attr: m_int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+    if (attribute)
+    {
+      d.write ("__attribute(%s(", attribute);
+      for (size_t i = 0; i < values.size(); ++i)
+      {
+	if (i > 0)
+	  d.write (", %d", values[i]);
+	else
+	  d.write ("%d", values[i]);
+      }
+      d.write ("))__\n");
+    }
+  }
+
   switch (m_kind)
     {
     default: gcc_unreachable ();
@@ -4404,6 +4452,31 @@ recording::function::get_address (recording::location *loc)
   return result;
 }
 
+void
+recording::function::add_attribute (gcc_jit_fn_attribute attribute)
+{
+  m_attributes.push_back (attribute);
+}
+
+void
+recording::function::add_string_attribute (gcc_jit_fn_attribute attribute,
+					   const char* value)
+{
+  m_string_attributes.push_back (
+	std::make_pair (attribute, std::string (value)));
+}
+
+void
+recording::function::add_integer_array_attribute (
+	gcc_jit_fn_attribute attribute,
+	const int* value,
+	size_t length)
+{
+  m_int_array_attributes.push_back (std::make_pair (
+    attribute,
+    std::vector<int> (value, value + length)));
+}
+
 /* Implementation of recording::memento::make_debug_string for
    functions.  */
 
@@ -4425,6 +4498,39 @@ static const char * const names_of_function_kinds[] = {
 
 /* Implementation of recording::memento::write_reproducer for functions. */
 
+static const char * const fn_attribute_reproducer_strings[] =
+{
+  "GCC_JIT_FN_ATTRIBUTE_ALIAS",
+  "GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_NOINLINE",
+  "GCC_JIT_FN_ATTRIBUTE_TARGET",
+  "GCC_JIT_FN_ATTRIBUTE_USED",
+  "GCC_JIT_FN_ATTRIBUTE_VISIBILITY",
+  "GCC_JIT_FN_ATTRIBUTE_COLD",
+  "GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE",
+  "GCC_JIT_FN_ATTRIBUTE_PURE",
+  "GCC_JIT_FN_ATTRIBUTE_CONST",
+  "GCC_JIT_FN_ATTRIBUTE_WEAK",
+  "GCC_JIT_FN_ATTRIBUTE_NONNULL",
+};
+
+std::string
+get_vector_int_debug (std::vector<int> &values)
+{
+  std::stringstream s;
+
+  s << "{";
+  for(auto it = values.begin(); it != values.end(); ++it)
+    {
+      if (it != values.begin() )
+	 s << ", ";
+      s << *it;
+    }
+  s << "}";
+  return s.str();
+}
+
 void
 recording::function::write_reproducer (reproducer &r)
 {
@@ -4467,6 +4573,25 @@ recording::function::write_reproducer (reproducer &r)
 	   m_params.length (),
 	   params_id,
 	   m_is_variadic);
+  for (auto attribute : m_attributes)
+    r.write("  gcc_jit_function_add_attribute (%s, %s);\n",
+	    id,
+	    fn_attribute_reproducer_strings[attribute]);
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_function_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+  for (auto attribute : m_int_array_attributes) {
+    r.write("  gcc_jit_function_add_integer_array_attribute (%s,\n"
+	    "                                                %s,\n"
+	    "                                                (int[])%s,\n"
+	    "                                                %lu);\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    get_vector_int_debug (std::get<1>(attribute)).c_str(),
+	    std::get<1>(attribute).size ());
+  }
 }
 
 
@@ -4879,12 +5004,14 @@ recording::global::replay_into (replayer *r)
 				 / m_type->dereference ()->get_size (),
 				 m_initializer,
 				 playback_string (m_name),
-				 m_flags)
+				 m_flags,
+				 m_string_attributes)
     : r->new_global (playback_location (r, m_loc),
 		     m_kind,
 		     m_type->playback_type (),
 		     playback_string (m_name),
-		     m_flags);
+		     m_flags,
+		     m_string_attributes);
 
   if (m_tls_model != GCC_JIT_TLS_MODEL_NONE)
     global->set_tls_model (recording::tls_models[m_tls_model]);
@@ -4943,6 +5070,15 @@ recording::global::write_to_dump (dump &d)
       break;
     }
 
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = variable_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
   d.write ("%s %s",
 	   m_type->get_debug_string (),
 	   get_debug_string ());
@@ -5013,6 +5149,10 @@ static const char * const tls_model_enum_strings[] = {
   "GCC_JIT_TLS_MODEL_LOCAL_EXEC",
 };
 
+static const char * const gcc_jit_variable_attribute_enum_strings[] = {
+  "GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY",
+};
+
 void
 recording::global::write_reproducer (reproducer &r)
 {
@@ -5042,6 +5182,13 @@ recording::global::write_reproducer (reproducer &r)
      id,
      m_link_section->c_str ());
 
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_lvalue_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+
+
   if (m_initializer)
     switch (m_type->dereference ()->get_size ())
       {
@@ -6622,7 +6769,8 @@ recording::local::replay_into (replayer *r)
   playback::lvalue *obj = m_func->playback_function ()
       ->new_local (playback_location (r, m_loc),
 		   m_type->playback_type (),
-		   playback_string (m_name));
+		   playback_string (m_name),
+		   m_string_attributes);
 
   if (m_reg_name != NULL)
     obj->set_register_name (m_reg_name->c_str ());
@@ -6644,9 +6792,9 @@ recording::local::write_to_dump (dump &d)
 {
   if (d.update_locations ())
     m_loc = d.make_location ();
-  d.write("  %s %s;\n",
-	  m_type->get_debug_string (),
-	  get_debug_string ());
+  d.write ("  %s %s;\n",
+	   m_type->get_debug_string (),
+	   get_debug_string ());
 }
 
 void
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index b951c715ca5..cd2e0adbe30 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -23,6 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "jit-common.h"
 #include "jit-logging.h"
+#include "libgccjit.h"
+
+#include <string>
+#include <vector>
 
 class timer;
 
@@ -1216,7 +1220,8 @@ public:
     m_link_section (NULL),
     m_reg_name (NULL),
     m_tls_model (GCC_JIT_TLS_MODEL_NONE),
-    m_alignment (0)
+    m_alignment (0),
+    m_string_attributes ()
   {}
 
   playback::lvalue *
@@ -1236,8 +1241,12 @@ public:
   as_rvalue () { return this; }
 
   const char *access_as_rvalue (reproducer &r) override;
+
+  void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value);
+
   virtual const char *access_as_lvalue (reproducer &r);
   virtual bool is_global () const { return false; }
+  virtual bool is_local () const { return false; }
   void set_tls_model (enum gcc_jit_tls_model model);
   void set_link_section (const char *name);
   void set_register_name (const char *reg_name);
@@ -1249,6 +1258,8 @@ protected:
   string *m_reg_name;
   enum gcc_jit_tls_model m_tls_model;
   unsigned m_alignment;
+  std::vector<std::pair<gcc_jit_variable_attribute,
+	      std::string>> m_string_attributes;
 };
 
 class param : public lvalue
@@ -1342,6 +1353,10 @@ public:
 
   rvalue *get_address (location *loc);
 
+  void add_attribute (gcc_jit_fn_attribute attribute);
+  void add_string_attribute (gcc_jit_fn_attribute attribute, const char* value);
+  void add_integer_array_attribute (gcc_jit_fn_attribute attribute, const int* value, size_t length);
+
 private:
   string * make_debug_string () final override;
   void write_reproducer (reproducer &r) final override;
@@ -1357,6 +1372,9 @@ private:
   auto_vec<local *> m_locals;
   auto_vec<block *> m_blocks;
   type *m_fn_ptr_type;
+  std::vector<gcc_jit_fn_attribute> m_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::string>> m_string_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::vector<int>>> m_int_array_attributes;
 };
 
 class block : public memento
@@ -2086,6 +2104,8 @@ public:
 
   void visit_children (rvalue_visitor *) final override {}
 
+  bool is_local () const final override { return true; }
+
   void write_to_dump (dump &d) final override;
 
 private:
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 8ecfe4aaa42..9616f3802b8 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -3965,6 +3965,73 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
   return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
 }
 
+void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				gcc_jit_fn_attribute attribute)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_attribute (attribute);
+}
+
+void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       gcc_jit_fn_attribute attribute,
+				       const char* value)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (value, NULL, NULL, "NULL value");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_string_attribute (attribute, value);
+}
+
+/* This function adds an attribute with multiple integer values.  For example
+   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
+   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
+   and `2` in `values` (and set `length` to `2`). */
+void
+gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+					      gcc_jit_fn_attribute attribute,
+					      const int* values,
+					      size_t length)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_integer_array_attribute (attribute, values, length);
+}
+
+void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     gcc_jit_variable_attribute attribute,
+				     const char* value)
+{
+  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");
+  RETURN_IF_FAIL (value, NULL, NULL, "NULL value");
+  RETURN_IF_FAIL (variable->is_global () || variable->is_local (),
+		  NULL,
+		  NULL,
+		  "variable should be a variable");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_VARIABLE_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_variable_attribute` enum value");
+
+  variable->add_string_attribute (attribute, value);
+}
+
 /* 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 cbcfabba3e8..235cab053e0 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1999,6 +1999,61 @@ gcc_jit_vector_type_get_element_type (gcc_jit_vector_type *vector_type);
 extern gcc_jit_type *
 gcc_jit_type_unqualified (gcc_jit_type *type);
 
+#define LIBGCCJIT_HAVE_ATTRIBUTES
+
+/* Function attributes.  */
+enum gcc_jit_fn_attribute
+{
+  GCC_JIT_FN_ATTRIBUTE_ALIAS,
+  GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_NOINLINE,
+  GCC_JIT_FN_ATTRIBUTE_TARGET,
+  GCC_JIT_FN_ATTRIBUTE_USED,
+  GCC_JIT_FN_ATTRIBUTE_VISIBILITY,
+  GCC_JIT_FN_ATTRIBUTE_COLD,
+  GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE,
+  GCC_JIT_FN_ATTRIBUTE_PURE,
+  GCC_JIT_FN_ATTRIBUTE_CONST,
+  GCC_JIT_FN_ATTRIBUTE_WEAK,
+  GCC_JIT_FN_ATTRIBUTE_NONNULL,
+
+  /* Maximum value of this enum, should always be last. */
+  GCC_JIT_FN_ATTRIBUTE_MAX,
+};
+
+/* Add an attribute to a function.  */
+extern void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				enum gcc_jit_fn_attribute attribute);
+
+extern void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       enum gcc_jit_fn_attribute attribute,
+				       const char* value);
+
+extern void
+gcc_jit_function_add_integer_array_attribute (
+  gcc_jit_function *func,
+  enum gcc_jit_fn_attribute attribute,
+  const int* value,
+  size_t length);
+
+/* Variable attributes.  */
+enum gcc_jit_variable_attribute
+{
+  GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY,
+
+  /* Maximum value of this enum, should always be last. */
+  GCC_JIT_VARIABLE_ATTRIBUTE_MAX,
+};
+
+/* Add a string attribute to a variable.  */
+extern void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     enum gcc_jit_variable_attribute attribute,
+				     const char* value);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index b62f5de72d0..dfb8a9d51fb 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -276,3 +276,11 @@ LIBGCCJIT_ABI_25 {
   global:
     gcc_jit_type_get_restrict;
 } LIBGCCJIT_ABI_24;
+
+LIBGCCJIT_ABI_26 {
+  global:
+    gcc_jit_function_add_attribute;
+    gcc_jit_function_add_string_attribute;
+    gcc_jit_lvalue_add_string_attribute;
+    gcc_jit_function_add_integer_array_attribute;
+} LIBGCCJIT_ABI_25;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index e762563f9bd..84001203352 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -32,6 +32,9 @@
 /* test-add-driver-options.c: We don't use this one, since the extra options
    affect the whole context.  */
 
+/* test-alias-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* test-alignment.c */
 #define create_code create_code_alignment
 #define verify_code verify_code_alignment
@@ -39,6 +42,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-always_inline-attribute.c: This can't be in the testcases array as it needs
+   the `-O0` flag.  */
+
 /* test-arith-overflow.c */
 #define create_code create_code_arith_overflow
 #define verify_code verify_code_arith_overflow
@@ -119,6 +125,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-cold-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-constants.c */
 #define create_code create_code_constants
 #define verify_code verify_code_constants
@@ -126,6 +135,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-const-attribute.c: This can't be in the testcases array as it needs
+   the `-O3` flag.  */
+
 /* test-debug-strings.c */
 #define create_code create_code_debug_strings
 #define verify_code verify_code_debug_strings
@@ -268,6 +280,12 @@
 #undef create_code
 #undef verify_code
 
+/* test-noinline-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
+/* test-nonnull-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-pr103562.c: We don't add this one, since it touches
    the optimization level of the context as a whole.  */
 
@@ -299,6 +317,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-pure-attribute.c: This can't be in the testcases array as it needs
+   the `-O3` flag.  */
+
 /* test-reading-struct.c */
 #define create_code create_code_reading_struct
 #define verify_code verify_code_reading_struct
@@ -313,7 +334,7 @@
 #undef create_code
 #undef verify_code
 
-/* test-restrict.c: This can't be in the testcases array as it needs
+/* test-restrict-attribute.c: This can't be in the testcases array as it needs
    the `-O3` flag.  */
 
 /* test-register-variable.c: This can't be in the testcases array as it
@@ -350,6 +371,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-used-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-using-global.c */
 #define create_code create_code_using_global
 #define verify_code verify_code_using_global
@@ -361,6 +385,9 @@
    of gcc_jit_context_set_bool_allow_unreachable_blocks affects the whole
    context.  */
 
+/* test-variable-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* test-vector-types.cc: We don't use this, since it's C++.  */
 
 /* test-version.c */
@@ -377,6 +404,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-weak-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* Now expose the individual testcases as instances of this struct.  */
 
 struct testcase
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 8bf7e51c24f..56972064d30 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
 	pass "${asm_filename} output pattern test, ${dg-output-text}"
 	verbose "Passed test for output pattern ${dg-output-text}" 3
     }
+}
+
+# Assuming that a .s file has been written out named
+# OUTPUT_FILENAME, check that the argument doesn't match
+# the output file.
+proc jit-verify-assembler-output-not { args } {
+    verbose "jit-verify-assembler: $args"
+
+    set dg-output-text [lindex $args 0]
+    verbose "dg-output-text: ${dg-output-text}"
+
+    upvar 2 name name
+    verbose "name: $name"
+
+    upvar 2 prog prog
+    verbose "prog: $prog"
+    set asm_filename [jit-get-output-filename $prog]
+    verbose "  asm_filename: ${asm_filename}"
 
+    # Read the assembly file.
+    set f [open $asm_filename r]
+    set content [read $f]
+    close $f
+
+    # Verify that the assembly matches the regex.
+    if { [regexp ${dg-output-text} $content] } {
+	fail "${asm_filename} output pattern test, is ${content}, should not match ${dg-output-text}"
+	verbose "Failed test for output pattern ${dg-output-text}" 3
+    } else {
+	pass "${asm_filename} output pattern test, ${dg-output-text}"
+	verbose "Passed test for output pattern ${dg-output-text}" 3
+    }
 }
+
+
 # Assuming that a .o file has been written out named
 # OUTPUT_FILENAME, invoke the driver to try to turn it into
 # an executable, and try to run the result.
diff --git a/gcc/testsuite/jit.dg/test-alias-attribute.c b/gcc/testsuite/jit.dg/test-alias-attribute.c
new file mode 100644
index 00000000000..eb29003dfc9
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-alias-attribute.c
@@ -0,0 +1,50 @@
+/* { 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-alias-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+void xxx () {}
+void f () __attribute__ ((alias ("xxx")));
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_EXPORTED,
+          void_type,
+          "xxx",
+          0, NULL,
+          0);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_IMPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_string_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_ALIAS, "xxx");
+
+  /* void xxx () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".set\\s+f,xxx" } } */
diff --git a/gcc/testsuite/jit.dg/test-always_inline-attribute.c b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
new file mode 100644
index 00000000000..5c3f386663f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
@@ -0,0 +1,153 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O0".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 0);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-always_inline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 gcc_jit_type *pint_type)
+{
+  /* The `a` function argument */
+  gcc_jit_param *a = gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          1, &a,
+          0);
+
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((always_inline))
+static inline int removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+static int not_removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+int foo () {
+  int x = 0;
+  x += removed(NULL);
+  x += not_removed(NULL);
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer (int_type);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, pint_type);
+  /* This one is to declare the function as "inline" */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_INLINE);
+  /* __attribute__ ((always_inline)) */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, pint_type);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(NULL); */
+  gcc_jit_rvalue *null = gcc_jit_context_null (ctxt, pint_type);
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 1, &null));
+  
+  /* x += not_removed(NULL); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 1, &null));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
new file mode 100644
index 00000000000..8dc7ec5a34b
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
@@ -0,0 +1,54 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the cold
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-cold-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+int
+__attribute__ ((cold))
+t()
+{
+  return -1;
+}
+
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "t",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(func_t, GCC_JIT_FN_ATTRIBUTE_COLD);
+  gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+  gcc_jit_rvalue *ret = gcc_jit_context_new_rvalue_from_int (ctxt,
+    int_type,
+    -1);
+
+  gcc_jit_block_end_with_return (block, NULL, ret);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "orl" } } */
diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
new file mode 100644
index 00000000000..c06742d163f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-const-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the const
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-const-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((const))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_CONST);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
new file mode 100644
index 00000000000..a455b4493fd
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
@@ -0,0 +1,121 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-noinline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func
+    = gcc_jit_context_new_function(ctxt, NULL,
+	  GCC_JIT_FUNCTION_INTERNAL,
+	  int_type,
+	  func_name,
+	  0, NULL,
+	  0);
+  gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+  gcc_jit_block_add_extended_asm (block, NULL, "");
+  gcc_jit_block_end_with_return (block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((noinline))
+static int not_removed() {
+  asm("");
+  return 1;
+}
+static int removed() {
+  asm("");
+  return 2;
+}
+int foo () {
+  int x = 0;
+  x += removed();
+  x += not_removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((no_inline)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_NOINLINE);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed.isra.0,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed.isra.0,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
new file mode 100644
index 00000000000..3306f890657
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
@@ -0,0 +1,94 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-nonnull.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__((nonnull(1)))
+int t(int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+	  GCC_JIT_FUNCTION_EXPORTED,
+	  int_type,
+	  "t",
+	  1, &a,
+	  0);
+  /* Adding `nonnull(1)` attribute. */
+  int indexes[1] = {1};
+  gcc_jit_function_add_integer_array_attribute (
+    func_t,
+    GCC_JIT_FN_ATTRIBUTE_NONNULL,
+    indexes,
+    1
+  );
+
+  /* if (!a) {
+    return -1;
+  } */
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func_t, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func_t, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func_t, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "if block" was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "testq" } } */
+/* { dg-final { jit-verify-assembler-output-not "-1" } } */
diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
new file mode 100644
index 00000000000..0c9ba1366e0
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
@@ -0,0 +1,134 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the pure
+   attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-pure-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((pure))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_PURE);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
new file mode 100644
index 00000000000..7d7444b624f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
@@ -0,0 +1,77 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 to see that the restrict
+	 attribute affects the optimizations. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+	// Set "-O3".
+	gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-restrict.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+	/* Let's try to inject the equivalent of:
+void t(int *__restrict__ a, int *__restrict__ b, char *__restrict__ c) {
+	*a += *c;
+	*b += *c;
+}
+	*/
+	gcc_jit_type *int_type =
+		gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+	gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+	gcc_jit_type *pint_restrict_type = gcc_jit_type_get_restrict(pint_type);
+
+	gcc_jit_type *void_type =
+		gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+	gcc_jit_param *a =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "a");
+	gcc_jit_param *b =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "b");
+	gcc_jit_param *c =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "c");
+	gcc_jit_param *params[3] = {a, b, c};
+
+	gcc_jit_function *func_t =
+		gcc_jit_context_new_function (ctxt, NULL,
+					GCC_JIT_FUNCTION_EXPORTED,
+					void_type,
+					"t",
+					3, params,
+					0);
+
+	gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+
+	/* *a += *c; */
+	gcc_jit_block_add_assignment_op (
+		block, NULL,
+		gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (a), NULL),
+		GCC_JIT_BINARY_OP_PLUS,
+		gcc_jit_lvalue_as_rvalue (
+			gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL)));
+	/* *b += *c; */
+	gcc_jit_block_add_assignment_op (
+		block, NULL,
+		gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (b), NULL),
+		GCC_JIT_BINARY_OP_PLUS,
+		gcc_jit_lvalue_as_rvalue (
+			gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL)));
+
+	gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "addl\\s+%eax,\\s+(%rdi)
+\\s+addl\\s+%eax,\\s+(%rsi)" } } */
diff --git a/gcc/testsuite/jit.dg/test-used-attribute.c b/gcc/testsuite/jit.dg/test-used-attribute.c
new file mode 100644
index 00000000000..cb20952c687
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-used-attribute.c
@@ -0,0 +1,112 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-used-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__((used))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo() {
+  int x = 0;
+  x += not_removed();
+  x += removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((used)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_USED);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-variable-attribute.c b/gcc/testsuite/jit.dg/test-variable-attribute.c
new file mode 100644
index 00000000000..ea854ff4a9f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-variable-attribute.c
@@ -0,0 +1,46 @@
+/* { 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-variable-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+int PRIVATE __attribute__ ((visibility ("hidden"))) = 42;
+int PUBLIC = 12;
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `PRIVATE` variable. */
+  gcc_jit_lvalue *private = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PRIVATE");
+  gcc_jit_lvalue_add_string_attribute (private,
+    GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, "hidden");
+  gcc_jit_rvalue *rval = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 42);
+  gcc_jit_global_set_initializer_rvalue (private, rval);
+
+  /* Creating the `PUBLIC` variable. */
+  gcc_jit_lvalue *public = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PUBLIC");
+  gcc_jit_rvalue *rval2 = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 12);
+  gcc_jit_global_set_initializer_rvalue (public, rval2);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".hidden\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output-not ".hidden\\s+PUBLIC" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PUBLIC" } } */
diff --git a/gcc/testsuite/jit.dg/test-weak-attribute.c b/gcc/testsuite/jit.dg/test-weak-attribute.c
new file mode 100644
index 00000000000..546ade1c3c4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-weak-attribute.c
@@ -0,0 +1,41 @@
+/* { 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-weak-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__ ((weak))
+void f () {}
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_WEAK);
+
+  /* void f () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (f_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".weak\\s+f" } } */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-11  0:00   ` Guillaume Gomez
@ 2024-01-11 18:46     ` David Malcolm
  2024-01-11 21:40       ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: David Malcolm @ 2024-01-11 18:46 UTC (permalink / raw)
  To: Guillaume Gomez; +Cc: jit, Antoni, gcc-patches

On Thu, 2024-01-11 at 01:00 +0100, Guillaume Gomez wrote:
> Hi David.
> 
> Thanks for the review!
> 
> > > +.. function::  void\
> > > +               gcc_jit_lvalue_add_string_attribute
> > > (gcc_jit_lvalue *variable,
> > > +                                                    enum
> > > gcc_jit_fn_attribute attribute,
> >                                                                   
> > ^^
> > 
> > This got out of sync with the declaration in the header file; it
> > should
> > be enum gcc_jit_variable_attribute attribute
> 
> Indeed, good catch!
> 
> > I took a brief look through the handler functions and with the
> > above
> > caveat I didn't see anything obviously wrong.  I'm going to assume
> > this
> > code is OK given that presumably you've been testing it within
> > rustc,
> > right?
> 
> Both in rustc and in the JIT tests we added.
> 
> [..snip...]
> 
> I added all the missing `RETURN_IF_FAIL` you mentioned. None of the
> arguments should be `NULL` so it was a mistake not to check it.
> 
> [..snip...]
> 
> I removed the tests comments as you mentioned.
> 
> > Please update jit.dg/all-non-failing-tests.h for the new tests;
> > it's
> > meant to list all of the (non failing) tests alphabetically.
> 
> It's not always correctly sorted. Might be worth sending a patch
> after this
> one gets merged to fix that.
> 
> > I *think* all of the new tests aren't suitable to be run as part of
> > a
> > shared context (e.g. due to touching the optimization level or
> > examining generated asm), so they should be listed in that header
> > with
> > comments explaining why.
> 
> I added them with a comment on top of each of them.
> 
> I joined the new patch version.
> 
> Thanks again for the review!

Thanks for the updated patch.  I noticed a few minor issues:

> diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
> index bb51f037b7e..b1aedc03787 100644
> --- a/gcc/jit/docs/topics/types.rst
> +++ b/gcc/jit/docs/topics/types.rst
> @@ -553,3 +553,80 @@ Reflection API
>     .. code-block:: c
>  
>        #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
> +
> +.. function::  void\
> +               gcc_jit_function_add_attribute (gcc_jit_function *func,
> +                                               enum gcc_jit_fn_attribute attribute)
> +
> +     Add an attribute ``attribute`` to a function ``func``.
> +
> +     This is equivalent to the following code:
> +
> +  .. code-block:: c
> +
> +    __attribute__((always_inline))
> +
> +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> +   its presence using
> +
> +   .. code-block:: c
> +
> +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> +
> +.. function::  void\
> +               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
> +                                                      enum gcc_jit_fn_attribute attribute,
> +                                                      const char *value)
> +
> +     Add a string attribute ``attribute`` with value ``value`` to a function
> +     ``func``.
> +
> +     This is equivalent to the following code:
> +
> +  .. code-block:: c
> +
> +    __attribute__ ((alias ("xxx")))
> +
> +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> +   its presence using
> +
> +   .. code-block:: c
> +
> +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> +
> +.. function::  void\
> +               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
> +                                                             enum gcc_jit_fn_attribute attribute,
> +                                                             const int *value,
> +                                                             size_t length)
> +
> +     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
> +     function ``func``. The integer values must be the same as you would write
> +     them in a C code.
> +
> +     This is equivalent to the following code:
> +
> +  .. code-block:: c
> +
> +    __attribute__ ((nonnull (1, 2)))
> +
> +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> +   its presence using
> +
> +   .. code-block:: c
> +
> +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> +
> +.. function::  void\
> +               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> +                                                    enum gcc_jit_variable_attribute attribute,
> +                                                    const char *value)
> +
> +     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
> +
> +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> +   its presence using
> +
> +   .. code-block:: c
> +
> +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES

The above looks correct, but the patch adds the entrypoint descriptions
to topics/types.rst, which seems like the wrong place.  The function-
related ones should be in topics/functions.rst in the "Functions"
section and the lvalue/variable one in topics/expression.rst after the
"Global variables" section.

> diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> index e762563f9bd..84001203352 100644
> --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h

[...snip...]

> @@ -313,7 +334,7 @@
>  #undef create_code
>  #undef verify_code
>  
> -/* test-restrict.c: This can't be in the testcases array as it needs
> +/* test-restrict-attribute.c: This can't be in the testcases array as it needs
>     the `-O3` flag.  */

test-restrict.c is a pre-existing testcase, so please don't delete its
entry.
BTW, the ChangeLog entry mentions adding test-restrict.c, but the patch
doesn't add it, so that part of the proposed ChangeLog is wrong.

Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
> new file mode 100644
> index 00000000000..8dc7ec5a34b
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> @@ -0,0 +1,54 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the cold
> +   attribute affects the optimizations. */

Please delete the above comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
> new file mode 100644
> index 00000000000..c06742d163f
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> @@ -0,0 +1,134 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O3 to see that the const
> +   attribute affects the optimizations. */

Please delete the above comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O3".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> new file mode 100644
> index 00000000000..a455b4493fd
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> @@ -0,0 +1,121 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
> +   attribute affects the optimizations. */

Please delete the above comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> new file mode 100644
> index 00000000000..3306f890657
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> @@ -0,0 +1,94 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
> +   attribute affects the optimizations. */

Please delete the above comment.


> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O2".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> +}

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
> new file mode 100644
> index 00000000000..0c9ba1366e0
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> @@ -0,0 +1,134 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O3 to see that the pure
> +   attribute affects the optimizations. */

Please delete the above comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +  // Set "-O3".
> +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> +}
> +

[...snip...]

> diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> new file mode 100644
> index 00000000000..7d7444b624f
> --- /dev/null
> +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> @@ -0,0 +1,77 @@
> +/* { dg-do compile { target x86_64-*-* } } */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#include "libgccjit.h"
> +
> +/* We don't want set_options() in harness.h to set -O3 to see that the restrict
> +	 attribute affects the optimizations. */

Please delete this comment.

> +#define TEST_ESCHEWS_SET_OPTIONS
> +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> +{
> +	// Set "-O3".
> +	gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> +}
> +

[...snip...]

Otherwise, looks good, assuming that the patch has been tested with the
full jit testsuite.

Thanks again
Dave


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-11 18:46     ` David Malcolm
@ 2024-01-11 21:40       ` Guillaume Gomez
  2024-01-11 22:38         ` David Malcolm
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2024-01-11 21:40 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit, Antoni, gcc-patches

Hi David,

> The above looks correct, but the patch adds the entrypoint descriptions
> to topics/types.rst, which seems like the wrong place.  The function-
> related ones should be in topics/functions.rst in the "Functions"
> section and the lvalue/variable one in topics/expression.rst after the
> "Global variables" section.

Ah indeed. Mix-up on my end. Fixed it.

> test-restrict.c is a pre-existing testcase, so please don't delete its
> entry.

Ah indeed, I went too quickly and thought it was a test I renamed...

> BTW, the ChangeLog entry mentions adding test-restrict.c, but the patch
> doesn't add it, so that part of the proposed ChangeLog is wrong.
>
> Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?

I messed up a bit, fixed it thanks to you. I didn't run the script in my last
update but just did:

```
$ contrib/gcc-changelog/git_check_commit.py $(git log -1 --format=%h)
Checking 3849ee2eadf0eeec2b0080a5142ced00be96a60d: OK
```

> Otherwise, looks good, assuming that the patch has been tested with the
> full jit testsuite.

When rebasing on upstream yesterday I discovered that two tests
were not working anymore. For the first one, it was simply because of
the changes in `dummy-frontend.cc`. For the second one
(test-noinline-attribute.c), it was because the rules for inlining changed
since we wrote this patch apparently (our fork is very late). Antoni discovered
that we could just add a call to `asm` to prevent this from happening so I
added it.

So yes, all jit tests are passing as expected. :)

Le jeu. 11 janv. 2024 à 19:46, David Malcolm <dmalcolm@redhat.com> a écrit :
>
> On Thu, 2024-01-11 at 01:00 +0100, Guillaume Gomez wrote:
> > Hi David.
> >
> > Thanks for the review!
> >
> > > > +.. function::  void\
> > > > +               gcc_jit_lvalue_add_string_attribute
> > > > (gcc_jit_lvalue *variable,
> > > > +                                                    enum
> > > > gcc_jit_fn_attribute attribute,
> > >
> > > ^^
> > >
> > > This got out of sync with the declaration in the header file; it
> > > should
> > > be enum gcc_jit_variable_attribute attribute
> >
> > Indeed, good catch!
> >
> > > I took a brief look through the handler functions and with the
> > > above
> > > caveat I didn't see anything obviously wrong.  I'm going to assume
> > > this
> > > code is OK given that presumably you've been testing it within
> > > rustc,
> > > right?
> >
> > Both in rustc and in the JIT tests we added.
> >
> > [..snip...]
> >
> > I added all the missing `RETURN_IF_FAIL` you mentioned. None of the
> > arguments should be `NULL` so it was a mistake not to check it.
> >
> > [..snip...]
> >
> > I removed the tests comments as you mentioned.
> >
> > > Please update jit.dg/all-non-failing-tests.h for the new tests;
> > > it's
> > > meant to list all of the (non failing) tests alphabetically.
> >
> > It's not always correctly sorted. Might be worth sending a patch
> > after this
> > one gets merged to fix that.
> >
> > > I *think* all of the new tests aren't suitable to be run as part of
> > > a
> > > shared context (e.g. due to touching the optimization level or
> > > examining generated asm), so they should be listed in that header
> > > with
> > > comments explaining why.
> >
> > I added them with a comment on top of each of them.
> >
> > I joined the new patch version.
> >
> > Thanks again for the review!
>
> Thanks for the updated patch.  I noticed a few minor issues:
>
> > diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
> > index bb51f037b7e..b1aedc03787 100644
> > --- a/gcc/jit/docs/topics/types.rst
> > +++ b/gcc/jit/docs/topics/types.rst
> > @@ -553,3 +553,80 @@ Reflection API
> >     .. code-block:: c
> >
> >        #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
> > +
> > +.. function::  void\
> > +               gcc_jit_function_add_attribute (gcc_jit_function *func,
> > +                                               enum gcc_jit_fn_attribute attribute)
> > +
> > +     Add an attribute ``attribute`` to a function ``func``.
> > +
> > +     This is equivalent to the following code:
> > +
> > +  .. code-block:: c
> > +
> > +    __attribute__((always_inline))
> > +
> > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> > +   its presence using
> > +
> > +   .. code-block:: c
> > +
> > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > +
> > +.. function::  void\
> > +               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
> > +                                                      enum gcc_jit_fn_attribute attribute,
> > +                                                      const char *value)
> > +
> > +     Add a string attribute ``attribute`` with value ``value`` to a function
> > +     ``func``.
> > +
> > +     This is equivalent to the following code:
> > +
> > +  .. code-block:: c
> > +
> > +    __attribute__ ((alias ("xxx")))
> > +
> > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> > +   its presence using
> > +
> > +   .. code-block:: c
> > +
> > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > +
> > +.. function::  void\
> > +               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
> > +                                                             enum gcc_jit_fn_attribute attribute,
> > +                                                             const int *value,
> > +                                                             size_t length)
> > +
> > +     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
> > +     function ``func``. The integer values must be the same as you would write
> > +     them in a C code.
> > +
> > +     This is equivalent to the following code:
> > +
> > +  .. code-block:: c
> > +
> > +    __attribute__ ((nonnull (1, 2)))
> > +
> > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> > +   its presence using
> > +
> > +   .. code-block:: c
> > +
> > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > +
> > +.. function::  void\
> > +               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
> > +                                                    enum gcc_jit_variable_attribute attribute,
> > +                                                    const char *value)
> > +
> > +     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
> > +
> > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
> > +   its presence using
> > +
> > +   .. code-block:: c
> > +
> > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
>
> The above looks correct, but the patch adds the entrypoint descriptions
> to topics/types.rst, which seems like the wrong place.  The function-
> related ones should be in topics/functions.rst in the "Functions"
> section and the lvalue/variable one in topics/expression.rst after the
> "Global variables" section.
>
> > diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > index e762563f9bd..84001203352 100644
> > --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
>
> [...snip...]
>
> > @@ -313,7 +334,7 @@
> >  #undef create_code
> >  #undef verify_code
> >
> > -/* test-restrict.c: This can't be in the testcases array as it needs
> > +/* test-restrict-attribute.c: This can't be in the testcases array as it needs
> >     the `-O3` flag.  */
>
> test-restrict.c is a pre-existing testcase, so please don't delete its
> entry.
> BTW, the ChangeLog entry mentions adding test-restrict.c, but the patch
> doesn't add it, so that part of the proposed ChangeLog is wrong.
>
> Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > new file mode 100644
> > index 00000000000..8dc7ec5a34b
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > @@ -0,0 +1,54 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the cold
> > +   attribute affects the optimizations. */
>
> Please delete the above comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
> > new file mode 100644
> > index 00000000000..c06742d163f
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> > @@ -0,0 +1,134 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O3 to see that the const
> > +   attribute affects the optimizations. */
>
> Please delete the above comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O3".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > new file mode 100644
> > index 00000000000..a455b4493fd
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > @@ -0,0 +1,121 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the `noinline`
> > +   attribute affects the optimizations. */
>
> Please delete the above comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > new file mode 100644
> > index 00000000000..3306f890657
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > @@ -0,0 +1,94 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O2 to see that the nonnull
> > +   attribute affects the optimizations. */
>
> Please delete the above comment.
>
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O2".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > +}
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > new file mode 100644
> > index 00000000000..0c9ba1366e0
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > @@ -0,0 +1,134 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O3 to see that the pure
> > +   attribute affects the optimizations. */
>
> Please delete the above comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +  // Set "-O3".
> > +  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > +}
> > +
>
> [...snip...]
>
> > diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > new file mode 100644
> > index 00000000000..7d7444b624f
> > --- /dev/null
> > +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > @@ -0,0 +1,77 @@
> > +/* { dg-do compile { target x86_64-*-* } } */
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +
> > +#include "libgccjit.h"
> > +
> > +/* We don't want set_options() in harness.h to set -O3 to see that the restrict
> > +      attribute affects the optimizations. */
>
> Please delete this comment.
>
> > +#define TEST_ESCHEWS_SET_OPTIONS
> > +static void set_options (gcc_jit_context *ctxt, const char *argv0)
> > +{
> > +     // Set "-O3".
> > +     gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > +}
> > +
>
> [...snip...]
>
> Otherwise, looks good, assuming that the patch has been tested with the
> full jit testsuite.
>
> Thanks again
> Dave
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-11 21:40       ` Guillaume Gomez
@ 2024-01-11 22:38         ` David Malcolm
  2024-01-12 10:09           ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: David Malcolm @ 2024-01-11 22:38 UTC (permalink / raw)
  To: Guillaume Gomez; +Cc: jit, Antoni, gcc-patches

On Thu, 2024-01-11 at 22:40 +0100, Guillaume Gomez wrote:
> Hi David,
> 
> > The above looks correct, but the patch adds the entrypoint
> > descriptions
> > to topics/types.rst, which seems like the wrong place.  The
> > function-
> > related ones should be in topics/functions.rst in the "Functions"
> > section and the lvalue/variable one in topics/expression.rst after
> > the
> > "Global variables" section.
> 
> Ah indeed. Mix-up on my end. Fixed it.
> 
> > test-restrict.c is a pre-existing testcase, so please don't delete
> > its
> > entry.
> 
> Ah indeed, I went too quickly and thought it was a test I renamed...
> 
> > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > patch
> > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > 
> > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> 
> I messed up a bit, fixed it thanks to you. I didn't run the script in
> my last
> update but just did:
> 
> ```
> $ contrib/gcc-changelog/git_check_commit.py $(git log -1 --format=%h)
> Checking 3849ee2eadf0eeec2b0080a5142ced00be96a60d: OK
> ```
> 
> > Otherwise, looks good, assuming that the patch has been tested with
> > the
> > full jit testsuite.
> 
> When rebasing on upstream yesterday I discovered that two tests
> were not working anymore. For the first one, it was simply because of
> the changes in `dummy-frontend.cc`. For the second one
> (test-noinline-attribute.c), it was because the rules for inlining
> changed
> since we wrote this patch apparently (our fork is very late). Antoni
> discovered
> that we could just add a call to `asm` to prevent this from happening
> so I
> added it.
> 
> So yes, all jit tests are passing as expected. :)

Good.

It sounds like the patch you have locally is ready, but it has some
nontrivial changes compared to the last version you posted to the list.
Please post your latest version to the list.

Do you have push rights, or do you need me to push it for you?

Thanks
Dave

> 
> Le jeu. 11 janv. 2024 à 19:46, David Malcolm <dmalcolm@redhat.com> a
> écrit :
> > 
> > On Thu, 2024-01-11 at 01:00 +0100, Guillaume Gomez wrote:
> > > Hi David.
> > > 
> > > Thanks for the review!
> > > 
> > > > > +.. function::  void\
> > > > > +               gcc_jit_lvalue_add_string_attribute
> > > > > (gcc_jit_lvalue *variable,
> > > > > +                                                    enum
> > > > > gcc_jit_fn_attribute attribute,
> > > > 
> > > > ^^
> > > > 
> > > > This got out of sync with the declaration in the header file;
> > > > it
> > > > should
> > > > be enum gcc_jit_variable_attribute attribute
> > > 
> > > Indeed, good catch!
> > > 
> > > > I took a brief look through the handler functions and with the
> > > > above
> > > > caveat I didn't see anything obviously wrong.  I'm going to
> > > > assume
> > > > this
> > > > code is OK given that presumably you've been testing it within
> > > > rustc,
> > > > right?
> > > 
> > > Both in rustc and in the JIT tests we added.
> > > 
> > > [..snip...]
> > > 
> > > I added all the missing `RETURN_IF_FAIL` you mentioned. None of
> > > the
> > > arguments should be `NULL` so it was a mistake not to check it.
> > > 
> > > [..snip...]
> > > 
> > > I removed the tests comments as you mentioned.
> > > 
> > > > Please update jit.dg/all-non-failing-tests.h for the new tests;
> > > > it's
> > > > meant to list all of the (non failing) tests alphabetically.
> > > 
> > > It's not always correctly sorted. Might be worth sending a patch
> > > after this
> > > one gets merged to fix that.
> > > 
> > > > I *think* all of the new tests aren't suitable to be run as
> > > > part of
> > > > a
> > > > shared context (e.g. due to touching the optimization level or
> > > > examining generated asm), so they should be listed in that
> > > > header
> > > > with
> > > > comments explaining why.
> > > 
> > > I added them with a comment on top of each of them.
> > > 
> > > I joined the new patch version.
> > > 
> > > Thanks again for the review!
> > 
> > Thanks for the updated patch.  I noticed a few minor issues:
> > 
> > > diff --git a/gcc/jit/docs/topics/types.rst
> > > b/gcc/jit/docs/topics/types.rst
> > > index bb51f037b7e..b1aedc03787 100644
> > > --- a/gcc/jit/docs/topics/types.rst
> > > +++ b/gcc/jit/docs/topics/types.rst
> > > @@ -553,3 +553,80 @@ Reflection API
> > >     .. code-block:: c
> > > 
> > >        #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
> > > +
> > > +.. function::  void\
> > > +               gcc_jit_function_add_attribute (gcc_jit_function
> > > *func,
> > > +                                               enum
> > > gcc_jit_fn_attribute attribute)
> > > +
> > > +     Add an attribute ``attribute`` to a function ``func``.
> > > +
> > > +     This is equivalent to the following code:
> > > +
> > > +  .. code-block:: c
> > > +
> > > +    __attribute__((always_inline))
> > > +
> > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > test for
> > > +   its presence using
> > > +
> > > +   .. code-block:: c
> > > +
> > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > +
> > > +.. function::  void\
> > > +               gcc_jit_function_add_string_attribute
> > > (gcc_jit_function *func,
> > > +                                                      enum
> > > gcc_jit_fn_attribute attribute,
> > > +                                                      const char
> > > *value)
> > > +
> > > +     Add a string attribute ``attribute`` with value ``value``
> > > to a function
> > > +     ``func``.
> > > +
> > > +     This is equivalent to the following code:
> > > +
> > > +  .. code-block:: c
> > > +
> > > +    __attribute__ ((alias ("xxx")))
> > > +
> > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > test for
> > > +   its presence using
> > > +
> > > +   .. code-block:: c
> > > +
> > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > +
> > > +.. function::  void\
> > > +               gcc_jit_function_add_integer_array_attribute
> > > (gcc_jit_function *func,
> > > +                                                            
> > > enum gcc_jit_fn_attribute attribute,
> > > +                                                            
> > > const int *value,
> > > +                                                            
> > > size_t length)
> > > +
> > > +     Add an attribute ``attribute`` with ``length`` integer
> > > values ``value`` to a
> > > +     function ``func``. The integer values must be the same as
> > > you would write
> > > +     them in a C code.
> > > +
> > > +     This is equivalent to the following code:
> > > +
> > > +  .. code-block:: c
> > > +
> > > +    __attribute__ ((nonnull (1, 2)))
> > > +
> > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > test for
> > > +   its presence using
> > > +
> > > +   .. code-block:: c
> > > +
> > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > +
> > > +.. function::  void\
> > > +               gcc_jit_lvalue_add_string_attribute
> > > (gcc_jit_lvalue *variable,
> > > +                                                    enum
> > > gcc_jit_variable_attribute attribute,
> > > +                                                    const char
> > > *value)
> > > +
> > > +     Add an attribute ``attribute`` with value ``value`` to a
> > > variable ``variable``.
> > > +
> > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > test for
> > > +   its presence using
> > > +
> > > +   .. code-block:: c
> > > +
> > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > 
> > The above looks correct, but the patch adds the entrypoint
> > descriptions
> > to topics/types.rst, which seems like the wrong place.  The
> > function-
> > related ones should be in topics/functions.rst in the "Functions"
> > section and the lvalue/variable one in topics/expression.rst after
> > the
> > "Global variables" section.
> > 
> > > diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > index e762563f9bd..84001203352 100644
> > > --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > 
> > [...snip...]
> > 
> > > @@ -313,7 +334,7 @@
> > >  #undef create_code
> > >  #undef verify_code
> > > 
> > > -/* test-restrict.c: This can't be in the testcases array as it
> > > needs
> > > +/* test-restrict-attribute.c: This can't be in the testcases
> > > array as it needs
> > >     the `-O3` flag.  */
> > 
> > test-restrict.c is a pre-existing testcase, so please don't delete
> > its
> > entry.
> > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > patch
> > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > 
> > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > new file mode 100644
> > > index 00000000000..8dc7ec5a34b
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > @@ -0,0 +1,54 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > that the cold
> > > +   attribute affects the optimizations. */
> > 
> > Please delete the above comment.
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +  // Set "-O2".
> > > +  gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > +}
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c
> > > b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > new file mode 100644
> > > index 00000000000..c06742d163f
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > @@ -0,0 +1,134 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > that the const
> > > +   attribute affects the optimizations. */
> > 
> > Please delete the above comment.
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +  // Set "-O3".
> > > +  gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > +}
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > new file mode 100644
> > > index 00000000000..a455b4493fd
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > @@ -0,0 +1,121 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > that the `noinline`
> > > +   attribute affects the optimizations. */
> > 
> > Please delete the above comment.
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +  // Set "-O2".
> > > +  gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > +}
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > new file mode 100644
> > > index 00000000000..3306f890657
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > @@ -0,0 +1,94 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > that the nonnull
> > > +   attribute affects the optimizations. */
> > 
> > Please delete the above comment.
> > 
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +  // Set "-O2".
> > > +  gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > +}
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > new file mode 100644
> > > index 00000000000..0c9ba1366e0
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > @@ -0,0 +1,134 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > that the pure
> > > +   attribute affects the optimizations. */
> > 
> > Please delete the above comment.
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +  // Set "-O3".
> > > +  gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > +}
> > > +
> > 
> > [...snip...]
> > 
> > > diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > new file mode 100644
> > > index 00000000000..7d7444b624f
> > > --- /dev/null
> > > +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > @@ -0,0 +1,77 @@
> > > +/* { dg-do compile { target x86_64-*-* } } */
> > > +
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +
> > > +#include "libgccjit.h"
> > > +
> > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > that the restrict
> > > +      attribute affects the optimizations. */
> > 
> > Please delete this comment.
> > 
> > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > +static void set_options (gcc_jit_context *ctxt, const char
> > > *argv0)
> > > +{
> > > +     // Set "-O3".
> > > +     gcc_jit_context_set_int_option(ctxt,
> > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > +}
> > > +
> > 
> > [...snip...]
> > 
> > Otherwise, looks good, assuming that the patch has been tested with
> > the
> > full jit testsuite.
> > 
> > Thanks again
> > Dave
> > 
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-11 22:38         ` David Malcolm
@ 2024-01-12 10:09           ` Guillaume Gomez
  2024-01-12 13:47             ` Guillaume Gomez
  0 siblings, 1 reply; 16+ messages in thread
From: Guillaume Gomez @ 2024-01-12 10:09 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit, Antoni, gcc-patches

> It sounds like the patch you have locally is ready, but it has some
> nontrivial changes compared to the last version you posted to the list.
> Please post your latest version to the list.

Sure!

This patch adds the support for attributes on functions and variables. It does
so by adding the following functions:

* gcc_jit_function_add_attribute
* gcc_jit_function_add_string_attribute
* gcc_jit_function_add_integer_array_attribute
* gcc_jit_lvalue_add_string_attribute

It adds the following types:

* gcc_jit_fn_attribute
* gcc_jit_variable_attribute

It adds tests to ensure that the attributes are correctly applied.

> Do you have push rights, or do you need me to push it for you?

I have push rights so I'll merge the patch myself. But thanks for offering to
do it.

Le jeu. 11 janv. 2024 à 23:38, David Malcolm <dmalcolm@redhat.com> a écrit :
>
> On Thu, 2024-01-11 at 22:40 +0100, Guillaume Gomez wrote:
> > Hi David,
> >
> > > The above looks correct, but the patch adds the entrypoint
> > > descriptions
> > > to topics/types.rst, which seems like the wrong place.  The
> > > function-
> > > related ones should be in topics/functions.rst in the "Functions"
> > > section and the lvalue/variable one in topics/expression.rst after
> > > the
> > > "Global variables" section.
> >
> > Ah indeed. Mix-up on my end. Fixed it.
> >
> > > test-restrict.c is a pre-existing testcase, so please don't delete
> > > its
> > > entry.
> >
> > Ah indeed, I went too quickly and thought it was a test I renamed...
> >
> > > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > > patch
> > > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > >
> > > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> >
> > I messed up a bit, fixed it thanks to you. I didn't run the script in
> > my last
> > update but just did:
> >
> > ```
> > $ contrib/gcc-changelog/git_check_commit.py $(git log -1 --format=%h)
> > Checking 3849ee2eadf0eeec2b0080a5142ced00be96a60d: OK
> > ```
> >
> > > Otherwise, looks good, assuming that the patch has been tested with
> > > the
> > > full jit testsuite.
> >
> > When rebasing on upstream yesterday I discovered that two tests
> > were not working anymore. For the first one, it was simply because of
> > the changes in `dummy-frontend.cc`. For the second one
> > (test-noinline-attribute.c), it was because the rules for inlining
> > changed
> > since we wrote this patch apparently (our fork is very late). Antoni
> > discovered
> > that we could just add a call to `asm` to prevent this from happening
> > so I
> > added it.
> >
> > So yes, all jit tests are passing as expected. :)
>
> Good.
>
> It sounds like the patch you have locally is ready, but it has some
> nontrivial changes compared to the last version you posted to the list.
> Please post your latest version to the list.
>
> Do you have push rights, or do you need me to push it for you?
>
> Thanks
> Dave
>
> >
> > Le jeu. 11 janv. 2024 à 19:46, David Malcolm <dmalcolm@redhat.com> a
> > écrit :
> > >
> > > On Thu, 2024-01-11 at 01:00 +0100, Guillaume Gomez wrote:
> > > > Hi David.
> > > >
> > > > Thanks for the review!
> > > >
> > > > > > +.. function::  void\
> > > > > > +               gcc_jit_lvalue_add_string_attribute
> > > > > > (gcc_jit_lvalue *variable,
> > > > > > +                                                    enum
> > > > > > gcc_jit_fn_attribute attribute,
> > > > >
> > > > > ^^
> > > > >
> > > > > This got out of sync with the declaration in the header file;
> > > > > it
> > > > > should
> > > > > be enum gcc_jit_variable_attribute attribute
> > > >
> > > > Indeed, good catch!
> > > >
> > > > > I took a brief look through the handler functions and with the
> > > > > above
> > > > > caveat I didn't see anything obviously wrong.  I'm going to
> > > > > assume
> > > > > this
> > > > > code is OK given that presumably you've been testing it within
> > > > > rustc,
> > > > > right?
> > > >
> > > > Both in rustc and in the JIT tests we added.
> > > >
> > > > [..snip...]
> > > >
> > > > I added all the missing `RETURN_IF_FAIL` you mentioned. None of
> > > > the
> > > > arguments should be `NULL` so it was a mistake not to check it.
> > > >
> > > > [..snip...]
> > > >
> > > > I removed the tests comments as you mentioned.
> > > >
> > > > > Please update jit.dg/all-non-failing-tests.h for the new tests;
> > > > > it's
> > > > > meant to list all of the (non failing) tests alphabetically.
> > > >
> > > > It's not always correctly sorted. Might be worth sending a patch
> > > > after this
> > > > one gets merged to fix that.
> > > >
> > > > > I *think* all of the new tests aren't suitable to be run as
> > > > > part of
> > > > > a
> > > > > shared context (e.g. due to touching the optimization level or
> > > > > examining generated asm), so they should be listed in that
> > > > > header
> > > > > with
> > > > > comments explaining why.
> > > >
> > > > I added them with a comment on top of each of them.
> > > >
> > > > I joined the new patch version.
> > > >
> > > > Thanks again for the review!
> > >
> > > Thanks for the updated patch.  I noticed a few minor issues:
> > >
> > > > diff --git a/gcc/jit/docs/topics/types.rst
> > > > b/gcc/jit/docs/topics/types.rst
> > > > index bb51f037b7e..b1aedc03787 100644
> > > > --- a/gcc/jit/docs/topics/types.rst
> > > > +++ b/gcc/jit/docs/topics/types.rst
> > > > @@ -553,3 +553,80 @@ Reflection API
> > > >     .. code-block:: c
> > > >
> > > >        #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
> > > > +
> > > > +.. function::  void\
> > > > +               gcc_jit_function_add_attribute (gcc_jit_function
> > > > *func,
> > > > +                                               enum
> > > > gcc_jit_fn_attribute attribute)
> > > > +
> > > > +     Add an attribute ``attribute`` to a function ``func``.
> > > > +
> > > > +     This is equivalent to the following code:
> > > > +
> > > > +  .. code-block:: c
> > > > +
> > > > +    __attribute__((always_inline))
> > > > +
> > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > test for
> > > > +   its presence using
> > > > +
> > > > +   .. code-block:: c
> > > > +
> > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > +
> > > > +.. function::  void\
> > > > +               gcc_jit_function_add_string_attribute
> > > > (gcc_jit_function *func,
> > > > +                                                      enum
> > > > gcc_jit_fn_attribute attribute,
> > > > +                                                      const char
> > > > *value)
> > > > +
> > > > +     Add a string attribute ``attribute`` with value ``value``
> > > > to a function
> > > > +     ``func``.
> > > > +
> > > > +     This is equivalent to the following code:
> > > > +
> > > > +  .. code-block:: c
> > > > +
> > > > +    __attribute__ ((alias ("xxx")))
> > > > +
> > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > test for
> > > > +   its presence using
> > > > +
> > > > +   .. code-block:: c
> > > > +
> > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > +
> > > > +.. function::  void\
> > > > +               gcc_jit_function_add_integer_array_attribute
> > > > (gcc_jit_function *func,
> > > > +
> > > > enum gcc_jit_fn_attribute attribute,
> > > > +
> > > > const int *value,
> > > > +
> > > > size_t length)
> > > > +
> > > > +     Add an attribute ``attribute`` with ``length`` integer
> > > > values ``value`` to a
> > > > +     function ``func``. The integer values must be the same as
> > > > you would write
> > > > +     them in a C code.
> > > > +
> > > > +     This is equivalent to the following code:
> > > > +
> > > > +  .. code-block:: c
> > > > +
> > > > +    __attribute__ ((nonnull (1, 2)))
> > > > +
> > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > test for
> > > > +   its presence using
> > > > +
> > > > +   .. code-block:: c
> > > > +
> > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > +
> > > > +.. function::  void\
> > > > +               gcc_jit_lvalue_add_string_attribute
> > > > (gcc_jit_lvalue *variable,
> > > > +                                                    enum
> > > > gcc_jit_variable_attribute attribute,
> > > > +                                                    const char
> > > > *value)
> > > > +
> > > > +     Add an attribute ``attribute`` with value ``value`` to a
> > > > variable ``variable``.
> > > > +
> > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > test for
> > > > +   its presence using
> > > > +
> > > > +   .. code-block:: c
> > > > +
> > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > >
> > > The above looks correct, but the patch adds the entrypoint
> > > descriptions
> > > to topics/types.rst, which seems like the wrong place.  The
> > > function-
> > > related ones should be in topics/functions.rst in the "Functions"
> > > section and the lvalue/variable one in topics/expression.rst after
> > > the
> > > "Global variables" section.
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > > index e762563f9bd..84001203352 100644
> > > > --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > > +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > >
> > > [...snip...]
> > >
> > > > @@ -313,7 +334,7 @@
> > > >  #undef create_code
> > > >  #undef verify_code
> > > >
> > > > -/* test-restrict.c: This can't be in the testcases array as it
> > > > needs
> > > > +/* test-restrict-attribute.c: This can't be in the testcases
> > > > array as it needs
> > > >     the `-O3` flag.  */
> > >
> > > test-restrict.c is a pre-existing testcase, so please don't delete
> > > its
> > > entry.
> > > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > > patch
> > > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > >
> > > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..8dc7ec5a34b
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > @@ -0,0 +1,54 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > that the cold
> > > > +   attribute affects the optimizations. */
> > >
> > > Please delete the above comment.
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +  // Set "-O2".
> > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > +}
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..c06742d163f
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > @@ -0,0 +1,134 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > that the const
> > > > +   attribute affects the optimizations. */
> > >
> > > Please delete the above comment.
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +  // Set "-O3".
> > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > +}
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..a455b4493fd
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > @@ -0,0 +1,121 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > that the `noinline`
> > > > +   attribute affects the optimizations. */
> > >
> > > Please delete the above comment.
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +  // Set "-O2".
> > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > +}
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..3306f890657
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > @@ -0,0 +1,94 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > that the nonnull
> > > > +   attribute affects the optimizations. */
> > >
> > > Please delete the above comment.
> > >
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +  // Set "-O2".
> > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > +}
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..0c9ba1366e0
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > @@ -0,0 +1,134 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > that the pure
> > > > +   attribute affects the optimizations. */
> > >
> > > Please delete the above comment.
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +  // Set "-O3".
> > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > +}
> > > > +
> > >
> > > [...snip...]
> > >
> > > > diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > new file mode 100644
> > > > index 00000000000..7d7444b624f
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > @@ -0,0 +1,77 @@
> > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > +
> > > > +#include <stdlib.h>
> > > > +#include <stdio.h>
> > > > +
> > > > +#include "libgccjit.h"
> > > > +
> > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > that the restrict
> > > > +      attribute affects the optimizations. */
> > >
> > > Please delete this comment.
> > >
> > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > *argv0)
> > > > +{
> > > > +     // Set "-O3".
> > > > +     gcc_jit_context_set_int_option(ctxt,
> > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > +}
> > > > +
> > >
> > > [...snip...]
> > >
> > > Otherwise, looks good, assuming that the patch has been tested with
> > > the
> > > full jit testsuite.
> > >
> > > Thanks again
> > > Dave
> > >
> >
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add support for function attributes and variable attributes
  2024-01-12 10:09           ` Guillaume Gomez
@ 2024-01-12 13:47             ` Guillaume Gomez
  0 siblings, 0 replies; 16+ messages in thread
From: Guillaume Gomez @ 2024-01-12 13:47 UTC (permalink / raw)
  To: David Malcolm; +Cc: jit, Antoni, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 17709 bytes --]

Just realized that you were asking for the patch I forgot to join...

Here it is.

Le ven. 12 janv. 2024 à 11:09, Guillaume Gomez
<guillaume1.gomez@gmail.com> a écrit :
>
> > It sounds like the patch you have locally is ready, but it has some
> > nontrivial changes compared to the last version you posted to the list.
> > Please post your latest version to the list.
>
> Sure!
>
> This patch adds the support for attributes on functions and variables. It does
> so by adding the following functions:
>
> * gcc_jit_function_add_attribute
> * gcc_jit_function_add_string_attribute
> * gcc_jit_function_add_integer_array_attribute
> * gcc_jit_lvalue_add_string_attribute
>
> It adds the following types:
>
> * gcc_jit_fn_attribute
> * gcc_jit_variable_attribute
>
> It adds tests to ensure that the attributes are correctly applied.
>
> > Do you have push rights, or do you need me to push it for you?
>
> I have push rights so I'll merge the patch myself. But thanks for offering to
> do it.
>
> Le jeu. 11 janv. 2024 à 23:38, David Malcolm <dmalcolm@redhat.com> a écrit :
> >
> > On Thu, 2024-01-11 at 22:40 +0100, Guillaume Gomez wrote:
> > > Hi David,
> > >
> > > > The above looks correct, but the patch adds the entrypoint
> > > > descriptions
> > > > to topics/types.rst, which seems like the wrong place.  The
> > > > function-
> > > > related ones should be in topics/functions.rst in the "Functions"
> > > > section and the lvalue/variable one in topics/expression.rst after
> > > > the
> > > > "Global variables" section.
> > >
> > > Ah indeed. Mix-up on my end. Fixed it.
> > >
> > > > test-restrict.c is a pre-existing testcase, so please don't delete
> > > > its
> > > > entry.
> > >
> > > Ah indeed, I went too quickly and thought it was a test I renamed...
> > >
> > > > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > > > patch
> > > > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > > >
> > > > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> > >
> > > I messed up a bit, fixed it thanks to you. I didn't run the script in
> > > my last
> > > update but just did:
> > >
> > > ```
> > > $ contrib/gcc-changelog/git_check_commit.py $(git log -1 --format=%h)
> > > Checking 3849ee2eadf0eeec2b0080a5142ced00be96a60d: OK
> > > ```
> > >
> > > > Otherwise, looks good, assuming that the patch has been tested with
> > > > the
> > > > full jit testsuite.
> > >
> > > When rebasing on upstream yesterday I discovered that two tests
> > > were not working anymore. For the first one, it was simply because of
> > > the changes in `dummy-frontend.cc`. For the second one
> > > (test-noinline-attribute.c), it was because the rules for inlining
> > > changed
> > > since we wrote this patch apparently (our fork is very late). Antoni
> > > discovered
> > > that we could just add a call to `asm` to prevent this from happening
> > > so I
> > > added it.
> > >
> > > So yes, all jit tests are passing as expected. :)
> >
> > Good.
> >
> > It sounds like the patch you have locally is ready, but it has some
> > nontrivial changes compared to the last version you posted to the list.
> > Please post your latest version to the list.
> >
> > Do you have push rights, or do you need me to push it for you?
> >
> > Thanks
> > Dave
> >
> > >
> > > Le jeu. 11 janv. 2024 à 19:46, David Malcolm <dmalcolm@redhat.com> a
> > > écrit :
> > > >
> > > > On Thu, 2024-01-11 at 01:00 +0100, Guillaume Gomez wrote:
> > > > > Hi David.
> > > > >
> > > > > Thanks for the review!
> > > > >
> > > > > > > +.. function::  void\
> > > > > > > +               gcc_jit_lvalue_add_string_attribute
> > > > > > > (gcc_jit_lvalue *variable,
> > > > > > > +                                                    enum
> > > > > > > gcc_jit_fn_attribute attribute,
> > > > > >
> > > > > > ^^
> > > > > >
> > > > > > This got out of sync with the declaration in the header file;
> > > > > > it
> > > > > > should
> > > > > > be enum gcc_jit_variable_attribute attribute
> > > > >
> > > > > Indeed, good catch!
> > > > >
> > > > > > I took a brief look through the handler functions and with the
> > > > > > above
> > > > > > caveat I didn't see anything obviously wrong.  I'm going to
> > > > > > assume
> > > > > > this
> > > > > > code is OK given that presumably you've been testing it within
> > > > > > rustc,
> > > > > > right?
> > > > >
> > > > > Both in rustc and in the JIT tests we added.
> > > > >
> > > > > [..snip...]
> > > > >
> > > > > I added all the missing `RETURN_IF_FAIL` you mentioned. None of
> > > > > the
> > > > > arguments should be `NULL` so it was a mistake not to check it.
> > > > >
> > > > > [..snip...]
> > > > >
> > > > > I removed the tests comments as you mentioned.
> > > > >
> > > > > > Please update jit.dg/all-non-failing-tests.h for the new tests;
> > > > > > it's
> > > > > > meant to list all of the (non failing) tests alphabetically.
> > > > >
> > > > > It's not always correctly sorted. Might be worth sending a patch
> > > > > after this
> > > > > one gets merged to fix that.
> > > > >
> > > > > > I *think* all of the new tests aren't suitable to be run as
> > > > > > part of
> > > > > > a
> > > > > > shared context (e.g. due to touching the optimization level or
> > > > > > examining generated asm), so they should be listed in that
> > > > > > header
> > > > > > with
> > > > > > comments explaining why.
> > > > >
> > > > > I added them with a comment on top of each of them.
> > > > >
> > > > > I joined the new patch version.
> > > > >
> > > > > Thanks again for the review!
> > > >
> > > > Thanks for the updated patch.  I noticed a few minor issues:
> > > >
> > > > > diff --git a/gcc/jit/docs/topics/types.rst
> > > > > b/gcc/jit/docs/topics/types.rst
> > > > > index bb51f037b7e..b1aedc03787 100644
> > > > > --- a/gcc/jit/docs/topics/types.rst
> > > > > +++ b/gcc/jit/docs/topics/types.rst
> > > > > @@ -553,3 +553,80 @@ Reflection API
> > > > >     .. code-block:: c
> > > > >
> > > > >        #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_restrict
> > > > > +
> > > > > +.. function::  void\
> > > > > +               gcc_jit_function_add_attribute (gcc_jit_function
> > > > > *func,
> > > > > +                                               enum
> > > > > gcc_jit_fn_attribute attribute)
> > > > > +
> > > > > +     Add an attribute ``attribute`` to a function ``func``.
> > > > > +
> > > > > +     This is equivalent to the following code:
> > > > > +
> > > > > +  .. code-block:: c
> > > > > +
> > > > > +    __attribute__((always_inline))
> > > > > +
> > > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > > test for
> > > > > +   its presence using
> > > > > +
> > > > > +   .. code-block:: c
> > > > > +
> > > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > > +
> > > > > +.. function::  void\
> > > > > +               gcc_jit_function_add_string_attribute
> > > > > (gcc_jit_function *func,
> > > > > +                                                      enum
> > > > > gcc_jit_fn_attribute attribute,
> > > > > +                                                      const char
> > > > > *value)
> > > > > +
> > > > > +     Add a string attribute ``attribute`` with value ``value``
> > > > > to a function
> > > > > +     ``func``.
> > > > > +
> > > > > +     This is equivalent to the following code:
> > > > > +
> > > > > +  .. code-block:: c
> > > > > +
> > > > > +    __attribute__ ((alias ("xxx")))
> > > > > +
> > > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > > test for
> > > > > +   its presence using
> > > > > +
> > > > > +   .. code-block:: c
> > > > > +
> > > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > > +
> > > > > +.. function::  void\
> > > > > +               gcc_jit_function_add_integer_array_attribute
> > > > > (gcc_jit_function *func,
> > > > > +
> > > > > enum gcc_jit_fn_attribute attribute,
> > > > > +
> > > > > const int *value,
> > > > > +
> > > > > size_t length)
> > > > > +
> > > > > +     Add an attribute ``attribute`` with ``length`` integer
> > > > > values ``value`` to a
> > > > > +     function ``func``. The integer values must be the same as
> > > > > you would write
> > > > > +     them in a C code.
> > > > > +
> > > > > +     This is equivalent to the following code:
> > > > > +
> > > > > +  .. code-block:: c
> > > > > +
> > > > > +    __attribute__ ((nonnull (1, 2)))
> > > > > +
> > > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > > test for
> > > > > +   its presence using
> > > > > +
> > > > > +   .. code-block:: c
> > > > > +
> > > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > > > +
> > > > > +.. function::  void\
> > > > > +               gcc_jit_lvalue_add_string_attribute
> > > > > (gcc_jit_lvalue *variable,
> > > > > +                                                    enum
> > > > > gcc_jit_variable_attribute attribute,
> > > > > +                                                    const char
> > > > > *value)
> > > > > +
> > > > > +     Add an attribute ``attribute`` with value ``value`` to a
> > > > > variable ``variable``.
> > > > > +
> > > > > +   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can
> > > > > test for
> > > > > +   its presence using
> > > > > +
> > > > > +   .. code-block:: c
> > > > > +
> > > > > +      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
> > > >
> > > > The above looks correct, but the patch adds the entrypoint
> > > > descriptions
> > > > to topics/types.rst, which seems like the wrong place.  The
> > > > function-
> > > > related ones should be in topics/functions.rst in the "Functions"
> > > > section and the lvalue/variable one in topics/expression.rst after
> > > > the
> > > > "Global variables" section.
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > > b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > > > index e762563f9bd..84001203352 100644
> > > > > --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > > > +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
> > > >
> > > > [...snip...]
> > > >
> > > > > @@ -313,7 +334,7 @@
> > > > >  #undef create_code
> > > > >  #undef verify_code
> > > > >
> > > > > -/* test-restrict.c: This can't be in the testcases array as it
> > > > > needs
> > > > > +/* test-restrict-attribute.c: This can't be in the testcases
> > > > > array as it needs
> > > > >     the `-O3` flag.  */
> > > >
> > > > test-restrict.c is a pre-existing testcase, so please don't delete
> > > > its
> > > > entry.
> > > > BTW, the ChangeLog entry mentions adding test-restrict.c, but the
> > > > patch
> > > > doesn't add it, so that part of the proposed ChangeLog is wrong.
> > > >
> > > > Does the patch pass ./contrib/gcc-changelog/git_check_commit.py ?
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..8dc7ec5a34b
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
> > > > > @@ -0,0 +1,54 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > > that the cold
> > > > > +   attribute affects the optimizations. */
> > > >
> > > > Please delete the above comment.
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +  // Set "-O2".
> > > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > > +}
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..c06742d163f
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-const-attribute.c
> > > > > @@ -0,0 +1,134 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > > that the const
> > > > > +   attribute affects the optimizations. */
> > > >
> > > > Please delete the above comment.
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +  // Set "-O3".
> > > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > > +}
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..a455b4493fd
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
> > > > > @@ -0,0 +1,121 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > > that the `noinline`
> > > > > +   attribute affects the optimizations. */
> > > >
> > > > Please delete the above comment.
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +  // Set "-O2".
> > > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > > +}
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..3306f890657
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
> > > > > @@ -0,0 +1,94 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O2 to see
> > > > > that the nonnull
> > > > > +   attribute affects the optimizations. */
> > > >
> > > > Please delete the above comment.
> > > >
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +  // Set "-O2".
> > > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
> > > > > +}
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..0c9ba1366e0
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
> > > > > @@ -0,0 +1,134 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > > that the pure
> > > > > +   attribute affects the optimizations. */
> > > >
> > > > Please delete the above comment.
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +  // Set "-O3".
> > > > > +  gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > > +}
> > > > > +
> > > >
> > > > [...snip...]
> > > >
> > > > > diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > > b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > > new file mode 100644
> > > > > index 00000000000..7d7444b624f
> > > > > --- /dev/null
> > > > > +++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
> > > > > @@ -0,0 +1,77 @@
> > > > > +/* { dg-do compile { target x86_64-*-* } } */
> > > > > +
> > > > > +#include <stdlib.h>
> > > > > +#include <stdio.h>
> > > > > +
> > > > > +#include "libgccjit.h"
> > > > > +
> > > > > +/* We don't want set_options() in harness.h to set -O3 to see
> > > > > that the restrict
> > > > > +      attribute affects the optimizations. */
> > > >
> > > > Please delete this comment.
> > > >
> > > > > +#define TEST_ESCHEWS_SET_OPTIONS
> > > > > +static void set_options (gcc_jit_context *ctxt, const char
> > > > > *argv0)
> > > > > +{
> > > > > +     // Set "-O3".
> > > > > +     gcc_jit_context_set_int_option(ctxt,
> > > > > GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
> > > > > +}
> > > > > +
> > > >
> > > > [...snip...]
> > > >
> > > > Otherwise, looks good, assuming that the patch has been tested with
> > > > the
> > > > full jit testsuite.
> > > >
> > > > Thanks again
> > > > Dave
> > > >
> > >
> >

[-- Attachment #2: 0001-PATCH-libgccjit-Add-support-for-function-attributes-.patch --]
[-- Type: text/x-patch, Size: 95130 bytes --]

From 3849ee2eadf0eeec2b0080a5142ced00be96a60d Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Wed, 10 Jan 2024 15:23:37 +0100
Subject: [PATCH] [PATCH] libgccjit: Add support for function attributes and
 variable attributes.

gcc/jit/ChangeLog:

	* dummy-frontend.cc (handle_alias_attribute): New function.
	(handle_always_inline_attribute): New function.
	(handle_cold_attribute): New function.
	(handle_fnspec_attribute): New function.
	(handle_format_arg_attribute): New function.
	(handle_format_attribute): New function.
	(handle_noinline_attribute): New function.
	(handle_target_attribute): New function.
	(handle_used_attribute): New function.
	(handle_visibility_attribute): New function.
	(handle_weak_attribute): New function.
	(handle_alias_ifunc_attribute): New function.
	* jit-playback.cc (fn_attribute_to_string): New function.
	(variable_attribute_to_string): New function.
	(global_new_decl): Add attributes support.
	(set_variable_attribute): New function.
	(new_global): Add attributes support.
	(new_global_initialized): Add attributes support.
	(new_local): Add attributes support.
	* jit-playback.h (fn_attribute_to_string): New function.
	(set_variable_attribute): New function.
	* jit-recording.cc (recording::lvalue::add_attribute): New function.
	(recording::function::function): New function.
	(recording::function::write_to_dump): Add attributes support.
	(recording::function::add_attribute): New function.
	(recording::function::add_string_attribute): New function.
	(recording::function::add_integer_array_attribute): New function.
	(recording::global::replay_into): Add attributes support.
	(recording::local::replay_into): Add attributes support.
	* jit-recording.h: Add attributes support.
	* libgccjit.cc (gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(gcc_jit_lvalue_add_attribute): New function.
	* libgccjit.h (enum gcc_jit_fn_attribute): New enum.
	(gcc_jit_function_add_attribute): New function.
	(gcc_jit_function_add_string_attribute): New function.
	(gcc_jit_function_add_integer_array_attribute): New function.
	(enum gcc_jit_variable_attribute): New function.
	(gcc_jit_lvalue_add_string_attribute): New function.
	* libgccjit.map: Declare new functions.

gcc/testsuite/ChangeLog:

	* jit.dg/all-non-failing-tests.h: Add new attributes tests.
	* jit.dg/jit.exp: Add `jit-verify-assembler-output-not` test command.
	* jit.dg/test-restrict-attribute.c: New test.
	* jit.dg/test-alias-attribute.c: New test.
	* jit.dg/test-always_inline-attribute.c: New test.
	* jit.dg/test-cold-attribute.c: New test.
	* jit.dg/test-const-attribute.c: New test.
	* jit.dg/test-noinline-attribute.c: New test.
	* jit.dg/test-nonnull-attribute.c: New test.
	* jit.dg/test-pure-attribute.c: New test.
	* jit.dg/test-used-attribute.c: New test.
	* jit.dg/test-variable-attribute.c: New test.
	* jit.dg/test-weak-attribute.c: New test.

gcc/jit/ChangeLog:
	* docs/topics/compatibility.rst: Add documentation for LIBGCCJIT_ABI_26.
	* docs/topics/functions.rst: Add documentation for new functions.
	* docs/topics/expressions.rst: Add documentation for new functions.

Co-authored-by: Antoni Boucher <bouanto@zoho.com>
Signed-off-by: Guillaume Gomez <guillaume1.gomez@gmail.com>
---
 gcc/jit/docs/topics/compatibility.rst         |  12 +
 gcc/jit/docs/topics/expressions.rst           |  17 +
 gcc/jit/docs/topics/functions.rst             |  63 +++
 gcc/jit/dummy-frontend.cc                     | 512 ++++++++++++++++--
 gcc/jit/jit-playback.cc                       | 169 +++++-
 gcc/jit/jit-playback.h                        |  37 +-
 gcc/jit/jit-recording.cc                      | 166 +++++-
 gcc/jit/jit-recording.h                       |  22 +-
 gcc/jit/libgccjit.cc                          |  67 +++
 gcc/jit/libgccjit.h                           |  55 ++
 gcc/jit/libgccjit.map                         |   8 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h  |  33 ++
 gcc/testsuite/jit.dg/jit.exp                  |  33 ++
 gcc/testsuite/jit.dg/test-alias-attribute.c   |  50 ++
 .../jit.dg/test-always_inline-attribute.c     | 153 ++++++
 gcc/testsuite/jit.dg/test-cold-attribute.c    |  52 ++
 gcc/testsuite/jit.dg/test-const-attribute.c   | 132 +++++
 .../jit.dg/test-noinline-attribute.c          | 119 ++++
 gcc/testsuite/jit.dg/test-nonnull-attribute.c |  92 ++++
 gcc/testsuite/jit.dg/test-pure-attribute.c    | 132 +++++
 .../jit.dg/test-restrict-attribute.c          |  75 +++
 gcc/testsuite/jit.dg/test-used-attribute.c    | 112 ++++
 .../jit.dg/test-variable-attribute.c          |  46 ++
 gcc/testsuite/jit.dg/test-weak-attribute.c    |  41 ++
 24 files changed, 2125 insertions(+), 73 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-alias-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-always_inline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-cold-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-const-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-noinline-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-nonnull-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-pure-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-restrict-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-used-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-variable-attribute.c
 create mode 100644 gcc/testsuite/jit.dg/test-weak-attribute.c

diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 704de1b51ed..cbf5b414d8c 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -378,3 +378,15 @@ alignment of a variable:
 --------------------
 ``LIBGCCJIT_ABI_25`` covers the addition of
 :func:`gcc_jit_type_get_restrict`
+
+.. _LIBGCCJIT_ABI_26:
+
+``LIBGCCJIT_ABI_26``
+--------------------
+``LIBGCCJIT_ABI_26`` covers the addition of functions to set attributes
+on functions and variables:
+
+  * :func:`gcc_jit_function_add_attribute`
+  * :func:`gcc_jit_function_add_string_attribute`
+  * :func:`gcc_jit_function_add_integer_array_attribute`
+  * :func:`gcc_jit_lvalue_add_string_attribute`
diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst
index cee6a308582..35ee05ca597 100644
--- a/gcc/jit/docs/topics/expressions.rst
+++ b/gcc/jit/docs/topics/expressions.rst
@@ -944,6 +944,23 @@ Global variables
 
       #ifdef LIBGCCJIT_HAVE_CTORS
 
+Variables
+*********
+
+.. function::  void\
+               gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+                                                    enum gcc_jit_variable_attribute attribute,
+                                                    const char *value)
+
+     Add an attribute ``attribute`` with value ``value`` to a variable ``variable``.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
 Working with pointers, structs and unions
 -----------------------------------------
 
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index 127bc0e56cf..804605ea939 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -197,6 +197,69 @@ Functions
 
    .. type:: gcc_jit_case
 
+.. function::  void\
+               gcc_jit_function_add_attribute (gcc_jit_function *func,
+                                               enum gcc_jit_fn_attribute attribute)
+
+     Add an attribute ``attribute`` to a function ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__((always_inline))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+                                                      enum gcc_jit_fn_attribute attribute,
+                                                      const char *value)
+
+     Add a string attribute ``attribute`` with value ``value`` to a function
+     ``func``.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((alias ("xxx")))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
+.. function::  void\
+               gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+                                                             enum gcc_jit_fn_attribute attribute,
+                                                             const int *value,
+                                                             size_t length)
+
+     Add an attribute ``attribute`` with ``length`` integer values ``value`` to a
+     function ``func``. The integer values must be the same as you would write
+     them in a C code.
+
+     This is equivalent to the following code:
+
+  .. code-block:: c
+
+    __attribute__ ((nonnull (1, 2)))
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_26`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+
 Blocks
 ------
 .. type:: gcc_jit_block
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 211f1be98fa..dbeeacd17a8 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -29,30 +29,42 @@ along with GCC; see the file COPYING3.  If not see
 #include "options.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "cgraph.h"
+#include "target.h"
 
 #include <mpfr.h>
 
 /* Attribute handling.  */
 
-static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
-static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
+static tree handle_always_inline_attribute (tree *, tree, tree, int,
+					    bool *);
+static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree handle_const_attribute (tree *, tree, tree, int, bool *);
+static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree handle_format_attribute (tree *, tree, tree, int, bool *);
+static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
-static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
+static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 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_sentinel_attribute (tree *, tree, tree, int, bool *);
-static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
-static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
-static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
 static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
 						       int, bool *);
-static tree ignore_attribute (tree *, tree, tree, int, bool *);
+static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
+static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
+static tree handle_target_attribute (tree *, tree, tree, int, bool *);
+static tree handle_transaction_pure_attribute (tree *, tree, tree, int, bool *);
+static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
+static tree handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree handle_visibility_attribute (tree *, tree, tree, int,
+					 bool *);
+static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
 
-static tree handle_format_attribute (tree *, tree, tree, int, bool *);
-static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
-static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+static tree ignore_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
@@ -61,7 +73,6 @@ static tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
 /* Define attributes that are mutually exclusive with one another.  */
 static const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
 {
-  ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("alloc_align", true, true, true),
   ATTR_EXCL ("alloc_size", true, true, true),
   ATTR_EXCL ("const", true, true, true),
@@ -78,57 +89,117 @@ static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
   ATTR_EXCL (NULL, false, false, false),
 };
 
+/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc.  */
+static const struct attribute_spec::exclusions attr_alloc_exclusions[] =
+{
+  ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("noreturn", true, true, true),
+  ATTR_EXCL ("pure", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
 {
   ATTR_EXCL ("const", true, true, true),
+  ATTR_EXCL ("alloc_align", true, true, true),
+  ATTR_EXCL ("alloc_size", true, true, true),
+  ATTR_EXCL ("malloc", true, true, true),
   ATTR_EXCL ("noreturn", true, true, true),
   ATTR_EXCL ("pure", true, true, true),
   ATTR_EXCL (NULL, false, false, false)
 };
 
+static const struct attribute_spec::exclusions attr_always_inline_exclusions[] =
+{
+  ATTR_EXCL ("noinline", true, true, true),
+  ATTR_EXCL ("target_clones", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+extern const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
+{
+  ATTR_EXCL ("cold", true, true, true),
+  ATTR_EXCL ("hot", true, true, true),
+  ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_noinline_exclusions[] =
+{
+  ATTR_EXCL ("always_inline", true, true, true),
+  ATTR_EXCL ("gnu_inline", true, true, true),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_target_exclusions[] =
+{
+  ATTR_EXCL ("target_clones", TARGET_HAS_FMV_TARGET_ATTRIBUTE,
+             TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE),
+  ATTR_EXCL (NULL, false, false, false),
+};
+
 /* Table of machine-independent attributes supported in libgccjit.  */
 static const attribute_spec jit_gnu_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "noreturn",               0, 0, true,  false, false, false,
-			      handle_noreturn_attribute,
-			      attr_noreturn_exclusions },
-  { "leaf",		      0, 0, true,  false, false, false,
-			      handle_leaf_attribute, NULL },
+  { "alias",		      1, 1, true,  false, false, false,
+			      handle_alias_attribute, NULL },
+  { "always_inline",	      0, 0, true,  false, false, false,
+			      handle_always_inline_attribute,
+			      attr_always_inline_exclusions },
+  { "cold",		      0, 0, true,  false, false, false,
+			      handle_cold_attribute,
+			      attr_cold_hot_exclusions },
   /* The same comments as for noreturn attributes apply to const ones.  */
-  { "const",                  0, 0, true,  false, false, false,
+  { "const",		      0, 0, true,  false, false, false,
 			      handle_const_attribute,
 			      attr_const_pure_exclusions },
-  { "malloc",                 0, 0, true,  false, false, false,
-			      handle_malloc_attribute, NULL },
-  { "pure",                   0, 0, true,  false, false, false,
-			      handle_pure_attribute,
-			      attr_const_pure_exclusions },
-  { "no vops",                0, 0, true,  false, false, false,
+  { "fn spec",		      1, 1, false, true, true, false,
+			      handle_fnspec_attribute, NULL },
+
+  { "leaf",		      0, 0, true,  false, false, false,
+			      handle_leaf_attribute, NULL },
+  { "malloc",		      0, 0, true,  false, false, false,
+			      handle_malloc_attribute, attr_alloc_exclusions },
+  { "noreturn",		      0, 0, true,  false, false, false,
+			      handle_noreturn_attribute,
+			      attr_noreturn_exclusions },
+  { "no vops",		      0, 0, true,  false, false, false,
 			      handle_novops_attribute, NULL },
-  { "nonnull",                0, -1, false, true, true, false,
+  { "noinline",		      0, 0, true,  false, false, false,
+			      handle_noinline_attribute,
+			      attr_noinline_exclusions },
+  { "nonnull",		      0, -1, false, true, true, false,
 			      handle_nonnull_attribute, NULL },
-  { "nothrow",                0, 0, true,  false, false, false,
+  { "nothrow",		      0, 0, true,  false, false, false,
 			      handle_nothrow_attribute, NULL },
   { "patchable_function_entry", 1, 2, true, false, false, false,
 			      handle_patchable_function_entry_attribute,
 			      NULL },
-  { "returns_twice",          0, 0, true,  false, false, false,
+  { "pure",		      0, 0, true,  false, false, false,
+			      handle_pure_attribute,
+			      attr_const_pure_exclusions },
+  { "returns_twice",	      0, 0, true,  false, false, false,
 			      handle_returns_twice_attribute,
 			      attr_returns_twice_exclusions },
-  { "sentinel",               0, 1, false, true, true, false,
+  { "sentinel",		      0, 1, false, true, true, false,
 			      handle_sentinel_attribute, NULL },
-  { "type generic",           0, 0, false, true, true, false,
+  { "target",		      1, -1, true, false, false, false,
+			      handle_target_attribute, attr_target_exclusions },
+  { "type generic",	      0, 0, false, true, true, false,
 			      handle_type_generic_attribute, NULL },
-  { "fn spec",	 	      1, 1, false, true, true, false,
-			      handle_fnspec_attribute, NULL },
   { "transaction_pure",	      0, 0, false, true, true, false,
 			      handle_transaction_pure_attribute, NULL },
+  { "used",         0, 0, true,  false, false, false,
+            handle_used_attribute, NULL },
+  { "visibility",       1, 1, false, false, false, false,
+            handle_visibility_attribute, NULL },
+  { "weak",         0, 0, true,  false, false, false,
+            handle_weak_attribute, NULL },
   /* For internal use only.  The leading '*' both prevents its usage in
      source code and signals that it may be overridden by machine tables.  */
   { "*tm regparm",            0, 0, false, true, true, false,
-			      ignore_attribute, NULL }
+			      ignore_attribute, NULL },
 };
 
 static const scoped_attribute_specs jit_gnu_attribute_table =
@@ -143,7 +214,7 @@ static const attribute_spec jit_format_attributes[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "format",                 3, 3, false, true,  true, false,
+  { "format",		      3, 3, false, true,  true, false,
 			      handle_format_attribute, NULL },
   { "format_arg",             1, 1, false, true,  true, false,
 			      handle_format_arg_attribute, NULL }
@@ -212,14 +283,9 @@ handle_leaf_attribute (tree *node, tree name,
    struct attribute_spec.handler.  */
 
 static tree
-handle_const_attribute (tree *node, tree ARG_UNUSED (name),
-			tree ARG_UNUSED (args), int ARG_UNUSED (flags),
-			bool * ARG_UNUSED (no_add_attrs))
+handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			int ARG_UNUSED (flags), bool *no_add_attrs)
 {
-  if (TREE_CODE (*node) != FUNCTION_DECL
-      || !fndecl_built_in_p (*node))
-    inform (UNKNOWN_LOCATION, "%s:%s: %E: %E", __FILE__, __func__, *node, name);
-
   tree type = TREE_TYPE (*node);
 
   /* See FIXME comment on noreturn in c_common_attribute_table.  */
@@ -228,11 +294,16 @@ handle_const_attribute (tree *node, tree ARG_UNUSED (name),
   else if (TREE_CODE (type) == POINTER_TYPE
 	   && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
     TREE_TYPE (*node)
-      = build_pointer_type
-	(build_type_variant (TREE_TYPE (type), 1,
-			     TREE_THIS_VOLATILE (TREE_TYPE (type))));
+      = (build_qualified_type
+	 (build_pointer_type
+	  (build_type_variant (TREE_TYPE (type), 1,
+			       TREE_THIS_VOLATILE (TREE_TYPE (type)))),
+	  TYPE_QUALS (type)));
   else
-    gcc_unreachable ();
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
 
   return NULL_TREE;
 }
@@ -494,6 +565,357 @@ handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle an "visibility" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_visibility_attribute (tree *node, tree name, tree args,
+			     int ARG_UNUSED (flags),
+			     bool *ARG_UNUSED (no_add_attrs))
+{
+  tree decl = *node;
+  tree id = TREE_VALUE (args);
+  enum symbol_visibility vis;
+
+  if (TYPE_P (*node))
+    {
+      if (TREE_CODE (*node) == ENUMERAL_TYPE)
+	/* OK.  */;
+      else if (!RECORD_OR_UNION_TYPE_P (*node))
+	{
+	  warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
+		   name);
+	  return NULL_TREE;
+	}
+      else if (TYPE_FIELDS (*node))
+	{
+	  error ("%qE attribute ignored because %qT is already defined",
+		 name, *node);
+	  return NULL_TREE;
+	}
+    }
+  else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("visibility argument not a string");
+      return NULL_TREE;
+    }
+
+  /*  If this is a type, set the visibility on the type decl.  */
+  if (TYPE_P (decl))
+    {
+      decl = TYPE_NAME (decl);
+      if (!decl)
+	return NULL_TREE;
+      if (TREE_CODE (decl) == IDENTIFIER_NODE)
+	{
+	   warning (OPT_Wattributes, "%qE attribute ignored on types",
+		    name);
+	   return NULL_TREE;
+	}
+    }
+
+  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
+    vis = VISIBILITY_DEFAULT;
+  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
+    vis = VISIBILITY_INTERNAL;
+  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
+    vis = VISIBILITY_HIDDEN;
+  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
+    vis = VISIBILITY_PROTECTED;
+  else
+    {
+      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
+	     name, "default", "hidden", "protected", "internal");
+      vis = VISIBILITY_DEFAULT;
+    }
+
+  if (DECL_VISIBILITY_SPECIFIED (decl)
+      && vis != DECL_VISIBILITY (decl))
+    {
+      tree attributes = (TYPE_P (*node)
+			 ? TYPE_ATTRIBUTES (*node)
+			 : DECL_ATTRIBUTES (decl));
+      if (lookup_attribute ("visibility", attributes))
+	error ("%qD redeclared with different visibility", decl);
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllimport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllimport");
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	       && lookup_attribute ("dllexport", attributes))
+	error ("%qD was declared %qs which implies default visibility",
+	       decl, "dllexport");
+    }
+
+  DECL_VISIBILITY (decl) = vis;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+
+  /* Go ahead and attach the attribute to the node as well.  This is needed
+     so we can determine whether we have VISIBILITY_DEFAULT because the
+     visibility was not specified, or because it was explicitly overridden
+     from the containing scope.  */
+
+  return NULL_TREE;
+}
+
+/* Handle a "always_inline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_always_inline_attribute (tree *node, tree name,
+				tree ARG_UNUSED (args),
+				int ARG_UNUSED (flags),
+				bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      /* Set the attribute and mark it for disregarding inline
+	 limits.  */
+      DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "cold" and attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      || TREE_CODE (*node) == LABEL_DECL)
+    {
+      /* Attribute cold processing is done later with lookup_attribute.  */
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "noinline" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_noinline_attribute (tree *node, tree name,
+			   tree ARG_UNUSED (args),
+			   int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    DECL_UNINLINABLE (*node) = 1;
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "weak" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_weak_attribute (tree *node, tree name,
+		       tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags),
+		       bool * ARG_UNUSED (no_add_attrs))
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL
+      && DECL_DECLARED_INLINE_P (*node))
+    {
+      warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
+      *no_add_attrs = true;
+    }
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weak", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (VAR_OR_FUNCTION_DECL_P (*node))
+    declare_weak (*node);
+  else
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+
+  return NULL_TREE;
+}
+
+/* Handle a "target" attribute.  */
+
+static tree
+handle_target_attribute (tree *node, tree name, tree args, int flags,
+			 bool *no_add_attrs)
+{
+  /* Ensure we have a function declaration.  */
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if (! targetm.target_option.valid_attribute_p (*node, name, args,
+						      flags))
+    *no_add_attrs = true;
+
+  /* Check that there's no empty string in values of the attribute.  */
+  for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
+    {
+      tree value = TREE_VALUE (t);
+      if (TREE_CODE (value) == STRING_CST
+	  && TREE_STRING_LENGTH (value) == 1
+	  && TREE_STRING_POINTER (value)[0] == '\0')
+	{
+	  warning (OPT_Wattributes, "empty string in attribute %<target%>");
+	  *no_add_attrs = true;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle a "used" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
+		       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  if (TREE_CODE (node) == FUNCTION_DECL
+      || (VAR_P (node) && TREE_STATIC (node))
+      || (TREE_CODE (node) == TYPE_DECL))
+    {
+      TREE_USED (node) = 1;
+      DECL_PRESERVE_P (node) = 1;
+      if (VAR_P (node))
+	DECL_READ_P (node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler, except that IS_ALIAS tells us
+   whether this is an alias as opposed to ifunc attribute.  */
+
+static tree
+handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
+			      bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (!is_alias || !VAR_P (decl)))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
+      /* A static variable declaration is always a tentative definition,
+	 but the alias is a non-tentative definition which overrides.  */
+      || (TREE_CODE (decl) != FUNCTION_DECL
+	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
+    {
+      error ("%q+D defined both normally and as %qE attribute", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+  else if (!is_alias
+	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
+	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
+    {
+      error ("weak %q+D cannot be defined %qE", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Note that the very first time we process a nested declaration,
+     decl_function_context will not be set.  Indeed, *would* never
+     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
+     we do below.  After such frobbery, pushdecl would set the context.
+     In any case, this is never what we want.  */
+  else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
+    {
+      tree id;
+
+      id = TREE_VALUE (args);
+      if (TREE_CODE (id) != STRING_CST)
+	{
+	  error ("attribute %qE argument not a string", name);
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+      id = get_identifier (TREE_STRING_POINTER (id));
+      /* This counts as a use of the object pointed to.  */
+      TREE_USED (id) = 1;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	DECL_INITIAL (decl) = error_mark_node;
+      else
+	TREE_STATIC (decl) = 1;
+
+      if (!is_alias)
+	{
+	  /* ifuncs are also aliases, so set that attribute too.  */
+	  DECL_ATTRIBUTES (decl)
+	    = tree_cons (get_identifier ("alias"), args,
+			 DECL_ATTRIBUTES (decl));
+	  DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
+					      NULL, DECL_ATTRIBUTES (decl));
+	}
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  if (decl_in_symtab_p (*node))
+    {
+      struct symtab_node *n = symtab_node::get (decl);
+      if (n && n->refuse_visibility_changes)
+	error ("%+qD declared %qs after being used",
+	       decl, is_alias ? "alias" : "ifunc");
+    }
+
+
+  return NULL_TREE;
+}
+
+/* Handle an "alias" or "ifunc" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_alias_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
+}
+
 /* (end of attribute-handling).  */
 
 /* Language-dependent contents of a type.  */
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index f87351eaf82..84df6c100e6 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config.h"
 #define INCLUDE_MUTEX
+#include "libgccjit.h"
 #include "system.h"
 #include "coretypes.h"
 #include "target.h"
@@ -499,6 +500,54 @@ new_param (location *loc,
   return new param (this, inner);
 }
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_FN_ATTRIBUTE_ALIAS:
+      return "alias";
+    case GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE:
+      return "always_inline";
+    case GCC_JIT_FN_ATTRIBUTE_INLINE:
+      return NULL;
+    case GCC_JIT_FN_ATTRIBUTE_NOINLINE:
+      return "noinline";
+    case GCC_JIT_FN_ATTRIBUTE_TARGET:
+      return "target";
+    case GCC_JIT_FN_ATTRIBUTE_USED:
+      return "used";
+    case GCC_JIT_FN_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_FN_ATTRIBUTE_COLD:
+      return "cold";
+    case GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE:
+      return "returns_twice";
+    case GCC_JIT_FN_ATTRIBUTE_PURE:
+      return "pure";
+    case GCC_JIT_FN_ATTRIBUTE_CONST:
+      return "const";
+    case GCC_JIT_FN_ATTRIBUTE_WEAK:
+      return "weak";
+    case GCC_JIT_FN_ATTRIBUTE_NONNULL:
+      return "nonnull";
+    case GCC_JIT_FN_ATTRIBUTE_MAX:
+      return NULL;
+  }
+  return NULL;
+}
+
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr)
+{
+  switch (attr)
+  {
+    case GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY:
+      return "visibility";
+    case GCC_JIT_VARIABLE_ATTRIBUTE_MAX:
+      return NULL;
+  }
+  return NULL;
+}
+
 /* Construct a playback::function instance.  */
 
 playback::function *
@@ -509,7 +558,13 @@ new_function (location *loc,
 	      const char *name,
 	      const auto_vec<param *> *params,
 	      int is_variadic,
-	      enum built_in_function builtin_id)
+	      enum built_in_function builtin_id,
+	      const std::vector<gcc_jit_fn_attribute> &attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::string>> &string_attributes,
+	      const std::vector<std::pair<gcc_jit_fn_attribute,
+					  std::vector<int>>>
+					  &int_array_attributes)
 {
   int i;
   param *param;
@@ -543,6 +598,8 @@ new_function (location *loc,
   DECL_RESULT (fndecl) = resdecl;
   DECL_CONTEXT (resdecl) = fndecl;
 
+  tree fn_attributes = NULL_TREE;
+
   if (builtin_id)
     {
       gcc_assert (loc == NULL);
@@ -588,12 +645,62 @@ new_function (location *loc,
       DECL_DECLARED_INLINE_P (fndecl) = 1;
 
       /* Add attribute "always_inline": */
-      DECL_ATTRIBUTES (fndecl) =
-	tree_cons (get_identifier ("always_inline"),
-		   NULL,
-		   DECL_ATTRIBUTES (fndecl));
+      fn_attributes = tree_cons (get_identifier ("always_inline"),
+				 NULL,
+				 fn_attributes);
     }
 
+  /* All attributes need to be declared in `dummy-frontend.cc` and more
+     specifically in `jit_attribute_table`. */
+  for (auto attr: attributes)
+  {
+    if (attr == GCC_JIT_FN_ATTRIBUTE_INLINE)
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+    {
+      tree ident = get_identifier (attribute);
+      fn_attributes = tree_cons (ident, NULL_TREE, fn_attributes);
+    }
+  }
+
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (ident)
+      fn_attributes = tree_cons (ident, attribute_value, fn_attributes);
+  }
+
+  for (auto attr: int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+
+    const char* attribute = fn_attribute_to_string (name);
+    tree ident = attribute ? get_identifier (attribute) : NULL;
+
+    if (!ident)
+      continue;
+
+    tree tree_list = NULL_TREE;
+    tree *p_tree_list = &tree_list;
+    for (auto value : values)
+    {
+      tree int_value = build_int_cst (integer_type_node, value);
+      *p_tree_list = build_tree_list (NULL, int_value);
+      p_tree_list = &TREE_CHAIN (*p_tree_list);
+    }
+    fn_attributes = tree_cons (ident, tree_list, fn_attributes);
+  }
+
+  decl_attributes (&fndecl, fn_attributes, 0);
   function *func = new function (this, fndecl, kind);
   m_functions.safe_push (func);
   return func;
@@ -607,7 +714,9 @@ global_new_decl (location *loc,
 		 enum gcc_jit_global_kind kind,
 		 type *type,
 		 const char *name,
-		 enum global_var_flags flags)
+		 enum global_var_flags flags,
+		 const std::vector<std::pair<gcc_jit_variable_attribute,
+					     std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -652,9 +761,32 @@ global_new_decl (location *loc,
   if (loc)
     set_tree_location (inner, loc);
 
+  set_variable_string_attribute (attributes, inner);
+
   return inner;
 }
 
+void
+playback::
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			       std::string>> &string_attributes,
+  tree decl)
+{
+  tree var_attributes = NULL_TREE;
+  for (auto attr: string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    tree attribute_value = build_tree_list (NULL_TREE,
+	::build_string (value.length () + 1, value.c_str ()));
+    tree ident = get_identifier (variable_attribute_to_string (name));
+    if (ident)
+      var_attributes = tree_cons (ident, attribute_value, var_attributes);
+  }
+  decl_attributes (&decl, var_attributes, 0);
+}
+
 /* In use by new_global and new_global_initialized.  */
 
 playback::lvalue *
@@ -674,10 +806,12 @@ new_global (location *loc,
 	    enum gcc_jit_global_kind kind,
 	    type *type,
 	    const char *name,
-	    enum global_var_flags flags)
+	    enum global_var_flags flags,
+	    const std::vector<std::pair<gcc_jit_variable_attribute,
+					std::string>> &attributes)
 {
   tree inner =
-    global_new_decl (loc, kind, type, name, flags);
+    global_new_decl (loc, kind, type, name, flags, attributes);
 
   return global_finalize_lvalue (inner);
 }
@@ -818,13 +952,15 @@ playback::context::
 new_global_initialized (location *loc,
 			enum gcc_jit_global_kind kind,
 			type *type,
-                        size_t element_size,
+			size_t element_size,
 			size_t initializer_num_elem,
 			const void *initializer,
 			const char *name,
-			enum global_var_flags flags)
+			enum global_var_flags flags,
+			const std::vector<std::pair<gcc_jit_variable_attribute,
+						    std::string>> &attributes)
 {
-  tree inner = global_new_decl (loc, kind, type, name, flags);
+  tree inner = global_new_decl (loc, kind, type, name, flags, attributes);
 
   vec<constructor_elt, va_gc> *constructor_elements = NULL;
 
@@ -1812,7 +1948,9 @@ playback::lvalue *
 playback::function::
 new_local (location *loc,
 	   type *type,
-	   const char *name)
+	   const char *name,
+	   const std::vector<std::pair<gcc_jit_variable_attribute,
+				       std::string>> &attributes)
 {
   gcc_assert (type);
   gcc_assert (name);
@@ -1825,6 +1963,8 @@ new_local (location *loc,
   DECL_CHAIN (inner) = BIND_EXPR_VARS (m_inner_bind_expr);
   BIND_EXPR_VARS (m_inner_bind_expr) = inner;
 
+  set_variable_string_attribute (attributes, inner);
+
   if (loc)
     set_tree_location (inner, loc);
   return new lvalue (m_ctxt, inner);
@@ -1947,6 +2087,9 @@ postprocess ()
 
       current_function_decl = NULL;
     }
+    else
+      /* Add to cgraph to output aliases: */
+      rest_of_decl_compilation (m_inner_fndecl, true, 0);
 }
 
 /* Don't leak vec's internal buffer (in non-GC heap) when we are
@@ -3365,7 +3508,7 @@ void
 playback::context::
 init_types ()
 {
-  /* See lto_init() in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc 
+  /* See lto_init () in lto-lang.cc or void visit (TypeBasic *t) in D's types.cc
      for reference. If TYPE_NAME is not set, debug info will not contain types */
 #define NAME_TYPE(t,n) \
 if (t) \
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 9654507a34d..05bafcd21c4 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -21,7 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef JIT_PLAYBACK_H
 #define JIT_PLAYBACK_H
 
+#include <string>
 #include <utility> // for std::pair
+#include <vector>
 
 #include "timevar.h"
 #include "varasm.h"
@@ -35,12 +37,21 @@ namespace gcc {
 
 namespace jit {
 
+const char* fn_attribute_to_string (gcc_jit_fn_attribute attr);
+const char* variable_attribute_to_string (gcc_jit_variable_attribute attr);
+
 /**********************************************************************
  Playback.
  **********************************************************************/
 
 namespace playback {
 
+void
+set_variable_string_attribute (
+  const std::vector<std::pair<gcc_jit_variable_attribute,
+			      std::string>> &attributes,
+  tree decl);
+
 /* playback::context is an abstract base class.
 
    The two concrete subclasses are:
@@ -104,14 +115,22 @@ public:
 		const char *name,
 		const auto_vec<param *> *params,
 		int is_variadic,
-		enum built_in_function builtin_id);
+		enum built_in_function builtin_id,
+		const std::vector<gcc_jit_fn_attribute> &attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::string>> &string_attributes,
+		const std::vector<std::pair<gcc_jit_fn_attribute,
+					    std::vector<int>>>
+					    &int_array_attributes);
 
   lvalue *
   new_global (location *loc,
 	      enum gcc_jit_global_kind kind,
 	      type *type,
 	      const char *name,
-	      enum global_var_flags flags);
+	      enum global_var_flags flags,
+	      const std::vector<std::pair<gcc_jit_variable_attribute,
+					  std::string>> &attributes);
 
   lvalue *
   new_global_initialized (location *loc,
@@ -121,7 +140,11 @@ public:
                           size_t initializer_num_elem,
                           const void *initializer,
 			  const char *name,
-			  enum global_var_flags flags);
+			  enum global_var_flags flags,
+			  const std::vector<std::pair<
+					    gcc_jit_variable_attribute,
+					    std::string>>
+					    &attributes);
 
   rvalue *
   new_ctor (location *log,
@@ -306,7 +329,9 @@ private:
                    enum gcc_jit_global_kind kind,
                    type *type,
 		   const char *name,
-		   enum global_var_flags flags);
+		   enum global_var_flags flags,
+		   const std::vector<std::pair<gcc_jit_variable_attribute,
+					       std::string>> &attributes);
   lvalue *
   global_finalize_lvalue (tree inner);
 
@@ -500,7 +525,9 @@ public:
   lvalue *
   new_local (location *loc,
 	     type *type,
-	     const char *name);
+	     const char *name,
+	     const std::vector<std::pair<gcc_jit_variable_attribute,
+					 std::string>> &attributes);
 
   block*
   new_block (const char *name);
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index 686c0587079..6ffadbea127 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-builtins.h"
 #include "jit-recording.h"
 #include "jit-playback.h"
+#include <sstream>
 
 namespace gcc {
 namespace jit {
@@ -2068,7 +2069,7 @@ recording::memento::get_debug_string ()
 void
 recording::memento::write_to_dump (dump &d)
 {
-  d.write("  %s\n", get_debug_string ());
+  d.write ("  %s\n", get_debug_string ());
 }
 
 /* The implementation of class gcc::jit::recording::string.  */
@@ -4026,6 +4027,13 @@ void recording::lvalue::set_alignment (unsigned bytes)
   m_alignment = bytes;
 }
 
+void recording::lvalue::add_string_attribute (
+	gcc_jit_variable_attribute attribute,
+	const char* value)
+{
+  m_string_attributes.push_back (std::make_pair (attribute, std::string (value)));
+}
+
 /* The implementation of class gcc::jit::recording::param.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -4102,7 +4110,10 @@ recording::function::function (context *ctxt,
   m_builtin_id (builtin_id),
   m_locals (),
   m_blocks (),
-  m_fn_ptr_type (NULL)
+  m_fn_ptr_type (NULL),
+  m_attributes (),
+  m_string_attributes (),
+  m_int_array_attributes ()
 {
   for (int i = 0; i< num_params; i++)
     {
@@ -4161,7 +4172,10 @@ recording::function::replay_into (replayer *r)
 				     m_name->c_str (),
 				     &params,
 				     m_is_variadic,
-				     m_builtin_id));
+				     m_builtin_id,
+				     m_attributes,
+				     m_string_attributes,
+				     m_int_array_attributes));
 }
 
 /* Create a recording::local instance and add it to
@@ -4210,6 +4224,40 @@ recording::function::new_block (const char *name)
 void
 recording::function::write_to_dump (dump &d)
 {
+  for (auto attr: m_attributes)
+  {
+    const char* attribute = fn_attribute_to_string (attr);
+    if (attribute)
+      d.write ("__attribute(%s)__\n", attribute);
+  }
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
+  for (auto attr: m_int_array_attributes)
+  {
+    gcc_jit_fn_attribute& name = std::get<0>(attr);
+    std::vector<int>& values = std::get<1>(attr);
+    const char* attribute = fn_attribute_to_string (name);
+    if (attribute)
+    {
+      d.write ("__attribute(%s(", attribute);
+      for (size_t i = 0; i < values.size(); ++i)
+      {
+	if (i > 0)
+	  d.write (", %d", values[i]);
+	else
+	  d.write ("%d", values[i]);
+      }
+      d.write ("))__\n");
+    }
+  }
+
   switch (m_kind)
     {
     default: gcc_unreachable ();
@@ -4404,6 +4452,31 @@ recording::function::get_address (recording::location *loc)
   return result;
 }
 
+void
+recording::function::add_attribute (gcc_jit_fn_attribute attribute)
+{
+  m_attributes.push_back (attribute);
+}
+
+void
+recording::function::add_string_attribute (gcc_jit_fn_attribute attribute,
+					   const char* value)
+{
+  m_string_attributes.push_back (
+	std::make_pair (attribute, std::string (value)));
+}
+
+void
+recording::function::add_integer_array_attribute (
+	gcc_jit_fn_attribute attribute,
+	const int* value,
+	size_t length)
+{
+  m_int_array_attributes.push_back (std::make_pair (
+    attribute,
+    std::vector<int> (value, value + length)));
+}
+
 /* Implementation of recording::memento::make_debug_string for
    functions.  */
 
@@ -4425,6 +4498,39 @@ static const char * const names_of_function_kinds[] = {
 
 /* Implementation of recording::memento::write_reproducer for functions. */
 
+static const char * const fn_attribute_reproducer_strings[] =
+{
+  "GCC_JIT_FN_ATTRIBUTE_ALIAS",
+  "GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_INLINE",
+  "GCC_JIT_FN_ATTRIBUTE_NOINLINE",
+  "GCC_JIT_FN_ATTRIBUTE_TARGET",
+  "GCC_JIT_FN_ATTRIBUTE_USED",
+  "GCC_JIT_FN_ATTRIBUTE_VISIBILITY",
+  "GCC_JIT_FN_ATTRIBUTE_COLD",
+  "GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE",
+  "GCC_JIT_FN_ATTRIBUTE_PURE",
+  "GCC_JIT_FN_ATTRIBUTE_CONST",
+  "GCC_JIT_FN_ATTRIBUTE_WEAK",
+  "GCC_JIT_FN_ATTRIBUTE_NONNULL",
+};
+
+std::string
+get_vector_int_debug (std::vector<int> &values)
+{
+  std::stringstream s;
+
+  s << "{";
+  for(auto it = values.begin(); it != values.end(); ++it)
+    {
+      if (it != values.begin() )
+	 s << ", ";
+      s << *it;
+    }
+  s << "}";
+  return s.str();
+}
+
 void
 recording::function::write_reproducer (reproducer &r)
 {
@@ -4467,6 +4573,25 @@ recording::function::write_reproducer (reproducer &r)
 	   m_params.length (),
 	   params_id,
 	   m_is_variadic);
+  for (auto attribute : m_attributes)
+    r.write("  gcc_jit_function_add_attribute (%s, %s);\n",
+	    id,
+	    fn_attribute_reproducer_strings[attribute]);
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_function_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+  for (auto attribute : m_int_array_attributes) {
+    r.write("  gcc_jit_function_add_integer_array_attribute (%s,\n"
+	    "                                                %s,\n"
+	    "                                                (int[])%s,\n"
+	    "                                                %lu);\n",
+	    id,
+	    fn_attribute_reproducer_strings[std::get<0>(attribute)],
+	    get_vector_int_debug (std::get<1>(attribute)).c_str(),
+	    std::get<1>(attribute).size ());
+  }
 }
 
 
@@ -4879,12 +5004,14 @@ recording::global::replay_into (replayer *r)
 				 / m_type->dereference ()->get_size (),
 				 m_initializer,
 				 playback_string (m_name),
-				 m_flags)
+				 m_flags,
+				 m_string_attributes)
     : r->new_global (playback_location (r, m_loc),
 		     m_kind,
 		     m_type->playback_type (),
 		     playback_string (m_name),
-		     m_flags);
+		     m_flags,
+		     m_string_attributes);
 
   if (m_tls_model != GCC_JIT_TLS_MODEL_NONE)
     global->set_tls_model (recording::tls_models[m_tls_model]);
@@ -4943,6 +5070,15 @@ recording::global::write_to_dump (dump &d)
       break;
     }
 
+  for (auto attr: m_string_attributes)
+  {
+    gcc_jit_variable_attribute& name = std::get<0>(attr);
+    std::string& value = std::get<1>(attr);
+    const char* attribute = variable_attribute_to_string (name);
+
+    if (attribute)
+      d.write ("__attribute(%s(\"%s\"))__\n", attribute, value.c_str());
+  }
   d.write ("%s %s",
 	   m_type->get_debug_string (),
 	   get_debug_string ());
@@ -5013,6 +5149,10 @@ static const char * const tls_model_enum_strings[] = {
   "GCC_JIT_TLS_MODEL_LOCAL_EXEC",
 };
 
+static const char * const gcc_jit_variable_attribute_enum_strings[] = {
+  "GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY",
+};
+
 void
 recording::global::write_reproducer (reproducer &r)
 {
@@ -5042,6 +5182,13 @@ recording::global::write_reproducer (reproducer &r)
      id,
      m_link_section->c_str ());
 
+  for (auto attribute : m_string_attributes)
+    r.write("  gcc_jit_lvalue_add_string_attribute (%s, %s, \"%s\");\n",
+	    id,
+	    gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)],
+	    std::get<1>(attribute).c_str());
+
+
   if (m_initializer)
     switch (m_type->dereference ()->get_size ())
       {
@@ -6622,7 +6769,8 @@ recording::local::replay_into (replayer *r)
   playback::lvalue *obj = m_func->playback_function ()
       ->new_local (playback_location (r, m_loc),
 		   m_type->playback_type (),
-		   playback_string (m_name));
+		   playback_string (m_name),
+		   m_string_attributes);
 
   if (m_reg_name != NULL)
     obj->set_register_name (m_reg_name->c_str ());
@@ -6644,9 +6792,9 @@ recording::local::write_to_dump (dump &d)
 {
   if (d.update_locations ())
     m_loc = d.make_location ();
-  d.write("  %s %s;\n",
-	  m_type->get_debug_string (),
-	  get_debug_string ());
+  d.write ("  %s %s;\n",
+	   m_type->get_debug_string (),
+	   get_debug_string ());
 }
 
 void
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index b951c715ca5..cd2e0adbe30 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -23,6 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "jit-common.h"
 #include "jit-logging.h"
+#include "libgccjit.h"
+
+#include <string>
+#include <vector>
 
 class timer;
 
@@ -1216,7 +1220,8 @@ public:
     m_link_section (NULL),
     m_reg_name (NULL),
     m_tls_model (GCC_JIT_TLS_MODEL_NONE),
-    m_alignment (0)
+    m_alignment (0),
+    m_string_attributes ()
   {}
 
   playback::lvalue *
@@ -1236,8 +1241,12 @@ public:
   as_rvalue () { return this; }
 
   const char *access_as_rvalue (reproducer &r) override;
+
+  void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value);
+
   virtual const char *access_as_lvalue (reproducer &r);
   virtual bool is_global () const { return false; }
+  virtual bool is_local () const { return false; }
   void set_tls_model (enum gcc_jit_tls_model model);
   void set_link_section (const char *name);
   void set_register_name (const char *reg_name);
@@ -1249,6 +1258,8 @@ protected:
   string *m_reg_name;
   enum gcc_jit_tls_model m_tls_model;
   unsigned m_alignment;
+  std::vector<std::pair<gcc_jit_variable_attribute,
+	      std::string>> m_string_attributes;
 };
 
 class param : public lvalue
@@ -1342,6 +1353,10 @@ public:
 
   rvalue *get_address (location *loc);
 
+  void add_attribute (gcc_jit_fn_attribute attribute);
+  void add_string_attribute (gcc_jit_fn_attribute attribute, const char* value);
+  void add_integer_array_attribute (gcc_jit_fn_attribute attribute, const int* value, size_t length);
+
 private:
   string * make_debug_string () final override;
   void write_reproducer (reproducer &r) final override;
@@ -1357,6 +1372,9 @@ private:
   auto_vec<local *> m_locals;
   auto_vec<block *> m_blocks;
   type *m_fn_ptr_type;
+  std::vector<gcc_jit_fn_attribute> m_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::string>> m_string_attributes;
+  std::vector<std::pair<gcc_jit_fn_attribute, std::vector<int>>> m_int_array_attributes;
 };
 
 class block : public memento
@@ -2086,6 +2104,8 @@ public:
 
   void visit_children (rvalue_visitor *) final override {}
 
+  bool is_local () const final override { return true; }
+
   void write_to_dump (dump &d) final override;
 
 private:
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 8ecfe4aaa42..9616f3802b8 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -3965,6 +3965,73 @@ gcc_jit_type_get_aligned (gcc_jit_type *type,
   return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
 }
 
+void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				gcc_jit_fn_attribute attribute)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_attribute (attribute);
+}
+
+void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       gcc_jit_fn_attribute attribute,
+				       const char* value)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (value, NULL, NULL, "NULL value");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_string_attribute (attribute, value);
+}
+
+/* This function adds an attribute with multiple integer values.  For example
+   `nonnull(1, 2)`.  The numbers in `values` are supposed to map how they
+   should be written in C code.  So for `nonnull(1, 2)`, you should pass `1`
+   and `2` in `values` (and set `length` to `2`). */
+void
+gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
+					      gcc_jit_fn_attribute attribute,
+					      const int* values,
+					      size_t length)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL func");
+  RETURN_IF_FAIL (values, NULL, NULL, "NULL values");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_FN_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_fn_attribute` enum value");
+
+  func->add_integer_array_attribute (attribute, values, length);
+}
+
+void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     gcc_jit_variable_attribute attribute,
+				     const char* value)
+{
+  RETURN_IF_FAIL (variable, NULL, NULL, "NULL variable");
+  RETURN_IF_FAIL (value, NULL, NULL, "NULL value");
+  RETURN_IF_FAIL (variable->is_global () || variable->is_local (),
+		  NULL,
+		  NULL,
+		  "variable should be a variable");
+  RETURN_IF_FAIL ((attribute >= 0 && attribute < GCC_JIT_VARIABLE_ATTRIBUTE_MAX),
+		  NULL,
+		  NULL,
+		  "attribute should be a `gcc_jit_variable_attribute` enum value");
+
+  variable->add_string_attribute (attribute, value);
+}
+
 /* 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 cbcfabba3e8..235cab053e0 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1999,6 +1999,61 @@ gcc_jit_vector_type_get_element_type (gcc_jit_vector_type *vector_type);
 extern gcc_jit_type *
 gcc_jit_type_unqualified (gcc_jit_type *type);
 
+#define LIBGCCJIT_HAVE_ATTRIBUTES
+
+/* Function attributes.  */
+enum gcc_jit_fn_attribute
+{
+  GCC_JIT_FN_ATTRIBUTE_ALIAS,
+  GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_INLINE,
+  GCC_JIT_FN_ATTRIBUTE_NOINLINE,
+  GCC_JIT_FN_ATTRIBUTE_TARGET,
+  GCC_JIT_FN_ATTRIBUTE_USED,
+  GCC_JIT_FN_ATTRIBUTE_VISIBILITY,
+  GCC_JIT_FN_ATTRIBUTE_COLD,
+  GCC_JIT_FN_ATTRIBUTE_RETURNS_TWICE,
+  GCC_JIT_FN_ATTRIBUTE_PURE,
+  GCC_JIT_FN_ATTRIBUTE_CONST,
+  GCC_JIT_FN_ATTRIBUTE_WEAK,
+  GCC_JIT_FN_ATTRIBUTE_NONNULL,
+
+  /* Maximum value of this enum, should always be last. */
+  GCC_JIT_FN_ATTRIBUTE_MAX,
+};
+
+/* Add an attribute to a function.  */
+extern void
+gcc_jit_function_add_attribute (gcc_jit_function *func,
+				enum gcc_jit_fn_attribute attribute);
+
+extern void
+gcc_jit_function_add_string_attribute (gcc_jit_function *func,
+				       enum gcc_jit_fn_attribute attribute,
+				       const char* value);
+
+extern void
+gcc_jit_function_add_integer_array_attribute (
+  gcc_jit_function *func,
+  enum gcc_jit_fn_attribute attribute,
+  const int* value,
+  size_t length);
+
+/* Variable attributes.  */
+enum gcc_jit_variable_attribute
+{
+  GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY,
+
+  /* Maximum value of this enum, should always be last. */
+  GCC_JIT_VARIABLE_ATTRIBUTE_MAX,
+};
+
+/* Add a string attribute to a variable.  */
+extern void
+gcc_jit_lvalue_add_string_attribute (gcc_jit_lvalue *variable,
+				     enum gcc_jit_variable_attribute attribute,
+				     const char* value);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index b62f5de72d0..dfb8a9d51fb 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -276,3 +276,11 @@ LIBGCCJIT_ABI_25 {
   global:
     gcc_jit_type_get_restrict;
 } LIBGCCJIT_ABI_24;
+
+LIBGCCJIT_ABI_26 {
+  global:
+    gcc_jit_function_add_attribute;
+    gcc_jit_function_add_string_attribute;
+    gcc_jit_lvalue_add_string_attribute;
+    gcc_jit_function_add_integer_array_attribute;
+} LIBGCCJIT_ABI_25;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index e762563f9bd..7f97dbe7306 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -32,6 +32,9 @@
 /* test-add-driver-options.c: We don't use this one, since the extra options
    affect the whole context.  */
 
+/* test-alias-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* test-alignment.c */
 #define create_code create_code_alignment
 #define verify_code verify_code_alignment
@@ -39,6 +42,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-always_inline-attribute.c: This can't be in the testcases array as it needs
+   the `-O0` flag.  */
+
 /* test-arith-overflow.c */
 #define create_code create_code_arith_overflow
 #define verify_code verify_code_arith_overflow
@@ -119,6 +125,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-cold-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-constants.c */
 #define create_code create_code_constants
 #define verify_code verify_code_constants
@@ -126,6 +135,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-const-attribute.c: This can't be in the testcases array as it needs
+   the `-O3` flag.  */
+
 /* test-debug-strings.c */
 #define create_code create_code_debug_strings
 #define verify_code verify_code_debug_strings
@@ -268,6 +280,12 @@
 #undef create_code
 #undef verify_code
 
+/* test-noinline-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
+/* test-nonnull-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-pr103562.c: We don't add this one, since it touches
    the optimization level of the context as a whole.  */
 
@@ -299,6 +317,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-pure-attribute.c: This can't be in the testcases array as it needs
+   the `-O3` flag.  */
+
 /* test-reading-struct.c */
 #define create_code create_code_reading_struct
 #define verify_code verify_code_reading_struct
@@ -316,6 +337,9 @@
 /* test-restrict.c: This can't be in the testcases array as it needs
    the `-O3` flag.  */
 
+/* test-restrict-attribute.c: This can't be in the testcases array as it needs
+   the `-O3` flag.  */
+
 /* test-register-variable.c: This can't be in the testcases array as it
    is target-specific.  */
 
@@ -350,6 +374,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-used-attribute.c: This can't be in the testcases array as it needs
+   the `-O2` flag.  */
+
 /* test-using-global.c */
 #define create_code create_code_using_global
 #define verify_code verify_code_using_global
@@ -361,6 +388,9 @@
    of gcc_jit_context_set_bool_allow_unreachable_blocks affects the whole
    context.  */
 
+/* test-variable-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* test-vector-types.cc: We don't use this, since it's C++.  */
 
 /* test-version.c */
@@ -377,6 +407,9 @@
 #undef create_code
 #undef verify_code
 
+/* test-weak-attribute.c: This can't be in the testcases array as it
+   doesn't have a verify_code implementation.  */
+
 /* Now expose the individual testcases as instances of this struct.  */
 
 struct testcase
diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp
index 8bf7e51c24f..56972064d30 100644
--- a/gcc/testsuite/jit.dg/jit.exp
+++ b/gcc/testsuite/jit.dg/jit.exp
@@ -899,8 +899,41 @@ proc jit-verify-assembler-output { args } {
 	pass "${asm_filename} output pattern test, ${dg-output-text}"
 	verbose "Passed test for output pattern ${dg-output-text}" 3
     }
+}
+
+# Assuming that a .s file has been written out named
+# OUTPUT_FILENAME, check that the argument doesn't match
+# the output file.
+proc jit-verify-assembler-output-not { args } {
+    verbose "jit-verify-assembler: $args"
+
+    set dg-output-text [lindex $args 0]
+    verbose "dg-output-text: ${dg-output-text}"
+
+    upvar 2 name name
+    verbose "name: $name"
+
+    upvar 2 prog prog
+    verbose "prog: $prog"
+    set asm_filename [jit-get-output-filename $prog]
+    verbose "  asm_filename: ${asm_filename}"
 
+    # Read the assembly file.
+    set f [open $asm_filename r]
+    set content [read $f]
+    close $f
+
+    # Verify that the assembly matches the regex.
+    if { [regexp ${dg-output-text} $content] } {
+	fail "${asm_filename} output pattern test, is ${content}, should not match ${dg-output-text}"
+	verbose "Failed test for output pattern ${dg-output-text}" 3
+    } else {
+	pass "${asm_filename} output pattern test, ${dg-output-text}"
+	verbose "Passed test for output pattern ${dg-output-text}" 3
+    }
 }
+
+
 # Assuming that a .o file has been written out named
 # OUTPUT_FILENAME, invoke the driver to try to turn it into
 # an executable, and try to run the result.
diff --git a/gcc/testsuite/jit.dg/test-alias-attribute.c b/gcc/testsuite/jit.dg/test-alias-attribute.c
new file mode 100644
index 00000000000..eb29003dfc9
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-alias-attribute.c
@@ -0,0 +1,50 @@
+/* { 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-alias-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+void xxx () {}
+void f () __attribute__ ((alias ("xxx")));
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_EXPORTED,
+          void_type,
+          "xxx",
+          0, NULL,
+          0);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_IMPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_string_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_ALIAS, "xxx");
+
+  /* void xxx () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".set\\s+f,xxx" } } */
diff --git a/gcc/testsuite/jit.dg/test-always_inline-attribute.c b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
new file mode 100644
index 00000000000..5c3f386663f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-always_inline-attribute.c
@@ -0,0 +1,153 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O0".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 0);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-always_inline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 gcc_jit_type *pint_type)
+{
+  /* The `a` function argument */
+  gcc_jit_param *a = gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          1, &a,
+          0);
+
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((always_inline))
+static inline int removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+static int not_removed (int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+int foo () {
+  int x = 0;
+  x += removed(NULL);
+  x += not_removed(NULL);
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer (int_type);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, pint_type);
+  /* This one is to declare the function as "inline" */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_INLINE);
+  /* __attribute__ ((always_inline)) */
+  gcc_jit_function_add_attribute(removed_func, GCC_JIT_FN_ATTRIBUTE_ALWAYS_INLINE);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, pint_type);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(NULL); */
+  gcc_jit_rvalue *null = gcc_jit_context_null (ctxt, pint_type);
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 1, &null));
+  
+  /* x += not_removed(NULL); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 1, &null));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-cold-attribute.c b/gcc/testsuite/jit.dg/test-cold-attribute.c
new file mode 100644
index 00000000000..0c76d3e3681
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-cold-attribute.c
@@ -0,0 +1,52 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-cold-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+int
+__attribute__ ((cold))
+t()
+{
+  return -1;
+}
+
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "t",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(func_t, GCC_JIT_FN_ATTRIBUTE_COLD);
+  gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+  gcc_jit_rvalue *ret = gcc_jit_context_new_rvalue_from_int (ctxt,
+    int_type,
+    -1);
+
+  gcc_jit_block_end_with_return (block, NULL, ret);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "orl" } } */
diff --git a/gcc/testsuite/jit.dg/test-const-attribute.c b/gcc/testsuite/jit.dg/test-const-attribute.c
new file mode 100644
index 00000000000..7b98041c9bb
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-const-attribute.c
@@ -0,0 +1,132 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-const-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((const))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_CONST);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-noinline-attribute.c b/gcc/testsuite/jit.dg/test-noinline-attribute.c
new file mode 100644
index 00000000000..eac6cae6b6a
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-noinline-attribute.c
@@ -0,0 +1,119 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-noinline-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func
+    = gcc_jit_context_new_function(ctxt, NULL,
+	  GCC_JIT_FUNCTION_INTERNAL,
+	  int_type,
+	  func_name,
+	  0, NULL,
+	  0);
+  gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+  gcc_jit_block_add_extended_asm (block, NULL, "");
+  gcc_jit_block_end_with_return (block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((noinline))
+static int not_removed() {
+  asm("");
+  return 1;
+}
+static int removed() {
+  asm("");
+  return 2;
+}
+int foo () {
+  int x = 0;
+  x += removed();
+  x += not_removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((no_inline)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_NOINLINE);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed.isra.0,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed.isra.0,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-nonnull-attribute.c b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
new file mode 100644
index 00000000000..42c38acb962
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-nonnull-attribute.c
@@ -0,0 +1,92 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-nonnull.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__((nonnull(1)))
+int t(int *a) {
+  if (!a) {
+    return -1;
+  }
+  return *a;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (ctxt, NULL, pint_type, "a");
+
+  gcc_jit_function *func_t =
+    gcc_jit_context_new_function (ctxt, NULL,
+	  GCC_JIT_FUNCTION_EXPORTED,
+	  int_type,
+	  "t",
+	  1, &a,
+	  0);
+  /* Adding `nonnull(1)` attribute. */
+  int indexes[1] = {1};
+  gcc_jit_function_add_integer_array_attribute (
+    func_t,
+    GCC_JIT_FN_ATTRIBUTE_NONNULL,
+    indexes,
+    1
+  );
+
+  /* if (!a) {
+    return -1;
+  } */
+  gcc_jit_block *if_cond =
+    gcc_jit_function_new_block (func_t, "if_cond");
+  gcc_jit_block *if_body =
+    gcc_jit_function_new_block (func_t, "if_body");
+  gcc_jit_block *after_if =
+    gcc_jit_function_new_block (func_t, "after_if");
+
+  /* if (!a) */
+  gcc_jit_block_end_with_conditional (
+    if_cond, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_EQ,
+      gcc_jit_param_as_rvalue (a),
+      gcc_jit_context_null (ctxt, pint_type)),
+    if_body,
+    after_if);
+  /* return -1; */
+  gcc_jit_block_end_with_return (
+    if_body, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, -1));
+
+  /* return *a; */
+  gcc_jit_block_end_with_return (
+    after_if, NULL,
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_rvalue_dereference (
+	gcc_jit_param_as_rvalue (a), NULL)));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "if block" was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "testq" } } */
+/* { dg-final { jit-verify-assembler-output-not "-1" } } */
diff --git a/gcc/testsuite/jit.dg/test-pure-attribute.c b/gcc/testsuite/jit.dg/test-pure-attribute.c
new file mode 100644
index 00000000000..0c773bc24d5
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-pure-attribute.c
@@ -0,0 +1,132 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O3".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-pure-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__ ((pure))
+int foo (int x);
+int xxx(void)
+{
+  int x = 45;
+  int sum = 0;
+
+  while (x >>= 1)
+    sum += foo (x) * 2;
+  return sum;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `foo` function. */
+  gcc_jit_param *n =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "x");
+  gcc_jit_param *params[1] = {n};
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_IMPORTED,
+          int_type,
+          "foo",
+          1, params,
+          0);
+  gcc_jit_function_add_attribute(foo_func, GCC_JIT_FN_ATTRIBUTE_PURE);
+
+  /* Creating the `xxx` function. */
+  gcc_jit_function *xxx_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "xxx",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (xxx_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "x");
+  gcc_jit_lvalue *sum =
+    gcc_jit_function_new_local (xxx_func, NULL, int_type, "sum");
+
+  /* int x = 45 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 45));
+  /* int sum = 0 */
+  gcc_jit_block_add_assignment (
+    block, NULL,
+    sum,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* while (x >>= 1) { sum += foo (x) * 2; } */
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (xxx_func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (xxx_func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (xxx_func, "after_loop");
+
+  gcc_jit_block_end_with_jump (block, NULL, loop_cond);
+
+
+  /* if (x >>= 1) */
+  /* Since gccjit doesn't (yet?) have support for `>>=` operator, we will decompose it into:
+     `if (x = x >> 1)` */
+  gcc_jit_block_add_assignment_op (
+    loop_cond, NULL,
+    x,
+    GCC_JIT_BINARY_OP_RSHIFT,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 1));
+  /* The condition itself */
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
+    gcc_jit_context_new_comparison (
+       ctxt, NULL,
+       GCC_JIT_COMPARISON_NE,
+       gcc_jit_lvalue_as_rvalue (x),
+       gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0)),
+    after_loop,
+    loop_body);
+
+  /* sum += foo (x) * 2; */
+  gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue(x);
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT, int_type,
+      gcc_jit_context_new_call (ctxt, NULL, foo_func, 1, &arg),
+      gcc_jit_context_new_rvalue_from_int (
+	ctxt,
+	int_type,
+	2)));
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
+
+  /* return sum; */
+  gcc_jit_block_end_with_return (after_loop, NULL, gcc_jit_lvalue_as_rvalue(sum));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the loop was optimized away */
+/* { dg-final { jit-verify-assembler-output-not "jne" } } */
diff --git a/gcc/testsuite/jit.dg/test-restrict-attribute.c b/gcc/testsuite/jit.dg/test-restrict-attribute.c
new file mode 100644
index 00000000000..d724472c652
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-restrict-attribute.c
@@ -0,0 +1,75 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+	// Set "-O3".
+	gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-restrict-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+	/* Let's try to inject the equivalent of:
+void t(int *__restrict__ a, int *__restrict__ b, char *__restrict__ c) {
+	*a += *c;
+	*b += *c;
+}
+	*/
+	gcc_jit_type *int_type =
+		gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+	gcc_jit_type *pint_type = gcc_jit_type_get_pointer(int_type);
+	gcc_jit_type *pint_restrict_type = gcc_jit_type_get_restrict(pint_type);
+
+	gcc_jit_type *void_type =
+		gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+	gcc_jit_param *a =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "a");
+	gcc_jit_param *b =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "b");
+	gcc_jit_param *c =
+		gcc_jit_context_new_param (ctxt, NULL, pint_restrict_type, "c");
+	gcc_jit_param *params[3] = {a, b, c};
+
+	gcc_jit_function *func_t =
+		gcc_jit_context_new_function (ctxt, NULL,
+					GCC_JIT_FUNCTION_EXPORTED,
+					void_type,
+					"t",
+					3, params,
+					0);
+
+	gcc_jit_block *block = gcc_jit_function_new_block (func_t, NULL);
+
+	/* *a += *c; */
+	gcc_jit_block_add_assignment_op (
+		block, NULL,
+		gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (a), NULL),
+		GCC_JIT_BINARY_OP_PLUS,
+		gcc_jit_lvalue_as_rvalue (
+			gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL)));
+	/* *b += *c; */
+	gcc_jit_block_add_assignment_op (
+		block, NULL,
+		gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (b), NULL),
+		GCC_JIT_BINARY_OP_PLUS,
+		gcc_jit_lvalue_as_rvalue (
+			gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL)));
+
+	gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output "addl\\s+%eax,\\s+(%rdi)
+\\s+addl\\s+%eax,\\s+(%rsi)" } } */
diff --git a/gcc/testsuite/jit.dg/test-used-attribute.c b/gcc/testsuite/jit.dg/test-used-attribute.c
new file mode 100644
index 00000000000..cb20952c687
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-used-attribute.c
@@ -0,0 +1,112 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+  // Set "-O2".
+  gcc_jit_context_set_int_option(ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2);
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND      GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME  "output-of-test-used-attribute.c.s"
+#include "harness.h"
+
+gcc_jit_function*
+create_function (gcc_jit_context *ctxt,
+		 const char *func_name,
+		 gcc_jit_type *int_type,
+		 int returned_value)
+{
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+          GCC_JIT_FUNCTION_INTERNAL,
+          int_type,
+          func_name,
+          0, NULL,
+          0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (func, NULL);
+  gcc_jit_block_end_with_return (foo_block, NULL,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, returned_value));
+
+  return func;
+}
+
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+__attribute__((used))
+static int not_removed() { return 1; }
+static int removed() { return 2; }
+int foo() {
+  int x = 0;
+  x += not_removed();
+  x += removed();
+  return x;
+}
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `not_removed` function. */
+  gcc_jit_function *not_removed_func =
+    create_function (ctxt, "not_removed", int_type, 1);
+  /* __attribute__ ((used)) */
+  gcc_jit_function_add_attribute(not_removed_func, GCC_JIT_FN_ATTRIBUTE_USED);
+
+  /* Creating the `removed` function. */
+  gcc_jit_function *removed_func =
+    create_function (ctxt, "removed", int_type, 2);
+
+  /* Creating the `foo` function. */
+  gcc_jit_function *foo_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "foo",
+				  0, NULL,
+				  0);
+
+  gcc_jit_block *foo_block = gcc_jit_function_new_block (foo_func, NULL);
+
+  /* Build locals:  */
+  gcc_jit_lvalue *x =
+    gcc_jit_function_new_local (foo_func, NULL, int_type, "x");
+
+  /* int x = 0; */
+  gcc_jit_block_add_assignment (
+    foo_block, NULL,
+    x,
+    gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 0));
+
+  /* x += removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, removed_func, 0, NULL));
+  
+  /* x += not_removed(); */
+  gcc_jit_block_add_assignment_op (
+    foo_block, NULL,
+    x,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_call (ctxt, NULL, not_removed_func, 0, NULL));
+
+  /* return x; */
+  gcc_jit_block_end_with_return (foo_block, NULL, gcc_jit_lvalue_as_rvalue(x));
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the "removed" function was inlined, but not the others */
+/* { dg-final { jit-verify-assembler-output-not ".type\\s+removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+not_removed,\\s+@function" } } */
+/* { dg-final { jit-verify-assembler-output ".type\\s+foo,\\s+@function" } } */
diff --git a/gcc/testsuite/jit.dg/test-variable-attribute.c b/gcc/testsuite/jit.dg/test-variable-attribute.c
new file mode 100644
index 00000000000..ea854ff4a9f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-variable-attribute.c
@@ -0,0 +1,46 @@
+/* { 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-variable-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+int PRIVATE __attribute__ ((visibility ("hidden"))) = 42;
+int PUBLIC = 12;
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Creating the `PRIVATE` variable. */
+  gcc_jit_lvalue *private = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PRIVATE");
+  gcc_jit_lvalue_add_string_attribute (private,
+    GCC_JIT_VARIABLE_ATTRIBUTE_VISIBILITY, "hidden");
+  gcc_jit_rvalue *rval = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 42);
+  gcc_jit_global_set_initializer_rvalue (private, rval);
+
+  /* Creating the `PUBLIC` variable. */
+  gcc_jit_lvalue *public = gcc_jit_context_new_global (ctxt,
+    NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "PUBLIC");
+  gcc_jit_rvalue *rval2 = gcc_jit_context_new_rvalue_from_int (
+      ctxt, int_type, 12);
+  gcc_jit_global_set_initializer_rvalue (public, rval2);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".hidden\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PRIVATE" } } */
+/* { dg-final { jit-verify-assembler-output-not ".hidden\\s+PUBLIC" } } */
+/* { dg-final { jit-verify-assembler-output ".globl\\s+PUBLIC" } } */
diff --git a/gcc/testsuite/jit.dg/test-weak-attribute.c b/gcc/testsuite/jit.dg/test-weak-attribute.c
new file mode 100644
index 00000000000..546ade1c3c4
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-weak-attribute.c
@@ -0,0 +1,41 @@
+/* { 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-weak-attribute.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+__attribute__ ((weak))
+void f () {}
+  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Creating the `f` function. */
+  gcc_jit_function *f_func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  void_type,
+				  "f",
+				  0, NULL,
+				  0);
+  gcc_jit_function_add_attribute(f_func, GCC_JIT_FN_ATTRIBUTE_WEAK);
+
+  /* void f () {} */
+  gcc_jit_block *block = gcc_jit_function_new_block (f_func, NULL);
+  gcc_jit_block_end_with_void_return (block, NULL);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* Check that the attribute was applied correctly */
+/* { dg-final { jit-verify-assembler-output ".weak\\s+f" } } */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2024-01-12 13:47 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-15 16:53 [PATCH] Add support for function attributes and variable attributes Guillaume Gomez
2023-11-15 16:56 ` Antoni Boucher
2023-11-23 21:52   ` Guillaume Gomez
2023-11-23 21:59     ` Antoni Boucher
2023-11-30  9:55       ` Guillaume Gomez
2023-12-07 17:13         ` Antoni Boucher
2023-12-09 11:12           ` Guillaume Gomez
2023-12-18 22:27             ` Guillaume Gomez
2024-01-03 13:37               ` Guillaume Gomez
2024-01-09 19:59 ` David Malcolm
2024-01-11  0:00   ` Guillaume Gomez
2024-01-11 18:46     ` David Malcolm
2024-01-11 21:40       ` Guillaume Gomez
2024-01-11 22:38         ` David Malcolm
2024-01-12 10:09           ` Guillaume Gomez
2024-01-12 13:47             ` Guillaume Gomez

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).