[1/2] jit: Add support for complex types

Message ID d59a1ed3fef447b9b77a72ae9c77f555@kth.se
State New
Delegated to: David Malcolm
Headers
Series jit: Add support for complex types |

Commit Message

Petter Tomner Sept. 14, 2021, 11:37 p.m. UTC
  From ff47bbec5a833b4470cae7cb636a5fbf31c6432e Mon Sep 17 00:00:00 2001
From: Petter Tomner <tomner@kth.se>
Date: Tue, 14 Sep 2021 23:51:41 +0200
Subject: [PATCH 1/2] jit: Support for complex types

The patch adds support of complex floating point types to libgccjit.
A new binary operator 'COMPLEX' is added to create complex values
from real values. Aswell as a function to create a complex double
literal.

To notify users if a binary linking to libgccjit depends on complex
types, complex type support most be enabled with a new option function.

Signed-off-by:
2021-09-15	Petter Tomner	<tomner@kth.se>

gcc/jit/
	* jit-common.h (INNER_BOOL_OPTION_ENABLE_COMPLEX_TYPES): new
	* jit-playback.c Create imaginary literals, conversions
	* jit-recording.c Reproducer, debug strings, forwarding
	* jit-recording.h
	  (float_size_qual): Poll size qualifier of float types
	  (is_complex): Poll if type is complex
	* libgccjit++.h New entrypoints, see below
	* libgccjit.c Implementation of new entry points
	  (gcc_jit_context_get_type): Extend range check, validation
	  (valid_binary_op_p): Extend range check
	  (gcc_jit_context_new_binary_op): Validation
	  (gcc_jit_context_new_unary_op): Validation
	  (gcc_jit_context_new_comparison): Validation
	* libgccjit.h
	  (gcc_jit_context_new_rvalue_from_complex_double): New
	  (GCC_JIT_BINARY_OP_COMPLEX): New
	  (LIBGCCJIT_HAVE_COMPLEX): New
	  (gcc_jit_context_set_bool_enable_complex_types): New
	* libgccjit.map Added new entrypoints
	  (LIBGCCJIT_ABI_16): New
---
 gcc/jit/jit-common.h    |   1 +
 gcc/jit/jit-playback.c  | 165 ++++++++++++++++++++++++++++++++++++++--
 gcc/jit/jit-recording.c | 116 +++++++++++++++++++++++++++-
 gcc/jit/jit-recording.h |  28 +++++++
 gcc/jit/libgccjit++.h   |  23 +++++-
 gcc/jit/libgccjit.c     |  97 ++++++++++++++++++++++-
 gcc/jit/libgccjit.h     |  45 ++++++++++-
 gcc/jit/libgccjit.map   |   6 ++
 8 files changed, 468 insertions(+), 13 deletions(-)
  

Patch

diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index f88e6755b00..bc0453fba05 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -198,6 +198,7 @@  enum inner_bool_option
 {
   INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS,
   INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER,
+  INNER_BOOL_OPTION_ENABLE_COMPLEX_TYPES,
 
   NUM_INNER_BOOL_OPTIONS
 };
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 59399dee251..8e4971958c3 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gcc.h"
 #include "diagnostic.h"
 #include "stmt.h"
+#include "complex.h"
 
 #include <pthread.h>
 
@@ -78,6 +79,8 @@  convert (tree dst_type, tree expr)
     case INTEGER_TYPE:
     case ENUMERAL_TYPE:
       return fold (convert_to_integer (dst_type, expr));
+    case REAL_TYPE:
+      return fold (convert_to_real (dst_type, expr));
 
     default:
       gcc_assert (gcc::jit::active_playback_ctxt);
@@ -679,6 +682,24 @@  new_global_initialized (location *loc,
 namespace playback
 {
 
+/* Return the corrensponding not complex float type of a
+   float type. I.e. complex float -> float */
+static tree
+complex_real_to_real (tree complex_type)
+{
+  if (TYPE_CANONICAL (complex_type) ==
+            TYPE_CANONICAL (complex_float_type_node))
+    return float_type_node;
+  else if (TYPE_CANONICAL (complex_type) ==
+	    TYPE_CANONICAL (complex_double_type_node))
+    return double_type_node;
+  else if (TYPE_CANONICAL (complex_type) ==
+	    TYPE_CANONICAL (complex_long_double_type_node))
+    return long_double_type_node;
+  else
+    gcc_unreachable();
+}
+
 /* Specialization of making an rvalue from a const, for host <int>.  */
 
 template <>
@@ -694,6 +715,25 @@  new_rvalue_from_const <int> (type *type,
       tree inner = build_int_cst (inner_type, value);
       return new rvalue (this, inner);
     }
+  else if (COMPLEX_FLOAT_TYPE_P (inner_type))
+    {
+      tree tree_real;
+      tree tree_imag;
+      tree real_type;
+      REAL_VALUE_TYPE real_value;
+      REAL_VALUE_TYPE imag_value;
+
+      real_type = complex_real_to_real (inner_type);
+
+      real_from_integer (&real_value, VOIDmode, value, SIGNED);
+      real_from_integer (&imag_value, VOIDmode, 0, SIGNED);
+
+      tree_real = build_real (real_type, real_value);
+      tree_imag = build_real (real_type, imag_value);
+
+      tree inner = build_complex (inner_type, tree_real, tree_imag);
+      return new rvalue (this, inner);
+    }
   else
     {
       REAL_VALUE_TYPE real_value;
@@ -718,6 +758,25 @@  new_rvalue_from_const <long> (type *type,
       tree inner = build_int_cst (inner_type, value);
       return new rvalue (this, inner);
     }
+  else if (COMPLEX_FLOAT_TYPE_P (inner_type))
+    {
+      tree tree_real;
+      tree tree_imag;
+      tree real_type;
+      REAL_VALUE_TYPE real_value;
+      REAL_VALUE_TYPE imag_value;
+
+      real_type = complex_real_to_real (inner_type);
+
+      real_from_integer (&real_value, VOIDmode, value, SIGNED);
+      real_from_integer (&imag_value, VOIDmode, 0, SIGNED);
+
+      tree_real = build_real (real_type, real_value);
+      tree_imag = build_real (real_type, imag_value);
+
+      tree inner = build_complex (inner_type, tree_real, tree_imag);
+      return new rvalue (this, inner);
+    }
   else
     {
       REAL_VALUE_TYPE real_value;
@@ -743,19 +802,104 @@  new_rvalue_from_const <double> (type *type,
      real.c:real_from_target appears to require the representation to be
      split into 32-bit values, and then sent as an pair of host long
      ints.  */
+
+  union
+  {
+    double as_double;
+    uint32_t as_uint32s[2];
+  } u_real, u_imag;
+
+  u_real.as_double = value;
+  long int as_long_ints[2];
+  as_long_ints[0] = u_real.as_uint32s[0];
+  as_long_ints[1] = u_real.as_uint32s[1];
+
   REAL_VALUE_TYPE real_value;
+  real_from_target (&real_value, as_long_ints, DFmode);
+
+  if (COMPLEX_FLOAT_TYPE_P (inner_type))
+    {
+      tree tree_real;
+      tree tree_imag;
+      tree real_type;
+
+      REAL_VALUE_TYPE imag_value;
+
+      long int zero_as_long_ints[2];
+      u_imag.as_double = 0.;
+      zero_as_long_ints[0] = u_imag.as_uint32s[0];
+      zero_as_long_ints[1] = u_imag.as_uint32s[1];
+
+      real_from_target (&imag_value, zero_as_long_ints, DFmode);
+
+      real_type = complex_real_to_real (inner_type);
+
+      tree_real = build_real (real_type, real_value);
+      tree_imag = build_real (real_type, imag_value);
+
+      tree inner = build_complex (inner_type, tree_real, tree_imag);
+      return new rvalue (this, inner);
+    }
+  else
+    {
+      tree inner = build_real (inner_type, real_value);
+      return new rvalue (this, inner);
+    }
+}
+
+/* Specialization of making an rvalue from a const,
+   for host <double _Complex>. */
+
+template <>
+rvalue *
+context::
+new_rvalue_from_const <_Complex double> (type *type,
+                _Complex double  value)
+{
+  tree inner_type = type->as_tree ();
+
   union
   {
     double as_double;
     uint32_t as_uint32s[2];
-  } u;
-  u.as_double = value;
+  } u_real, u_imag;
+
+  u_real.as_double = creal(value);
   long int as_long_ints[2];
-  as_long_ints[0] = u.as_uint32s[0];
-  as_long_ints[1] = u.as_uint32s[1];
+  as_long_ints[0] = u_real.as_uint32s[0];
+  as_long_ints[1] = u_real.as_uint32s[1];
+
+  REAL_VALUE_TYPE real_value;
   real_from_target (&real_value, as_long_ints, DFmode);
-  tree inner = build_real (inner_type, real_value);
-  return new rvalue (this, inner);
+
+  if (COMPLEX_FLOAT_TYPE_P (inner_type))
+    {
+      tree tree_real;
+      tree tree_imag;
+      tree real_type;
+
+      REAL_VALUE_TYPE imag_value;
+
+      long int value_as_long_ints[2];
+      u_imag.as_double = cimag(value);
+      value_as_long_ints[0] = u_imag.as_uint32s[0];
+      value_as_long_ints[1] = u_imag.as_uint32s[1];
+
+      real_from_target (&imag_value, value_as_long_ints, DFmode);
+
+      real_type = complex_real_to_real (inner_type);
+
+      tree_real = build_real (real_type, real_value);
+      tree_imag = build_real (real_type, imag_value);
+
+      tree inner = build_complex (inner_type, tree_real, tree_imag);
+      return new rvalue (this, inner);
+    }
+  else
+    {
+      tree inner = build_real (inner_type, real_value);
+      return new rvalue (this, inner);
+    }
 }
 
 /* Specialization of making an rvalue from a const, for host <void *>.  */
@@ -985,6 +1129,11 @@  new_binary_op (location *loc,
     case GCC_JIT_BINARY_OP_RSHIFT:
       inner_op = RSHIFT_EXPR;
       break;
+
+    /* Create complex value from two real values */
+    case GCC_JIT_BINARY_OP_COMPLEX:
+      inner_op = COMPLEX_EXPR;
+      break;
     }
 
   tree inner_expr = build2 (inner_op,
@@ -1163,6 +1312,10 @@  playback::context::build_cast (playback::location *loc,
 		      build_int_cst (TREE_TYPE (t_expr), 0));
       goto maybe_fold;
 
+    case COMPLEX_TYPE:
+      t_ret = convert_to_complex (t_dst_type, t_expr);
+      goto maybe_fold;
+
     case REAL_TYPE:
       t_ret = convert_to_real (t_dst_type, t_expr);
       goto maybe_fold;
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index 117ff70114c..565d078d517 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -26,6 +26,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "toplev.h"
 
 #include <pthread.h>
+#include <complex.h>
 
 #include "jit-builtins.h"
 #include "jit-recording.h"
@@ -1609,7 +1610,8 @@  static const char * const
 static const char * const
  inner_bool_option_reproducer_strings[NUM_INNER_BOOL_OPTIONS] = {
   "gcc_jit_context_set_bool_allow_unreachable_blocks",
-  "gcc_jit_context_set_bool_use_external_driver"
+  "gcc_jit_context_set_bool_use_external_driver",
+  "gcc_jit_context_set_bool_enable_complex_types"
 };
 
 /* Write the current value of all options to the log file (if any).  */
@@ -2423,6 +2425,59 @@  recording::memento_of_get_type::is_float () const
     }
 }
 
+/* Implementation of pure virtual hook recording::type::is_complex for
+   recording::memento_of_get_type.  */
+
+bool
+recording::memento_of_get_type::is_complex () const
+{
+  switch (m_kind)
+    {
+    default: gcc_unreachable ();
+
+    case GCC_JIT_TYPE_VOID:
+      return false;
+
+    case GCC_JIT_TYPE_VOID_PTR:
+      return false;
+
+    case GCC_JIT_TYPE_BOOL:
+      return false;
+
+    case GCC_JIT_TYPE_CHAR:
+    case GCC_JIT_TYPE_SIGNED_CHAR:
+    case GCC_JIT_TYPE_UNSIGNED_CHAR:
+    case GCC_JIT_TYPE_SHORT:
+    case GCC_JIT_TYPE_UNSIGNED_SHORT:
+    case GCC_JIT_TYPE_INT:
+    case GCC_JIT_TYPE_UNSIGNED_INT:
+    case GCC_JIT_TYPE_LONG:
+    case GCC_JIT_TYPE_UNSIGNED_LONG:
+    case GCC_JIT_TYPE_LONG_LONG:
+    case GCC_JIT_TYPE_UNSIGNED_LONG_LONG:
+      return false;
+
+    case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_DOUBLE:
+    case GCC_JIT_TYPE_LONG_DOUBLE:
+      return false;
+
+    case GCC_JIT_TYPE_CONST_CHAR_PTR:
+      return false;
+
+    case GCC_JIT_TYPE_SIZE_T:
+      return false;
+
+    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;
+    }
+}
+
 /* Implementation of pure virtual hook recording::type::is_bool for
    recording::memento_of_get_type.  */
 
@@ -4704,6 +4759,7 @@  recording::global::write_reproducer (reproducer &r)
 template class recording::memento_of_new_rvalue_from_const <int>;
 template class recording::memento_of_new_rvalue_from_const <long>;
 template class recording::memento_of_new_rvalue_from_const <double>;
+template class recording::memento_of_new_rvalue_from_const <_Complex double>;
 template class recording::memento_of_new_rvalue_from_const <void *>;
 
 /* Implementation of the pure virtual hook recording::memento::replay_into
@@ -4881,6 +4937,51 @@  recording::memento_of_new_rvalue_from_const <double>::write_reproducer (reproduc
     m_value);
 }
 
+/* The make_debug_string specialization for <double complex>, rendering it as
+     (TARGET_TYPE)LITERAL
+   e.g.
+     "(complex float)(42.0+42.0j)".  */
+
+template <>
+string *
+memento_of_new_rvalue_from_const <_Complex double>::make_debug_string ()
+{
+  double real = creal(m_value);
+  double imag = cimag(m_value);
+  return string::from_printf (m_ctxt,
+                  "(%s)(%g%+gj)",
+                  m_type->get_debug_string (),
+                  real, imag);
+}
+
+/* The get_wide_int specialization for <double complex>.  */
+
+template <>
+bool
+memento_of_new_rvalue_from_const <_Complex double>::get_wide_int (wide_int *) const
+{
+  return false;
+}
+
+/* The write_reproducer specialization for <double>.  */
+
+template <>
+void
+recording::memento_of_new_rvalue_from_const <_Complex double>::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "rvalue");
+  double real = creal(m_value);
+  double imag = cimag(m_value);
+  r.write ("  gcc_jit_rvalue *%s =\n"
+    "    gcc_jit_context_new_rvalue_from_complex_double (%s, /* gcc_jit_context *ctxt */\n"
+    "                                            %s, /* gcc_jit_type *numeric_type */\n"
+    "                                            %f+%fj); /* double value */\n",
+    id,
+    r.get_identifier (get_context ()),
+    r.get_identifier_as_type (m_type),
+    real, imag);
+}
+
 /* The make_debug_string specialization for <void *>, rendering it as
      (TARGET_TYPE)HEX
    e.g.
@@ -5178,13 +5279,20 @@  static const char * const binary_op_strings[] = {
   "||", /* GCC_JIT_BINARY_OP_LOGICAL_OR */
   "<<", /* GCC_JIT_BINARY_OP_LSHIFT */
   ">>", /* GCC_JIT_BINARY_OP_RSHIFT */
+  "", /* GCC_JIT_BINARY_OP_COMPLEX - dummy not used */
 };
 
 recording::string *
 recording::binary_op::make_debug_string ()
 {
   enum precedence prec = get_precedence ();
-  return string::from_printf (m_ctxt,
+  if (m_op == GCC_JIT_BINARY_OP_COMPLEX)
+    return string::from_printf (m_ctxt,
+			      "(%s + %s * _Imaginary_I)",
+			      m_a->get_debug_string_parens (prec),
+			      m_b->get_debug_string_parens (prec));
+  else
+    return string::from_printf (m_ctxt,
 			      "%s %s %s",
 			      m_a->get_debug_string_parens (prec),
 			      binary_op_strings[m_op],
@@ -5203,7 +5311,8 @@  const char * const binary_op_reproducer_strings[] = {
   "GCC_JIT_BINARY_OP_LOGICAL_AND",
   "GCC_JIT_BINARY_OP_LOGICAL_OR",
   "GCC_JIT_BINARY_OP_LSHIFT",
-  "GCC_JIT_BINARY_OP_RSHIFT"
+  "GCC_JIT_BINARY_OP_RSHIFT",
+  "GCC_JIT_BINARY_OP_COMPLEX",
 };
 
 /* Implementation of recording::memento::write_reproducer for binary ops.  */
@@ -5244,6 +5353,7 @@  static const enum precedence binary_op_precedence[] = {
   PRECEDENCE_LOGICAL_OR, /* GCC_JIT_BINARY_OP_LOGICAL_OR */
   PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_LSHIFT */
   PRECEDENCE_SHIFT, /* GCC_JIT_BINARY_OP_RSHIFT */
+  PRECEDENCE_CAST, /* GCC_JIT_BINARY_OP_COMPLEX */
 };
 } /* namespace recording */
 
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 03fa1160cf0..d5e0c359a48 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -544,10 +544,14 @@  public:
   virtual bool is_int () const = 0;
   virtual bool is_float () const = 0;
   virtual bool is_bool () const = 0;
+  virtual bool is_complex () const = 0;
   virtual type *is_pointer () = 0;
   virtual type *is_array () = 0;
   virtual bool is_void () const { return false; }
   virtual bool has_known_size () const { return true; }
+  /* Used to pair complex float types with real float type
+     of the same "base type". */
+  virtual int float_size_qual () const { return 0; }
 
   bool is_numeric () const
   {
@@ -598,9 +602,28 @@  public:
     return type::accepts_writes_from (rtype);
   }
 
+  int float_size_qual () const FINAL OVERRIDE
+  {
+    switch (m_kind)
+      {
+      default:
+	return 0;
+      case GCC_JIT_TYPE_FLOAT:
+      case GCC_JIT_TYPE_COMPLEX_FLOAT:
+	return 1;
+      case GCC_JIT_TYPE_DOUBLE:
+      case GCC_JIT_TYPE_COMPLEX_DOUBLE:
+	return 2;
+      case GCC_JIT_TYPE_LONG_DOUBLE:
+      case GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE:
+	return 3;
+      }
+  }
+
   bool is_int () const FINAL OVERRIDE;
   bool is_float () const FINAL OVERRIDE;
   bool is_bool () const FINAL OVERRIDE;
+  bool is_complex () const FINAL OVERRIDE;
   type *is_pointer () FINAL OVERRIDE { return dereference (); }
   type *is_array () FINAL OVERRIDE { return NULL; }
   bool is_void () const FINAL OVERRIDE { return m_kind == GCC_JIT_TYPE_VOID; }
@@ -635,6 +658,7 @@  public:
   bool is_int () const FINAL OVERRIDE { return false; }
   bool is_float () const FINAL OVERRIDE { return false; }
   bool is_bool () const FINAL OVERRIDE { return false; }
+  bool is_complex () const FINAL OVERRIDE { return false; }
   type *is_pointer () FINAL OVERRIDE { return m_other_type; }
   type *is_array () FINAL OVERRIDE { return NULL; }
 
@@ -661,6 +685,7 @@  public:
   bool is_int () const FINAL OVERRIDE { return m_other_type->is_int (); }
   bool is_float () const FINAL OVERRIDE { return m_other_type->is_float (); }
   bool is_bool () const FINAL OVERRIDE { return m_other_type->is_bool (); }
+  bool is_complex () const FINAL OVERRIDE { return m_other_type->is_complex (); }
   type *is_pointer () FINAL OVERRIDE { return m_other_type->is_pointer (); }
   type *is_array () FINAL OVERRIDE { return m_other_type->is_array (); }
 
@@ -771,6 +796,7 @@  class array_type : public type
   bool is_int () const FINAL OVERRIDE { return false; }
   bool is_float () const FINAL OVERRIDE { return false; }
   bool is_bool () const FINAL OVERRIDE { return false; }
+  bool is_complex () const FINAL OVERRIDE { return false; }
   type *is_pointer () FINAL OVERRIDE { return NULL; }
   type *is_array () FINAL OVERRIDE { return m_element_type; }
   int num_elements () { return m_num_elements; }
@@ -805,6 +831,7 @@  public:
   bool is_int () const FINAL OVERRIDE { return false; }
   bool is_float () const FINAL OVERRIDE { return false; }
   bool is_bool () const FINAL OVERRIDE { return false; }
+  bool is_complex () const FINAL OVERRIDE { return false; }
   type *is_pointer () FINAL OVERRIDE { return NULL; }
   type *is_array () FINAL OVERRIDE { return NULL; }
 
@@ -918,6 +945,7 @@  public:
   bool is_int () const FINAL OVERRIDE { return false; }
   bool is_float () const FINAL OVERRIDE { return false; }
   bool is_bool () const FINAL OVERRIDE { return false; }
+  bool is_complex () const FINAL OVERRIDE { return false; }
   type *is_pointer () FINAL OVERRIDE { return NULL; }
   type *is_array () FINAL OVERRIDE { return NULL; }
 
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 82831ff5da0..6be5a9b9cc3 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -128,7 +128,8 @@  namespace gccjit
 
     void set_bool_allow_unreachable_blocks (int bool_value);
     void set_bool_use_external_driver (int bool_value);
-
+    void set_bool_enable_complex_types (int bool_value);
+    
     void add_command_line_option (const char *optname);
     void add_driver_option (const char *optname);
 
@@ -191,6 +192,8 @@  namespace gccjit
     rvalue one (type numeric_type) const;
     rvalue new_rvalue (type numeric_type,
 		       double value) const;
+    rvalue new_rvalue (type numeric_type,
+		       _Complex double value) const;
     rvalue new_rvalue (type pointer_type,
 		       void *value) const;
     rvalue new_rvalue (const std::string &value) const;
@@ -732,6 +735,13 @@  context::set_bool_use_external_driver (int bool_value)
 						bool_value);
 }
 
+inline void
+context::set_bool_enable_complex_types (int bool_value)
+{
+  gcc_jit_context_set_bool_enable_complex_types (m_inner_ctxt,
+						 bool_value);
+}
+
 inline void
 context::add_command_line_option (const char *optname)
 {
@@ -950,6 +960,17 @@  context::new_rvalue (type numeric_type,
 					    value));
 }
 
+inline rvalue
+context::new_rvalue (type numeric_type,
+		     _Complex double value) const
+{
+  return rvalue (
+    gcc_jit_context_new_rvalue_from_complex_double (
+      m_inner_ctxt,
+      numeric_type.get_inner_type (),
+      value));
+}
+
 inline rvalue
 context::new_rvalue (type pointer_type,
 		     void *value) const
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 7fa948007ad..0859b38f609 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -448,9 +448,17 @@  gcc_jit_context_get_type (gcc_jit_context *ctxt,
   JIT_LOG_FUNC (ctxt->get_logger ());
   RETURN_NULL_IF_FAIL_PRINTF1 (
     (type >= GCC_JIT_TYPE_VOID
-     && type <= GCC_JIT_TYPE_FILE_PTR),
+     && type <= GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE),
     ctxt, NULL,
     "unrecognized value for enum gcc_jit_types: %i", type);
+  if (type >= GCC_JIT_TYPE_COMPLEX_FLOAT
+      && type <= GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE)
+    RETURN_NULL_IF_FAIL (
+      ctxt->get_inner_bool_option(
+        gcc::jit::INNER_BOOL_OPTION_ENABLE_COMPLEX_TYPES),
+      ctxt, NULL,
+      "complex type are only available after enabling them with"
+      " gcc_jit_context_set_bool_enable_complex_types()");
 
   return (gcc_jit_type *)ctxt->get_type (type);
 }
@@ -1320,6 +1328,26 @@  gcc_jit_context_new_rvalue_from_double (gcc_jit_context *ctxt,
 	  ->new_rvalue_from_const <double> (numeric_type, value));
 }
 
+gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_from_complex_double (gcc_jit_context *ctxt,
+                    gcc_jit_type *numeric_type,
+                    double _Complex value)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
+
+  RETURN_NULL_IF_FAIL_PRINTF1 (
+    numeric_type->is_float (),
+    ctxt, NULL,
+    "not a floating point type (type: %s)",
+    numeric_type->get_debug_string ());
+
+  return ((gcc_jit_rvalue *)ctxt
+      ->new_rvalue_from_const <double _Complex> (numeric_type, value));
+}
+
+
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
@@ -1415,6 +1443,14 @@  gcc_jit_context_new_unary_op (gcc_jit_context *ctxt,
     result_type->get_debug_string ());
   RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
 
+  if (rvalue->get_type ()->is_complex ())
+    RETURN_NULL_IF_FAIL_PRINTF2 (
+      op == GCC_JIT_UNARY_OP_MINUS,
+      ctxt, loc,
+      "only unary minus defined for complex types: %s (type: %s)",
+      rvalue->get_debug_string (),
+      rvalue->get_type ()->get_debug_string ());
+
   return (gcc_jit_rvalue *)ctxt->new_unary_op (loc, op, result_type, rvalue);
 }
 
@@ -1426,7 +1462,7 @@  static bool
 valid_binary_op_p (enum gcc_jit_binary_op op)
 {
   return (op >= GCC_JIT_BINARY_OP_PLUS
-	  && op <= GCC_JIT_BINARY_OP_RSHIFT);
+	  && op <= GCC_JIT_BINARY_OP_COMPLEX);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -1470,6 +1506,38 @@  gcc_jit_context_new_binary_op (gcc_jit_context *ctxt,
     a->get_debug_string (), b->get_debug_string (),
     result_type->get_debug_string ());
 
+  if (op == GCC_JIT_BINARY_OP_COMPLEX)
+    {
+      RETURN_NULL_IF_FAIL (
+       result_type->is_complex (),
+       ctxt, loc,
+       "return type is not complex");
+      RETURN_NULL_IF_FAIL_PRINTF3 (
+	!a->get_type ()->is_complex () &&
+	a->get_type ()->is_float (),
+	ctxt, loc,
+	"first operand of %s is not real: %s (type: %s)",
+	gcc::jit::binary_op_reproducer_strings[op],
+	a->get_debug_string (),
+	a->get_type ()->get_debug_string ());
+      RETURN_NULL_IF_FAIL_PRINTF3 (
+	!b->get_type ()->is_complex () &&
+	b->get_type ()->is_float (),
+	ctxt, loc,
+	"2nd operand of %s is not real: %s (type: %s)",
+	gcc::jit::binary_op_reproducer_strings[op],
+	b->get_debug_string (),
+	b->get_type ()->get_debug_string ());
+      RETURN_NULL_IF_FAIL_PRINTF2 (
+        a->get_type ()-> float_size_qual () ==
+        result_type->float_size_qual (),
+	ctxt, loc,
+	"size qualifiers of complex operand's not the same as the result:"
+	" operands: %s, result:  %s",
+	a->get_type ()->get_debug_string (),
+	result_type->get_debug_string ());
+    }
+
   return (gcc_jit_rvalue *)ctxt->new_binary_op (loc, op, result_type, a, b);
 }
 
@@ -1506,6 +1574,14 @@  gcc_jit_context_new_comparison (gcc_jit_context *ctxt,
     b->get_debug_string (),
     b->get_type ()->get_debug_string ());
 
+  if (a->get_type ()->is_complex())
+    RETURN_NULL_IF_FAIL_PRINTF1 (
+     (op == GCC_JIT_COMPARISON_EQ
+      || op == GCC_JIT_COMPARISON_NE),
+     ctxt, loc,
+     "invalid enum gcc_jit_comparison for complex operands: %i",
+     op);
+
   return (gcc_jit_rvalue *)ctxt->new_comparison (loc, op, a, b);
 }
 
@@ -2735,6 +2811,23 @@  gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt,
     bool_value);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::set_inner_bool_option method in
+   jit-recording.c. */
+
+void
+gcc_jit_context_set_bool_enable_complex_types (gcc_jit_context *ctxt,
+						   int bool_value)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  ctxt->set_inner_bool_option (
+    gcc::jit::INNER_BOOL_OPTION_ENABLE_COMPLEX_TYPES,
+    bool_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 5c722c2c57f..f3d2c40d8be 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -848,6 +848,17 @@  gcc_jit_context_new_rvalue_from_double (gcc_jit_context *ctxt,
 					gcc_jit_type *numeric_type,
 					double value);
 
+/* Complex floating-point constants.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_16; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_COMPLEX
+*/
+extern gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_from_complex_double (gcc_jit_context *ctxt,
+					gcc_jit_type *numeric_type,
+					double _Complex value);
+
 /* Pointers.  */
 extern gcc_jit_rvalue *
 gcc_jit_context_new_rvalue_from_ptr (gcc_jit_context *ctxt,
@@ -959,7 +970,26 @@  enum gcc_jit_binary_op
   /* Right shift; analogous to:
        (EXPR_A) >> (EXPR_B)
      in C.  */
-  GCC_JIT_BINARY_OP_RSHIFT
+  GCC_JIT_BINARY_OP_RSHIFT,
+
+  /* Create a complex floating point value from
+     two real floating point values. The operands
+     and the result need to have the same size
+     qualifier.
+
+     Analogous to:
+       CMPLX (real,imag)
+     in C.
+
+     The first operand is the real part, the other
+     the imaginary. Negative zeroes are preserved
+     and Inf:s do not lead to NaNs.
+
+     This operator  was added in LIBGCCJIT_ABI_16;
+     you can test for its presence using
+       #ifdef LIBGCCJIT_HAVE_COMPLEX
+  */
+  GCC_JIT_BINARY_OP_COMPLEX
 };
 
 extern gcc_jit_rvalue *
@@ -1434,6 +1464,19 @@  gcc_jit_timer_print (gcc_jit_timer *timer,
 		     FILE *f_out);
 
 
+#define LIBGCCJIT_HAVE_COMPLEX
+
+/* Enables complex type support in libgccjit.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_16; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_COMPLEX
+*/
+
+extern void
+gcc_jit_context_set_bool_enable_complex_types (gcc_jit_context *ctxt,
+					       int bool_value);
+
 #define LIBGCCJIT_HAVE_gcc_jit_rvalue_set_bool_require_tail_call
 
 /* Mark/clear a call as needing tail-call optimization.
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 337ea6c7fe4..c9b044dc8f5 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -205,3 +205,9 @@  LIBGCCJIT_ABI_15 {
     gcc_jit_extended_asm_add_clobber;
     gcc_jit_context_add_top_level_asm;
 } LIBGCCJIT_ABI_14;
+
+LIBGCCJIT_ABI_16 {
+  global:
+    gcc_jit_context_new_rvalue_from_complex_double;
+    gcc_jit_context_set_bool_enable_complex_types;
+} LIBGCCJIT_ABI_15;
\ No newline at end of file