diff mbox

[02/02] PR jit/64020: Fixes to handling of builtins

Message ID 1416966897-15936-3-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Nov. 26, 2014, 1:54 a.m. UTC
Investigating PR jit/64020 showed that tree-ssa-math-opts.c was
failing to optimize sin/cos operations within the JIT since this:

  fndecl = mathfn_built_in (type, BUILT_IN_CEXPI);

was returning NULL.

Several issues were present:
(A) In the JIT we don't pre-build all builtins, just the ones that the
    client code explicitly requests, and hence we could have
    BUILT_IN_SIN/COS without BUILT_IN_CEXPI.
(B) The JIT wasn't populating builtin_info.decl and
    builtin_info.implicit_p.

The fix for (A) is to detect when BUILT_IN_SIN/COS get used by client
code, and then implicitly ensure that BUILT_IN_CEXPI exists.

Wiring up BUILT_IN_CEXPI requires implementing complex types, so its
simplest to expose these directly in the public API by adding them to
enum gcc_jit_types.

The fix for (B) is to call set_builtin_decl when creating a fndecl for
a builtin (within gcc::jit::playback::context::new_function).

Comparing that function with analogous code in
c-family/c-common.c:def_builtin_1 and in add_builtin_function I
noticed that we weren't setting the DECL_BUILT_IN_CLASS and
attributes on the fndecl, so this patch fixes that also.

Doing so requires creating fn attributes for the builtins, and adding
them to the fndecls that we create on playback.  Creating such trees
for the attributes means that the builtins_manager now covers playback
as well as recording, so move it out of the gcc::jit::recording
namespace into just gcc::jit.

This fixes the issue: manual inspection of the built code (on x86_64)
after this patch shows that test-function.c's test_of_builtin_trig
successfully merges the sin/cos calls into a sincos call:

test_of_builtin_trig:
.LFB5:
	.cfi_startproc
.LVL4:
.L7:
	subq	$24, %rsp
	.cfi_def_cfa_offset 32
	leaq	8(%rsp), %rdi
	movq	%rsp, %rsi
	call	sincos@PLT
.LVL5:
	movsd	8(%rsp), %xmm0
	mulsd	(%rsp), %xmm0
	addq	$24, %rsp
	.cfi_def_cfa_offset 8
	addsd	%xmm0, %xmm0
	ret
	.cfi_endproc

Ideally this patch would also add a check for that into
test-function.c but the jit testsuite doesn't yet have a good way to
add such tests, so I'll leave that for a followup.

gcc/jit/ChangeLog:
	PR jit/64020
	* docs/topics/types.rst (Standard types) Add new enum values to
	the table of enum gcc_jit_types: GCC_JIT_TYPE_COMPLEX_FLOAT,
	GCC_JIT_TYPE_COMPLEX_DOUBLE, GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE.
	Widen the left-hand column so that
	GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE will fit.

	* jit-builtins.c: Include stringpool.h and jit-playback.h.
	Move everything out of the gcc::jit::recording namespace into
	just gcc::jit.
	(struct builtin_data): Add fields "fnclass", "attr", and
	"implicit_p".
	(DEF_BUILTIN): Update macro so populate the new fields.
	(builtins_manager::builtins_manager): Update for move out of
	recording namespace.  Initialize the m_attributes array.
	(builtins_manager::get_builtin_function): Likewise.
	(builtins_manager::get_builtin_function_by_id): New function.
	(builtins_manager::make_builtin_function): Update for move out of
	recording namespace.  Add fix for PR jit/64020 by detecting
	specific builtin ids and having them ensure that builtins for
	other ids are created as necessary.
	(builtins_manager::get_type): Update for move out of recording
	namespace.
	(builtins_manager::make_type): Likewise.  Add some missing
	#undefs.
	(builtins_manager::make_primitive_type): Update for move out of
	recording namespace.  Implement the three BT_COMPLEX_ cases and
	BT_DOUBLE_PTR.
	(builtins_manager::make_fn_type): Update for move out of recording
	namespace.
	(builtins_manager::make_ptr_type): Likewise.
	(builtins_manager::finish_playback): New function.
	(builtins_manager::get_class): New function.
	(builtins_manager::implicit_p): New function.
	(builtins_manager::get_attrs_tree): Two new functions.
	(builtins_manager::make_attrs_tree): New function.

	* jit-builtins.h: Move everything out of the gcc::jit::recording
	namespace into just gcc::jit.
	(enum built_in_attribute): New.
	(builtins_manager::builtins_manager): Update decl for namespace
	change.
	(builtins_manager::get_builtin_function): Likewise.
	(builtins_manager::get_class): New.
	(builtins_manager::implicit_p): New.
	(builtins_manager::get_attrs_tree): Two new functions.
	(builtins_manager::make_attrs_tree): New function.
	(builtins_manager::finish_playback): New.
	(builtins_manager::get_builtin_function_by_id): New.
	(builtins_manager::make_builtin_function): Update decl for
	namespace change.
	(builtins_manager::get_type): Likewise.
	(builtins_manager::make_type): Likewise.
	(builtins_manager::make_primitive_type): Likewise.
	(builtins_manager::make_fn_type): Likewise.
	(builtins_manager::make_ptr_type): Likewise.
	(builtins_manager): Likewise for fields.  Add new field
	"m_attributes".

	* jit-common.h (NUM_GCC_JIT_TYPES): Update.
	(builtins_manager): Update forward decl to reflect namespace
	change.

	* jit-playback.c: Include attribs.h and jit-builtins.h.
	(gcc::jit::playback::context::get_tree_node_for_type): Add cases
	for the new COMPLEX_ types.
	(gcc::jit::playback::context::new_function): If creating a
	builtin, set the DECL_BUILT_IN_CLASS and attributes on the fndecl,
	and call set_builtin_decl.
	(gcc::jit::playback::context::replay): If we have a
	builtins_manager, call its finish_playback method when we're done.

	* jit-playback.h:
	(gcc::jit::playback::context::get_builtins_manager): New function.

	* jit-recording.c
	(gcc::jit::recording::context::get_builtins_manager): New function.
	(gcc::jit::recording::get_builtin_function): Use
	get_builtins_manager, in case we're a child context.
	(gcc::jit::recording::memento_of_get_type::dereference): Add the
	COMPLEX_ types.
	(gcc::jit::recording::memento_of_get_type::is_int): Likewise.
	(gcc::jit::recording::memento_of_get_type::is_float): Likewise.
	(gcc::jit::recording::memento_of_get_type::is_bool): Likewise.
	(get_type_strings): Likewise.

	* jit-recording.h
	(gcc::jit::recording::context::get_builtins_manager): New.

	* libgccjit.h (enum gcc_jit_types): Add
	GCC_JIT_TYPE_COMPLEX_FLOAT, GCC_JIT_TYPE_COMPLEX_DOUBLE,
	GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE.
---
 gcc/jit/docs/topics/types.rst |  49 +++++-----
 gcc/jit/jit-builtins.c        | 209 ++++++++++++++++++++++++++++++++++--------
 gcc/jit/jit-builtins.h        |  74 ++++++++++++---
 gcc/jit/jit-common.h          |   4 +-
 gcc/jit/jit-playback.c        |  30 +++++-
 gcc/jit/jit-playback.h        |   5 +
 gcc/jit/jit-recording.c       |  48 +++++++++-
 gcc/jit/jit-recording.h       |   3 +
 gcc/jit/libgccjit.h           |   8 +-
 9 files changed, 348 insertions(+), 82 deletions(-)
diff mbox

Patch

diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index 6770eca..2824c96 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -57,32 +57,35 @@  Standard types
 
    Access a specific type.  The available types are:
 
-   =========================================  ================================
-   `enum gcc_jit_types` value                 Meaning
-   =========================================  ================================
-   :c:data:`GCC_JIT_TYPE_VOID`                C's ``void`` type.
-   :c:data:`GCC_JIT_TYPE_VOID_PTR`            C's ``void *``.
-   :c:data:`GCC_JIT_TYPE_BOOL`                C++'s ``bool`` type; also C99's
-                                              ``_Bool`` type, aka ``bool`` if
-                                              using stdbool.h.
-   :c:data:`GCC_JIT_TYPE_CHAR`                C's ``char`` (of some signedness)
-   :c:data:`GCC_JIT_TYPE_SIGNED_CHAR`         C's ``signed char``
-   :c:data:`GCC_JIT_TYPE_UNSIGNED_CHAR`       C's ``unsigned char``
-   :c:data:`GCC_JIT_TYPE_SHORT`               C's ``short`` (signed)
-   :c:data:`GCC_JIT_TYPE_UNSIGNED_SHORT`      C's ``unsigned short``
-   :c:data:`GCC_JIT_TYPE_INT`                 C's ``int`` (signed)
-   :c:data:`GCC_JIT_TYPE_UNSIGNED_INT`        C's ``unsigned int``
-   :c:data:`GCC_JIT_TYPE_LONG`                C's ``long`` (signed)
-   :c:data:`GCC_JIT_TYPE_UNSIGNED_LONG`       C's ``unsigned long``
-   :c:data:`GCC_JIT_TYPE_LONG_LONG`           C99's ``long long`` (signed)
-   :c:data:`GCC_JIT_TYPE_UNSIGNED_LONG_LONG`  C99's ``unsigned long long``
+   ==========================================  ================================
+   `enum gcc_jit_types` value                  Meaning
+   ==========================================  ================================
+   :c:data:`GCC_JIT_TYPE_VOID`                 C's ``void`` type.
+   :c:data:`GCC_JIT_TYPE_VOID_PTR`             C's ``void *``.
+   :c:data:`GCC_JIT_TYPE_BOOL`                 C++'s ``bool`` type; also C99's
+                                               ``_Bool`` type, aka ``bool`` if
+                                               using stdbool.h.
+   :c:data:`GCC_JIT_TYPE_CHAR`                 C's ``char`` (of some signedness)
+   :c:data:`GCC_JIT_TYPE_SIGNED_CHAR`          C's ``signed char``
+   :c:data:`GCC_JIT_TYPE_UNSIGNED_CHAR`        C's ``unsigned char``
+   :c:data:`GCC_JIT_TYPE_SHORT`                C's ``short`` (signed)
+   :c:data:`GCC_JIT_TYPE_UNSIGNED_SHORT`       C's ``unsigned short``
+   :c:data:`GCC_JIT_TYPE_INT`                  C's ``int`` (signed)
+   :c:data:`GCC_JIT_TYPE_UNSIGNED_INT`         C's ``unsigned int``
+   :c:data:`GCC_JIT_TYPE_LONG`                 C's ``long`` (signed)
+   :c:data:`GCC_JIT_TYPE_UNSIGNED_LONG`        C's ``unsigned long``
+   :c:data:`GCC_JIT_TYPE_LONG_LONG`            C99's ``long long`` (signed)
+   :c:data:`GCC_JIT_TYPE_UNSIGNED_LONG_LONG`   C99's ``unsigned long long``
    :c:data:`GCC_JIT_TYPE_FLOAT`
    :c:data:`GCC_JIT_TYPE_DOUBLE`
    :c:data:`GCC_JIT_TYPE_LONG_DOUBLE`
-   :c:data:`GCC_JIT_TYPE_CONST_CHAR_PTR`      C type: ``(const char *)``
-   :c:data:`GCC_JIT_TYPE_SIZE_T`              C's ``size_t`` type
-   :c:data:`GCC_JIT_TYPE_FILE_PTR`            C type: ``(FILE *)``
-   =========================================  ================================
+   :c:data:`GCC_JIT_TYPE_CONST_CHAR_PTR`       C type: ``(const char *)``
+   :c:data:`GCC_JIT_TYPE_SIZE_T`               C's ``size_t`` type
+   :c:data:`GCC_JIT_TYPE_FILE_PTR`             C type: ``(FILE *)``
+   :c:data:`GCC_JIT_TYPE_COMPLEX_FLOAT`        C99's ``_Complex float``
+   :c:data:`GCC_JIT_TYPE_COMPLEX_DOUBLE`       C99's ``_Complex double``
+   :c:data:`GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE`  C99's ``_Complex long double``
+   ==========================================  ================================
 
 .. function:: gcc_jit_type *\
               gcc_jit_context_get_int_type (gcc_jit_context *ctxt, \
diff --git a/gcc/jit/jit-builtins.c b/gcc/jit/jit-builtins.c
index 9063075..eabf4e4 100644
--- a/gcc/jit/jit-builtins.c
+++ b/gcc/jit/jit-builtins.c
@@ -23,17 +23,17 @@  along with GCC; see the file COPYING3.  If not see
 #include "opts.h"
 #include "tree.h"
 #include "target.h"
+#include "stringpool.h"
 
 #include "jit-common.h"
 #include "jit-builtins.h"
 #include "jit-recording.h"
+#include "jit-playback.h"
 
 namespace gcc {
 
 namespace jit {
 
-namespace recording {
-
 const char *const prefix = "__builtin_";
 const size_t prefix_len = strlen (prefix);
 
@@ -41,9 +41,12 @@  const size_t prefix_len = strlen (prefix);
 struct builtin_data
 {
   const char *name;
+  enum built_in_class fnclass;
   enum jit_builtin_type type;
   bool both_p;
   bool fallback_p;
+  enum built_in_attribute attr;
+  bool implicit_p;
 
   const char *get_asm_name () const
   {
@@ -54,8 +57,9 @@  struct builtin_data
   }
 };
 
-#define DEF_BUILTIN(X, NAME, C, TYPE, LT, BOTH_P, FALLBACK_P, NA, AT, IM, COND)\
-  {NAME, TYPE, BOTH_P, FALLBACK_P},
+#define DEF_BUILTIN(X, NAME, CLASS, TYPE, LT, BOTH_P, FALLBACK_P, \
+		    NONANSI_P, ATTRS, IMPLICIT, COND)		  \
+  {NAME, CLASS, TYPE, BOTH_P, FALLBACK_P, ATTRS, IMPLICIT},
 static const struct builtin_data builtin_data[] =
 {
 #include "builtins.def"
@@ -130,20 +134,21 @@  find_builtin_by_name (const char *in_name,
 
 // class builtins_manager
 
-/* Constructor for gcc::jit::recording::builtins_manager.  */
+/* Constructor for gcc::jit::builtins_manager.  */
 
-builtins_manager::builtins_manager (context *ctxt)
+builtins_manager::builtins_manager (recording::context *ctxt)
   : m_ctxt (ctxt)
 {
   memset (m_types, 0, sizeof (m_types));
   memset (m_builtin_functions, 0, sizeof (m_builtin_functions));
+  memset (m_attributes, 0, sizeof (m_attributes));
 }
 
 /* Locate a builtin function by name.
    Create a recording::function of the appropriate type, reusing them
    if they've already been seen.  */
 
-function *
+recording::function *
 builtins_manager::get_builtin_function (const char *name)
 {
   enum built_in_function builtin_id;
@@ -153,6 +158,16 @@  builtins_manager::get_builtin_function (const char *name)
       return NULL;
     }
 
+  return get_builtin_function_by_id (builtin_id);
+}
+
+/* Locate a builtin function by id.
+   Create a recording::function of the appropriate type, reusing them
+   if they've already been seen.  */
+
+recording::function *
+builtins_manager::get_builtin_function_by_id (enum built_in_function builtin_id)
+{
   gcc_assert (builtin_id >= 0);
   gcc_assert (builtin_id < END_BUILTINS);
 
@@ -160,7 +175,7 @@  builtins_manager::get_builtin_function (const char *name)
      the same id on a context give back the same object.  */
   if (!m_builtin_functions[builtin_id])
     {
-      function *fn = make_builtin_function (builtin_id);
+      recording::function *fn = make_builtin_function (builtin_id);
       if (fn)
 	{
 	  m_builtin_functions[builtin_id] = fn;
@@ -173,23 +188,23 @@  builtins_manager::get_builtin_function (const char *name)
 
 /* Create the recording::function for a given builtin function, by ID.  */
 
-function *
+recording::function *
 builtins_manager::make_builtin_function (enum built_in_function builtin_id)
 {
   const struct builtin_data& bd = builtin_data[builtin_id];
   enum jit_builtin_type type_id = bd.type;
-  type *t = get_type (type_id);
+  recording::type *t = get_type (type_id);
   if (!t)
     return NULL;
-  function_type *func_type = t->as_a_function_type ();
+  recording::function_type *func_type = t->as_a_function_type ();
   if (!func_type)
     return NULL;
 
-  vec<type *> param_types = func_type->get_param_types ();
+  vec<recording::type *> param_types = func_type->get_param_types ();
   recording::param **params = new recording::param *[param_types.length ()];
 
   int i;
-  type *param_type;
+  recording::type *param_type;
   FOR_EACH_VEC_ELT (param_types, i, param_type)
     {
       char buf[16];
@@ -199,24 +214,47 @@  builtins_manager::make_builtin_function (enum built_in_function builtin_id)
 				     buf);
     }
   const char *asm_name = bd.get_asm_name ();
-  function *result =
-    new function (m_ctxt,
-		  NULL,
-		  GCC_JIT_FUNCTION_IMPORTED, // FIXME
-		  func_type->get_return_type (),
-		  m_ctxt->new_string (asm_name),
-		  param_types.length (),
-		  params,
-		  func_type->is_variadic (),
-		  builtin_id);
+  recording::function *result =
+    new recording::function (m_ctxt,
+			     NULL,
+			     GCC_JIT_FUNCTION_IMPORTED, // FIXME
+			     func_type->get_return_type (),
+			     m_ctxt->new_string (asm_name),
+			     param_types.length (),
+			     params,
+			     func_type->is_variadic (),
+			     builtin_id);
   delete[] params;
+
+  /* PR/64020 - If the client code is using builtin cos or sin,
+     tree-ssa-math-opt.c's execute_cse_sincos_1 may attempt
+     to optimize them to use __builtin_cexpi; for this,
+     BUILT_IN_CEXPI needs to exist.
+
+     Hence query the cache for BUILT_IN_CEXPI to ensure it gets
+     built.  */
+  if (builtin_id == BUILT_IN_COS || builtin_id == BUILT_IN_SIN)
+    (void)get_builtin_function_by_id (BUILT_IN_CEXPI);
+
+  /* builtins.c:expand_builtin_cexpi can optimize the various
+     CEXP builtins to SINCOS builtins, and hence we may require
+     SINCOS builtins latter.
+
+     Ensure the appropriate SINCOS builtin exists.  */
+  if (builtin_id == BUILT_IN_CEXPIF)
+    (void)get_builtin_function_by_id (BUILT_IN_SINCOSF);
+  else if (builtin_id == BUILT_IN_CEXPI)
+    (void)get_builtin_function_by_id (BUILT_IN_SINCOS);
+  else if (builtin_id == BUILT_IN_CEXPIL)
+    (void)get_builtin_function_by_id (BUILT_IN_SINCOSL);
+
   return result;
 }
 
 /* Get the recording::type for a given type of builtin function,
    by ID, creating it if it doesn't already exist.  */
 
-type *
+recording::type *
 builtins_manager::get_type (enum jit_builtin_type type_id)
 {
   if (!m_types[type_id])
@@ -226,7 +264,7 @@  builtins_manager::get_type (enum jit_builtin_type type_id)
 
 /* Create the recording::type for a given type of builtin function.  */
 
-type *
+recording::type *
 builtins_manager::make_type (enum jit_builtin_type type_id)
 {
   /* Use builtin-types.def to construct a switch statement, with each
@@ -283,12 +321,15 @@  builtins_manager::make_type (enum jit_builtin_type type_id)
 #include "builtin-types.def"
 
 #undef DEF_PRIMITIVE_TYPE
+#undef DEF_FUNCTION_TYPE_0
 #undef DEF_FUNCTION_TYPE_1
 #undef DEF_FUNCTION_TYPE_2
 #undef DEF_FUNCTION_TYPE_3
 #undef DEF_FUNCTION_TYPE_4
 #undef DEF_FUNCTION_TYPE_5
 #undef DEF_FUNCTION_TYPE_6
+#undef DEF_FUNCTION_TYPE_7
+#undef DEF_FUNCTION_TYPE_8
 #undef DEF_FUNCTION_TYPE_VAR_0
 #undef DEF_FUNCTION_TYPE_VAR_1
 #undef DEF_FUNCTION_TYPE_VAR_2
@@ -307,7 +348,7 @@  builtins_manager::make_type (enum jit_builtin_type type_id)
 
    Only some types are currently supported.  */
 
-type*
+recording::type*
 builtins_manager::make_primitive_type (enum jit_builtin_type type_id)
 {
   switch (type_id)
@@ -339,9 +380,12 @@  builtins_manager::make_primitive_type (enum jit_builtin_type type_id)
     case BT_FLOAT: return m_ctxt->get_type (GCC_JIT_TYPE_FLOAT);
     case BT_DOUBLE: return m_ctxt->get_type (GCC_JIT_TYPE_DOUBLE);
     case BT_LONGDOUBLE: return m_ctxt->get_type (GCC_JIT_TYPE_LONG_DOUBLE);
-    // case BT_COMPLEX_FLOAT:
-    // case BT_COMPLEX_DOUBLE:
-    // case BT_COMPLEX_LONGDOUBLE:
+    case BT_COMPLEX_FLOAT:
+      return m_ctxt->get_type (GCC_JIT_TYPE_COMPLEX_FLOAT);
+    case BT_COMPLEX_DOUBLE:
+      return m_ctxt->get_type (GCC_JIT_TYPE_COMPLEX_DOUBLE);
+    case BT_COMPLEX_LONGDOUBLE:
+      return m_ctxt->get_type (GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE);
     case BT_PTR: return m_ctxt->get_type (GCC_JIT_TYPE_VOID_PTR);
     case BT_FILEPTR: return m_ctxt->get_type (GCC_JIT_TYPE_FILE_PTR);
     // case BT_CONST:
@@ -350,7 +394,8 @@  builtins_manager::make_primitive_type (enum jit_builtin_type type_id)
     // case BT_PTRMODE:
     // case BT_INT_PTR:
     // case BT_FLOAT_PTR:
-    // case BT_DOUBLE_PTR:
+    case BT_DOUBLE_PTR:
+      return m_ctxt->get_type (GCC_JIT_TYPE_DOUBLE)->get_pointer ();
     // case BT_CONST_DOUBLE_PTR:
     // case BT_LONGDOUBLE_PTR:
     // case BT_PID:
@@ -378,7 +423,7 @@  builtins_manager::make_primitive_type (enum jit_builtin_type type_id)
 /* Create the recording::function_type for a given function type
    signature.  */
 
-function_type *
+recording::function_type *
 builtins_manager::make_fn_type (enum jit_builtin_type,
 				enum jit_builtin_type return_type_id,
 				bool is_variadic,
@@ -386,9 +431,9 @@  builtins_manager::make_fn_type (enum jit_builtin_type,
 {
   va_list list;
   int i;
-  type **param_types = new type *[num_args];
-  type *return_type = NULL;
-  function_type *result = NULL;
+  recording::type **param_types = new recording::type *[num_args];
+  recording::type *return_type = NULL;
+  recording::function_type *result = NULL;
 
   va_start (list, num_args);
   for (i = 0; i < num_args; ++i)
@@ -417,14 +462,104 @@  builtins_manager::make_fn_type (enum jit_builtin_type,
 
 /* Handler for DEF_POINTER_TYPE within builtins_manager::make_type.  */
 
-type *
+recording::type *
 builtins_manager::make_ptr_type (enum jit_builtin_type,
 				 enum jit_builtin_type other_type_id)
 {
-  type *base_type = get_type (other_type_id);
+  recording::type *base_type = get_type (other_type_id);
   return base_type->get_pointer ();
 }
 
-} // namespace recording
+/* Playback support.  */
+
+/* A builtins_manager is associated with a recording::context
+   and might be reused for multiple compiles on various
+   playback::contexts, perhaps with different options.
+
+   Purge any playback state.  Currently this is just the table of
+   attributes.  */
+
+void
+builtins_manager::finish_playback (void)
+{
+  memset (m_attributes, 0, sizeof (m_attributes));
+}
+
+/* Get the enum built_in_class for BUILTIN_ID.  */
+
+enum built_in_class
+builtins_manager::get_class (enum built_in_function builtin_id)
+{
+  return builtin_data[builtin_id].fnclass;
+}
+
+/* Is BUILTIN_ID implicit?  */
+
+bool
+builtins_manager::implicit_p (enum built_in_function builtin_id)
+{
+  return builtin_data[builtin_id].implicit_p;
+}
+
+/* Get any attributes (in tree form) for the function declaration
+   for BUILTIN_ID.
+
+   These are created on-demand, and cached within the m_attributes
+   array, until finish_playback.  */
+
+tree
+builtins_manager::get_attrs_tree (enum built_in_function builtin_id)
+{
+  enum built_in_attribute attr = builtin_data[builtin_id].attr;
+  return get_attrs_tree (attr);
+}
+
+/* As above, but for an enum built_in_attribute.  */
+
+tree
+builtins_manager::get_attrs_tree (enum built_in_attribute attr)
+{
+  gcc_assert (attr < ATTR_LAST);
+  if (!m_attributes [attr])
+    m_attributes [attr] = make_attrs_tree (attr);
+  return m_attributes [attr];
+}
+
+/* Handle a cache-miss within the m_attributes array by
+   generating the attributes for enum built_in_attribute
+   in tree form.  */
+
+tree
+builtins_manager::make_attrs_tree (enum built_in_attribute attr)
+{
+  switch (attr)
+    {
+      /* Generate cases from builtin-attrs.def.  */
+#define DEF_ATTR_NULL_TREE(ENUM)				\
+      case ENUM: return NULL_TREE;
+#define DEF_ATTR_INT(ENUM, VALUE)				\
+      case ENUM: return build_int_cst (integer_type_node, VALUE);
+#define DEF_ATTR_STRING(ENUM, VALUE)				\
+      case ENUM: return build_string (strlen (VALUE), VALUE);
+#define DEF_ATTR_IDENT(ENUM, STRING)				\
+      case ENUM: return get_identifier (STRING);
+#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN)	\
+      case ENUM: return tree_cons (get_attrs_tree (PURPOSE),	\
+				   get_attrs_tree (VALUE),	\
+				   get_attrs_tree (CHAIN));
+#include "builtin-attrs.def"
+#undef DEF_ATTR_NULL_TREE
+#undef DEF_ATTR_INT
+#undef DEF_ATTR_IDENT
+#undef DEF_ATTR_TREE_LIST
+
+    default:
+      /* We somehow got a value not covered by the autogenerated
+	 cases.  */
+      gcc_unreachable ();
+      return NULL;
+    }
+}
+
 } // namespace jit
 } // namespace gcc
diff --git a/gcc/jit/jit-builtins.h b/gcc/jit/jit-builtins.h
index 7c46bfd..abc204d 100644
--- a/gcc/jit/jit-builtins.h
+++ b/gcc/jit/jit-builtins.h
@@ -26,8 +26,6 @@  namespace gcc {
 
 namespace jit {
 
-namespace recording {
-
 /* Create an enum of the builtin types.  */
 
 enum jit_builtin_type
@@ -71,43 +69,91 @@  enum jit_builtin_type
   BT_LAST
 }; /* enum jit_builtin_type */
 
+/* Create an enum of the attributes that can be present on builtins.  */
+
+enum built_in_attribute
+{
+#define DEF_ATTR_NULL_TREE(ENUM) ENUM,
+#define DEF_ATTR_INT(ENUM, VALUE) ENUM,
+#define DEF_ATTR_STRING(ENUM, VALUE) ENUM,
+#define DEF_ATTR_IDENT(ENUM, STRING) ENUM,
+#define DEF_ATTR_TREE_LIST(ENUM, PURPOSE, VALUE, CHAIN) ENUM,
+#include "builtin-attrs.def"
+#undef DEF_ATTR_NULL_TREE
+#undef DEF_ATTR_INT
+#undef DEF_ATTR_STRING
+#undef DEF_ATTR_IDENT
+#undef DEF_ATTR_TREE_LIST
+  ATTR_LAST
+};
+
 /***********************************************************************/
 
 class builtins_manager
 {
 public:
-  builtins_manager (context *ctxt);
+  builtins_manager (recording::context *ctxt);
 
-  function *
+  recording::function *
   get_builtin_function (const char *name);
 
+  static enum built_in_class
+  get_class (enum built_in_function builtin_id);
+
+  static bool
+  implicit_p (enum built_in_function builtin_id);
+
+  tree
+  get_attrs_tree (enum built_in_function builtin_id);
+
+  tree
+  get_attrs_tree (enum built_in_attribute attr);
+
+  void
+  finish_playback (void);
+
 private:
-  function *make_builtin_function (enum built_in_function builtin_id);
+  recording::function *
+  get_builtin_function_by_id (enum built_in_function builtin_id);
+
+  recording::function *
+  make_builtin_function (enum built_in_function builtin_id);
 
-  type *get_type (enum jit_builtin_type type_id);
+  recording::type *
+  get_type (enum jit_builtin_type type_id);
 
-  type *make_type (enum jit_builtin_type type_id);
+  recording::type *
+  make_type (enum jit_builtin_type type_id);
 
-  type*
+  recording::type*
   make_primitive_type (enum jit_builtin_type type_id);
 
-  function_type*
+  recording::function_type*
   make_fn_type (enum jit_builtin_type type_id,
 		enum jit_builtin_type return_type_id,
 		bool is_variadic,
 		int num_args, ...);
 
-  type*
+  recording::type*
   make_ptr_type (enum jit_builtin_type type_id,
 		 enum jit_builtin_type other_type_id);
 
+  tree
+  make_attrs_tree (enum built_in_attribute attr);
+
 private:
-  context *m_ctxt;
-  type *m_types[BT_LAST];
-  function *m_builtin_functions[END_BUILTINS];
+  /* Recording fields.  */
+  recording::context *m_ctxt;
+  recording::type *m_types[BT_LAST];
+  recording::function *m_builtin_functions[END_BUILTINS];
+
+  /* Playback fields.  */
+  /* m_attributes is not GTY-marked, but is only ever used from within
+     the region of playback::context::replay () in which a GC can't
+     happen.  */
+  tree m_attributes[ATTR_LAST];
 };
 
-} // namespace recording
 } // namespace jit
 } // namespace gcc
 
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 58e4a8c..c9dde3e 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -34,7 +34,7 @@  along with GCC; see the file COPYING3.  If not see
 #endif
 #endif
 
-const int NUM_GCC_JIT_TYPES = GCC_JIT_TYPE_FILE_PTR + 1;
+const int NUM_GCC_JIT_TYPES = GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE + 1;
 
 /* This comment is included by the docs.
 
@@ -97,6 +97,7 @@  namespace jit {
 
 class result;
 class dump;
+class builtins_manager; // declared within jit-builtins.h
 
 namespace recording {
 
@@ -104,7 +105,6 @@  namespace recording {
 
   /* Indentation indicates inheritance: */
   class context;
-  class builtins_manager; // declared within jit-builtins.h
   class memento;
     class string;
     class location;
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index d16b3c4..ecdae80 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -46,10 +46,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "print-tree.h"
 #include "gimplify.h"
 #include "gcc-driver-name.h"
+#include "attribs.h"
 
 #include "jit-common.h"
 #include "jit-playback.h"
 #include "jit-result.h"
+#include "jit-builtins.h"
 
 
 /* gcc::jit::playback::context::build_cast uses the convert.h API,
@@ -198,6 +200,13 @@  get_tree_node_for_type (enum gcc_jit_types type_)
 
     case GCC_JIT_TYPE_FILE_PTR:
       return fileptr_type_node;
+
+    case GCC_JIT_TYPE_COMPLEX_FLOAT:
+      return complex_float_type_node;
+    case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+      return complex_double_type_node;
+    case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
+      return complex_long_double_type_node;
     }
 
   return NULL;
@@ -399,10 +408,21 @@  new_function (location *loc,
 
   if (builtin_id)
     {
-      DECL_BUILT_IN_CLASS (fndecl) = BUILT_IN_NORMAL;
       DECL_FUNCTION_CODE (fndecl) = builtin_id;
       gcc_assert (loc == NULL);
       DECL_SOURCE_LOCATION (fndecl) = BUILTINS_LOCATION;
+
+      DECL_BUILT_IN_CLASS (fndecl) =
+	builtins_manager::get_class (builtin_id);
+      set_builtin_decl (builtin_id, fndecl,
+			builtins_manager::implicit_p (builtin_id));
+
+      builtins_manager *bm = get_builtins_manager ();
+      tree attrs = bm->get_attrs_tree (builtin_id);
+      if (attrs)
+	decl_attributes (&fndecl, attrs, ATTR_FLAG_BUILT_IN);
+      else
+	decl_attributes (&fndecl, NULL_TREE, 0);
     }
 
   if (kind != GCC_JIT_FUNCTION_IMPORTED)
@@ -1795,6 +1815,14 @@  replay ()
      refs.  Hence we must stop using them before the GC can run.  */
   m_recording_ctxt->disassociate_from_playback ();
 
+  /* The builtins_manager, if any, is associated with the recording::context
+     and might be reused for future compiles on other playback::contexts,
+     but its m_attributes array is not GTY-labeled and hence will become
+     nonsense if the GC runs.  Purge this state.  */
+  builtins_manager *bm = get_builtins_manager ();
+  if (bm)
+    bm->finish_playback ();
+
   timevar_pop (TV_JIT_REPLAY);
 
   if (!errors_occurred ())
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 25a4c28..02f08ba 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -175,6 +175,11 @@  public:
     return m_recording_ctxt->get_bool_option (opt);
   }
 
+  builtins_manager *get_builtins_manager () const
+  {
+    return m_recording_ctxt->get_builtins_manager ();
+  }
+
   result *
   compile ();
 
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index 32bf034..82ec399 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -580,6 +580,25 @@  recording::context::new_function (recording::location *loc,
   return result;
 }
 
+/* Locate the builtins_manager (if any) for this family of contexts,
+   creating it if it doesn't exist already.
+
+   All of the recording contexts in a family share one builtins_manager:
+   if we have a child context, follow the parent links to get the
+   ultimate ancestor context, and look for it/store it there.  */
+
+builtins_manager *
+recording::context::get_builtins_manager ()
+{
+  if (m_parent_ctxt)
+    return m_parent_ctxt->get_builtins_manager ();
+
+  if (!m_builtins_manager)
+    m_builtins_manager = new builtins_manager (this);
+
+  return m_builtins_manager;
+}
+
 /* Get a recording::function instance, which is lazily-created and added
    to the context's lists of mementos.
 
@@ -589,9 +608,8 @@  recording::context::new_function (recording::location *loc,
 recording::function *
 recording::context::get_builtin_function (const char *name)
 {
-  if (!m_builtins_manager)
-    m_builtins_manager = new builtins_manager (this);
-  return m_builtins_manager->get_builtin_function (name);
+  builtins_manager *bm = get_builtins_manager ();
+  return bm->get_builtin_function (name);
 }
 
 /* Create a recording::global instance and add it to this context's list
@@ -1248,6 +1266,9 @@  recording::memento_of_get_type::dereference ()
     case GCC_JIT_TYPE_FLOAT:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
+    case GCC_JIT_TYPE_COMPLEX_FLOAT:
+    case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+    case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
       /* Not a pointer: */
       return NULL;
 
@@ -1309,6 +1330,11 @@  recording::memento_of_get_type::is_int () const
 
     case GCC_JIT_TYPE_FILE_PTR:
       return false;
+
+    case GCC_JIT_TYPE_COMPLEX_FLOAT:
+    case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+    case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
+      return false;
     }
 }
 
@@ -1357,6 +1383,11 @@  recording::memento_of_get_type::is_float () const
 
     case GCC_JIT_TYPE_FILE_PTR:
       return false;
+
+    case GCC_JIT_TYPE_COMPLEX_FLOAT:
+    case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+    case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
+      return true;
     }
 }
 
@@ -1405,6 +1436,11 @@  recording::memento_of_get_type::is_bool () const
 
     case GCC_JIT_TYPE_FILE_PTR:
       return false;
+
+    case GCC_JIT_TYPE_COMPLEX_FLOAT:
+    case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+    case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
+      return false;
     }
 }
 
@@ -1451,7 +1487,11 @@  static const char * const get_type_strings[] = {
 
   "size_t",  /* GCC_JIT_TYPE_SIZE_T */
 
-  "FILE *"  /* GCC_JIT_TYPE_FILE_PTR */
+  "FILE *",  /* GCC_JIT_TYPE_FILE_PTR */
+
+  "complex float", /* GCC_JIT_TYPE_COMPLEX_FLOAT */
+  "complex double", /* GCC_JIT_TYPE_COMPLEX_DOUBLE */
+  "complex long double"  /* GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE */
 
 };
 
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 1b0ef91..31fb304 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -52,6 +52,9 @@  public:
   context (context *parent_ctxt);
   ~context ();
 
+  builtins_manager *
+  get_builtins_manager ();
+
   void record (memento *m);
   void replay_into (replayer *r);
   void disassociate_from_playback ();
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index e07002d..ed6390e 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -382,7 +382,13 @@  enum gcc_jit_types
   GCC_JIT_TYPE_SIZE_T,
 
  /* C type: (FILE *)  */
-  GCC_JIT_TYPE_FILE_PTR
+  GCC_JIT_TYPE_FILE_PTR,
+
+  /* Complex numbers.  */
+  GCC_JIT_TYPE_COMPLEX_FLOAT,
+  GCC_JIT_TYPE_COMPLEX_DOUBLE,
+  GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE
+
 };
 
 extern gcc_jit_type *