public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
To: David Malcolm <dmalcolm@redhat.com>
Cc: jit@gcc.gnu.org, Antoni <bouanto@zoho.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] Add support for function attributes and variable attributes
Date: Thu, 11 Jan 2024 01:00:43 +0100	[thread overview]
Message-ID: <CAAOQCfQEOCwRwWkHX=hNfYNcLg0EjuGsh5M_dymKqNxze9mEkQ@mail.gmail.com> (raw)
In-Reply-To: <319a998a3c63eaf0e28d7267a901ad2c016dba49.camel@redhat.com>

[-- 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


  reply	other threads:[~2024-01-11  0:00 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-15 16:53 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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAAOQCfQEOCwRwWkHX=hNfYNcLg0EjuGsh5M_dymKqNxze9mEkQ@mail.gmail.com' \
    --to=guillaume1.gomez@gmail.com \
    --cc=bouanto@zoho.com \
    --cc=dmalcolm@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jit@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).