public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
From: Antoni Boucher <bouanto@zoho.com>
To: jit@gcc.gnu.org, gcc-patches@gcc.gnu.org
Cc: David Malcolm <dmalcolm@redhat.com>
Subject: Re: [PATCH] libgccjit: Add support for machine-dependent builtins
Date: Sat, 11 Feb 2023 19:32:19 -0500	[thread overview]
Message-ID: <75551971f85c70f8e3df8584d81ced627e293177.camel@zoho.com> (raw)
In-Reply-To: <184790403bd8227db4054b9e672afbfdf879dc0c.camel@zoho.com>

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

I forgot to attach the patch.
Here it is.

On Sat, 2023-02-11 at 19:30 -0500, Antoni Boucher via Jit wrote:
> Hi.
> This patch adds support for machine-dependent builtins in libgccjit
> (bug 108762).
> 
> There are two things I don't like in this patch:
> 
>  1. There are a few functions copied from the C frontend
> (common_mark_addressable_vec and a few others).
> 
>  2. Getting a target builtin only works from the second compilation
> since the type information is recorded at the first compilation. I
> couldn't find a way to get the builtin data without using the
> langhook.
> It is necessary to get the type information for type checking and
> instrospection.
> 
> Any idea how to fix these issues?
> 
> Thanks for the review.


[-- Attachment #2: 0001-libgccjit-Add-support-for-machine-dependent-builtins.patch --]
[-- Type: text/x-patch, Size: 60738 bytes --]

From f1f4a113853b416776253b596d7feeb87e608bfd Mon Sep 17 00:00:00 2001
From: Antoni Boucher <bouanto@zoho.com>
Date: Mon, 23 Jan 2023 17:21:15 -0500
Subject: [PATCH] libgccjit: Add support for machine-dependent builtins

gcc/config:
	PR jit/108762
	* i386/i386-builtins.cc: New function (clear_builtin_types).

gcc/jit:
	PR jit/108762
	* docs/topics/compatibility.rst (LIBGCCJIT_ABI_25): New ABI tag.
	* docs/topics/expressions.rst: Add documentation for the
	functions gcc_jit_context_new_rvalue_vector_perm and
	gcc_jit_context_new_vector_access.
	* docs/topics/functions.rst: Add documentation for the function
	gcc_jit_context_get_target_builtin_function.
	* docs/topics/types.rst: Add documentation for GCC_JIT_TYPE_BFLOAT16.
	* dummy-frontend.cc: Include headers target.h, jit-recording.h,
	print-tree.h, unordered_map and string, new variables (target_builtins,
	target_function_types, and target_builtins_ctxt), new function
	(tree_type_to_jit_type).
	* jit-builtins.cc: Specify that the function types are not from
	target builtins.
	* jit-playback.cc: New argument is_target_builtin to new_function, new
	functions (new_rvalue_vector_perm, common_mark_addressable_vec,
	gnu_vector_type_p, lvalue_p, convert_vector_to_array_for_subscript,
	new_vector_access).
	* jit-playback.h: New argument is_target_builtin to
	new_function, new functions (new_rvalue_vector_perm,
	new_vector_access).
	* jit-recording.cc: New argument is_target_builtin to
	new_function_type, function_type constructor and function
	constructor, new functions
	(get_target_builtin_function, new_rvalue_vector_perm,
	new_vector_access, memento_of_new_rvalue_vector_perm,
	memento_of_new_rvalue_vector_perm::replay_into,
	memento_of_new_rvalue_vector_perm::visit_children,
	memento_of_new_rvalue_vector_perm::make_debug_string,
	memento_of_new_rvalue_vector_perm::write_reproducer,
	vector_access::replay_into, vector_access::visit_children,
	vector_access::make_debug_string, vector_access::write_reproducer).
	* jit-recording.h: Include headers string and unordered_map, new
	variable target_function_types, new argument is_target_builtin
	to new_function_type, function_type and function, new classes
	memento_of_new_rvalue_vector_perm and vector_access, new functions
	(get_target_builtin_function, new_rvalue_vector_perm,
	new_vector_access, copy, memento_of_new_rvalue_vector_perm,
	memento_of_new_rvalue_vector_perm::replay_into,
	memento_of_new_rvalue_vector_perm::visit_children,
	memento_of_new_rvalue_vector_perm::make_debug_string,
	memento_of_new_rvalue_vector_perm::write_reproducer,
	vector_access::replay_into, vector_access::visit_children,
	vector_access::make_debug_string, vector_access::write_reproducer).
	* libgccjit.cc: New functions
	(gcc_jit_context_get_target_builtin_function,
	gcc_jit_context_new_vector_access,
	gcc_jit_context_new_rvalue_vector_perm).
	* libgccjit.h: New variant GCC_JIT_TYPE_BFLOAT16 en enum
	gcc_jit_types, new functions
	(gcc_jit_context_get_target_builtin_function,
	gcc_jit_context_new_vector_access,
	gcc_jit_context_new_rvalue_vector_perm).
	* libgccjit.map: New functions
	(gcc_jit_context_get_target_builtin_function,
	gcc_jit_context_new_rvalue_vector_perm,
	gcc_jit_context_new_vector_access).

gcc/testsuite:
	PR jit/108762
	* jit.dg/all-non-failing-tests.h: New test test-target-builtins.c.
	* jit.dg/test-target-builtins.c: New test.
---
 gcc/config/i386/i386-builtins.cc             |  18 ++
 gcc/jit/docs/topics/compatibility.rst        |  10 +
 gcc/jit/docs/topics/expressions.rst          |  45 ++++
 gcc/jit/docs/topics/functions.rst            |  19 ++
 gcc/jit/docs/topics/types.rst                |   2 +
 gcc/jit/dummy-frontend.cc                    | 203 ++++++++++++++-
 gcc/jit/jit-builtins.cc                      |   6 +-
 gcc/jit/jit-common.h                         |   2 +-
 gcc/jit/jit-playback.cc                      | 166 +++++++++++-
 gcc/jit/jit-playback.h                       |  16 +-
 gcc/jit/jit-recording.cc                     | 255 ++++++++++++++++++-
 gcc/jit/jit-recording.h                      | 176 ++++++++++++-
 gcc/jit/libgccjit.cc                         | 127 +++++++++
 gcc/jit/libgccjit.h                          |  44 +++-
 gcc/jit/libgccjit.map                        |   7 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h |   3 +
 gcc/testsuite/jit.dg/test-target-builtins.c  |  77 ++++++
 17 files changed, 1153 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-target-builtins.c

diff --git a/gcc/config/i386/i386-builtins.cc b/gcc/config/i386/i386-builtins.cc
index fc0c82b156e..f9b9937a669 100644
--- a/gcc/config/i386/i386-builtins.cc
+++ b/gcc/config/i386/i386-builtins.cc
@@ -226,6 +226,22 @@ static GTY(()) tree ix86_builtins[(int) IX86_BUILTIN_MAX];
 
 struct builtin_isa ix86_builtins_isa[(int) IX86_BUILTIN_MAX];
 
+static void
+clear_builtin_types (void)
+{
+  for (int i = 0 ; i < IX86_BT_LAST_CPTR + 1 ; i++)
+    ix86_builtin_type_tab[i] = NULL;
+
+  for (int i = 0 ; i < IX86_BUILTIN_MAX ; i++)
+  {
+    ix86_builtins[i] = NULL;
+    ix86_builtins_isa[i].set_and_not_built_p = true;
+  }
+
+  for (int i = 0 ; i < IX86_BT_LAST_ALIAS + 1 ; i++)
+    ix86_builtin_func_type_tab[i] = NULL;
+}
+
 tree get_ix86_builtin (enum ix86_builtins c)
 {
   return ix86_builtins[c];
@@ -1443,6 +1459,8 @@ ix86_init_builtins (void)
 {
   tree ftype, decl;
 
+  clear_builtin_types ();
+
   ix86_init_builtin_types ();
 
   /* Builtins to get CPU type and features. */
diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index f4ffa07ec48..402df5b60b7 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -371,3 +371,13 @@ alignment of a variable:
 
   * :func:`gcc_jit_lvalue_set_alignment`
   * :func:`gcc_jit_lvalue_get_alignment`
+
+.. _LIBGCCJIT_ABI_25:
+
+``LIBGCCJIT_ABI_25``
+--------------------
+``LIBGCCJIT_ABI_25`` covers the addition of functions to get target builtins and manipulate vectors:
+
+  * :func:`gcc_jit_context_get_target_builtin_function`
+  * :func:`gcc_jit_context_new_rvalue_vector_perm`
+  * :func:`gcc_jit_context_new_vector_access`
diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst
index 42cfee36302..57905959634 100644
--- a/gcc/jit/docs/topics/expressions.rst
+++ b/gcc/jit/docs/topics/expressions.rst
@@ -295,6 +295,35 @@ Vector expressions
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_rvalue_from_vector
 
+.. function:: gcc_jit_rvalue * \
+              gcc_jit_context_new_rvalue_vector_perm (gcc_jit_context *ctxt, \
+                                                      gcc_jit_location *loc, \
+                                                      gcc_jit_rvalue *elements1, \
+                                                      gcc_jit_rvalue *elements2, \
+                                                      gcc_jit_rvalue *mask);
+
+   Build a permutation of two vectors.
+
+   "elements1" and "elements2" should have the same type.
+   The length of "mask" and "elements1" should be the same.
+   The element type of "mask" should be integral.
+   The size of the element type of "mask" and "elements1" should be the same.
+
+   This entrypoint was added in :ref:`LIBGCCJIT_ABI_25`; you can test for
+   its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_TARGET_BUILTIN
+
+    Analogous to:
+
+    .. code-block:: c
+
+       __builtin_shuffle (elements1, elements2, mask)
+
+    in C.
+
 Unary Operations
 ****************
 
@@ -1020,3 +1049,19 @@ Field access is provided separately for both lvalues and rvalues.
       PTR[INDEX]
 
    in C (or, indeed, to ``PTR + INDEX``).
+
+.. function:: gcc_jit_lvalue *\
+              gcc_jit_context_new_vector_access (gcc_jit_context *ctxt,\
+                                                 gcc_jit_location *loc,\
+                                                 gcc_jit_rvalue *vector,\
+                                                 gcc_jit_rvalue *index)
+
+   Given an rvalue of vector type ``T __attribute__ ((__vector_size__ (SIZE)))``, get the element `T` at
+   the given index.
+   Analogous to:
+
+   .. code-block:: c
+
+      VECTOR[INDEX]
+
+   in C.
diff --git a/gcc/jit/docs/topics/functions.rst b/gcc/jit/docs/topics/functions.rst
index cf5cb716daf..e9b77fdb892 100644
--- a/gcc/jit/docs/topics/functions.rst
+++ b/gcc/jit/docs/topics/functions.rst
@@ -140,6 +140,25 @@ Functions
       uses such a parameter will lead to an error being emitted within
       the context.
 
+.. function::  gcc_jit_function *\
+               gcc_jit_context_get_target_builtin_function (gcc_jit_context *ctxt,\
+                                                            const char *name)
+
+   Get the :type:`gcc_jit_function` for the built-in function with the
+   given name.  For example:
+
+   .. code-block:: c
+
+      gcc_jit_function *fn
+        = gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_ia32_pmuldq512_mask");
+
+   .. note:: Due to technical limitations with how libgccjit interacts with
+      the insides of GCC, not all built-in functions are supported.  More
+      precisely, not all types are supported for parameters of built-in
+      functions from libgccjit.  Attempts to get a built-in function that
+      uses such a parameter will lead to an error being emitted within
+      the context.
+
 .. function::  gcc_jit_object *\
                gcc_jit_function_as_object (gcc_jit_function *func)
 
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index f5f2aac1f0c..e7db00779cc 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -129,6 +129,8 @@ Standard types
        - C99's ``_Complex double``
      * - :c:data:`GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE`
        - C99's ``_Complex long double``
+     * - :c:data:`GCC_JIT_TYPE_BFLOAT16`
+       -
 
 .. function:: gcc_jit_type *\
               gcc_jit_context_get_int_type (gcc_jit_context *ctxt, \
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 8b7294e32cc..cf7ef66d5b8 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "target.h"
 #include "jit-playback.h"
 #include "stor-layout.h"
 #include "debug.h"
@@ -29,8 +30,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "options.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "jit-recording.h"
+#include "print-tree.h"
 
 #include <mpfr.h>
+#include <unordered_map>
+#include <string>
+
+using namespace gcc::jit;
 
 /* Attribute handling.  */
 
@@ -86,6 +93,11 @@ static const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
   ATTR_EXCL (NULL, false, false, false)
 };
 
+hash_map<nofree_string_hash, tree> target_builtins{};
+std::unordered_map<std::string, recording::function_type*> target_function_types
+{};
+recording::context target_builtins_ctxt{NULL};
+
 /* Table of machine-independent attributes supported in libgccjit.  */
 const struct attribute_spec jit_attribute_table[] =
 {
@@ -594,6 +606,7 @@ jit_langhook_init (void)
 
   build_common_tree_nodes (false);
 
+  target_builtins.empty ();
   build_common_builtin_nodes ();
 
   /* The default precision for floating point numbers.  This is used
@@ -601,6 +614,8 @@ jit_langhook_init (void)
      eventually be controllable by a command line option.  */
   mpfr_set_default_prec (256);
 
+  targetm.init_builtins ();
+
   return true;
 }
 
@@ -668,11 +683,197 @@ jit_langhook_type_for_mode (machine_mode mode, int unsignedp)
   return NULL;
 }
 
-/* Record a builtin function.  We just ignore builtin functions.  */
+recording::type* tree_type_to_jit_type (tree type)
+{
+  if (TREE_CODE (type) == VECTOR_TYPE)
+  {
+    tree inner_type = TREE_TYPE (type);
+    recording::type* element_type = tree_type_to_jit_type (inner_type);
+    poly_uint64 size = TYPE_VECTOR_SUBPARTS (type);
+    long constant_size = size.to_constant ();
+    if (element_type != NULL)
+      return element_type->get_vector (constant_size);
+    return NULL;
+  }
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    // For __builtin_ms_va_start.
+    // FIXME: wrong type.
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_VOID);
+  if (TREE_CODE (type) == RECORD_TYPE)
+    // For __builtin_sysv_va_copy.
+    // FIXME: wrong type.
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_VOID);
+  for (int i = 0; i < NUM_FLOATN_NX_TYPES; i++)
+    if (type == FLOATN_NX_TYPE_NODE (i))
+      // FIXME: wrong type.
+      return new recording::memento_of_get_type (&target_builtins_ctxt,
+						 GCC_JIT_TYPE_VOID);
+  if (type == void_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_VOID);
+  else if (type == ptr_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_VOID_PTR);
+  else if (type == const_ptr_type_node)
+  {
+    // Void const ptr.
+    recording::type* result =
+      new recording::memento_of_get_type (&target_builtins_ctxt,
+					  GCC_JIT_TYPE_VOID_PTR);
+    return new recording::memento_of_get_const (result);
+  }
+  else if (type == unsigned_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_UNSIGNED_INT);
+  else if (type == long_unsigned_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_UNSIGNED_LONG);
+  else if (type == integer_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_INT);
+  else if (type == long_integer_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_LONG);
+  else if (type == long_long_integer_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_LONG_LONG);
+  else if (type == signed_char_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_SIGNED_CHAR);
+  else if (type == char_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_CHAR);
+  else if (type == unsigned_intQI_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_UINT8_T);
+  else if (type == short_integer_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_SHORT);
+  else if (type == short_unsigned_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_UNSIGNED_SHORT);
+  else if (type == complex_float_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_COMPLEX_FLOAT);
+  else if (type == complex_double_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_COMPLEX_DOUBLE);
+  else if (type == complex_long_double_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					    GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE);
+  else if (type == float_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_FLOAT);
+  else if (type == double_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_DOUBLE);
+  else if (type == long_double_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_LONG_DOUBLE);
+  else if (type == bfloat16_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_BFLOAT16);
+  else if (type == dfloat128_type_node)
+    // FIXME: wrong type.
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_VOID);
+  else if (type == long_long_unsigned_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
+  else if (type == boolean_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_BOOL);
+  else if (type == size_type_node)
+    return new recording::memento_of_get_type (&target_builtins_ctxt,
+					       GCC_JIT_TYPE_SIZE_T);
+  else if (TREE_CODE (type) == POINTER_TYPE)
+  {
+    tree inner_type = TREE_TYPE (type);
+    recording::type* element_type = tree_type_to_jit_type (inner_type);
+    return element_type->get_pointer ();
+  }
+  else
+  {
+    // Attempt to find an unqualified type when the current type has qualifiers.
+    tree tp = TYPE_MAIN_VARIANT (type);
+    for ( ; tp != NULL ; tp = TYPE_NEXT_VARIANT (tp))
+    {
+      if (TYPE_QUALS (tp) == 0 && type != tp)
+      {
+	recording::type* result = tree_type_to_jit_type (tp);
+	if (result != NULL)
+	{
+	  if (TYPE_READONLY (tp))
+	    result = new recording::memento_of_get_const (result);
+	  if (TYPE_VOLATILE (tp))
+	    result = new recording::memento_of_get_volatile (result);
+	  return result;
+	}
+      }
+    }
+
+    fprintf (stderr, "Unknown type:\n");
+    debug_tree (type);
+    abort ();
+  }
+
+  return NULL;
+}
+
+/* Record a builtin function.  We save their types to be able to check types
+   in recording and for reflection.  */
 
 static tree
 jit_langhook_builtin_function (tree decl)
 {
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+  {
+    const char* name = IDENTIFIER_POINTER (DECL_NAME (decl));
+    target_builtins.put (name, decl);
+
+    std::string string_name (name);
+    if (target_function_types.count (string_name) == 0)
+    {
+      tree function_type = TREE_TYPE (decl);
+      tree arg = TYPE_ARG_TYPES (function_type);
+      bool is_variadic = false;
+
+      auto_vec <recording::type *> param_types;
+
+      while (arg != void_list_node)
+      {
+	if (arg == NULL)
+	{
+	  is_variadic = true;
+	  break;
+	}
+	if (arg != void_list_node)
+	{
+	  recording::type* arg_type = tree_type_to_jit_type (TREE_VALUE (arg));
+	  if (arg_type == NULL)
+	    return decl;
+	  param_types.safe_push (arg_type);
+	}
+	arg = TREE_CHAIN (arg);
+      }
+
+      tree result_type = TREE_TYPE (function_type);
+      recording::type* return_type = tree_type_to_jit_type (result_type);
+
+      if (return_type == NULL)
+	return decl;
+
+      recording::function_type* func_type =
+	new recording::function_type (&target_builtins_ctxt, return_type,
+				      param_types.length (),
+				      param_types.address (), is_variadic,
+				      false);
+
+      target_function_types[string_name] = func_type;
+    }
+  }
   return decl;
 }
 
diff --git a/gcc/jit/jit-builtins.cc b/gcc/jit/jit-builtins.cc
index fdd0739789d..63628beac81 100644
--- a/gcc/jit/jit-builtins.cc
+++ b/gcc/jit/jit-builtins.cc
@@ -215,7 +215,8 @@ builtins_manager::make_builtin_function (enum built_in_function builtin_id)
 			     param_types.length (),
 			     params,
 			     func_type->is_variadic (),
-			     builtin_id);
+			     builtin_id,
+			     false);
   delete[] params;
 
   /* PR/64020 - If the client code is using builtin cos or sin,
@@ -582,7 +583,8 @@ builtins_manager::make_fn_type (enum jit_builtin_type,
   result = m_ctxt->new_function_type (return_type,
 				      num_args,
 				      param_types,
-				      is_variadic);
+				      is_variadic,
+				      false);
 
  error:
   delete[] param_types;
diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h
index 80c1618da96..983c9190d44 100644
--- a/gcc/jit/jit-common.h
+++ b/gcc/jit/jit-common.h
@@ -36,7 +36,7 @@ along with GCC; see the file COPYING3.  If not see
 #endif
 #endif
 
-const int NUM_GCC_JIT_TYPES = GCC_JIT_TYPE_INT128_T + 1;
+const int NUM_GCC_JIT_TYPES = GCC_JIT_TYPE_BFLOAT16 + 1;
 
 /* This comment is included by the docs.
 
diff --git a/gcc/jit/jit-playback.cc b/gcc/jit/jit-playback.cc
index e06f161aad9..209b4cca191 100644
--- a/gcc/jit/jit-playback.cc
+++ b/gcc/jit/jit-playback.cc
@@ -280,6 +280,8 @@ get_tree_node_for_type (enum gcc_jit_types type_)
 
     case GCC_JIT_TYPE_FLOAT:
       return float_type_node;
+    case GCC_JIT_TYPE_BFLOAT16:
+      return bfloat16_type_node;
     case GCC_JIT_TYPE_DOUBLE:
       return double_type_node;
     case GCC_JIT_TYPE_LONG_DOUBLE:
@@ -509,7 +511,8 @@ 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,
+	      int is_target_builtin)
 {
   int i;
   param *param;
@@ -543,6 +546,14 @@ new_function (location *loc,
   DECL_RESULT (fndecl) = resdecl;
   DECL_CONTEXT (resdecl) = fndecl;
 
+  if (is_target_builtin)
+  {
+    tree *decl = target_builtins.get (name);
+    if (decl != NULL)
+      fndecl = *decl;
+    else
+      add_error (loc, "cannot find target builtin %s", name);
+  }
   if (builtin_id)
     {
       gcc_assert (loc == NULL);
@@ -1007,10 +1018,31 @@ playback::context::new_rvalue_from_vector (location *,
   vec_alloc (v, elements.length ());
   for (unsigned i = 0; i < elements.length (); ++i)
     CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, elements[i]->as_tree ());
-  tree t_ctor = build_constructor (type->as_tree (), v);
+  tree t_ctor;
+  t_ctor = build_constructor (type->as_tree (), v);
   return new rvalue (this, t_ctor);
 }
 
+/* Construct a playback::rvalue instance (wrapping a tree) for a
+   vector perm.  */
+
+playback::rvalue *
+playback::context::new_rvalue_vector_perm (location *loc,
+					   rvalue* elements1,
+					   rvalue* elements2,
+					   rvalue* mask)
+{
+  tree t_elements1 = elements1->as_tree ();
+  tree t_elements2 = elements2->as_tree ();
+  tree t_mask = mask->as_tree ();
+
+  tree t_vector_perm = build3 (VEC_PERM_EXPR, TREE_TYPE (t_elements1),
+			       t_elements1, t_elements2, t_mask);
+  if (loc)
+    set_tree_location (t_vector_perm, loc);
+  return new rvalue (this, t_vector_perm);
+}
+
 /* Coerce a tree expression into a boolean tree expression.  */
 
 tree
@@ -1527,6 +1559,136 @@ new_array_access (location *loc,
     }
 }
 
+/* The following functions come from c-common.h.  */
+/* Like c_mark_addressable but don't check register qualifier.  */
+void
+common_mark_addressable_vec (tree t)
+{
+  while (handled_component_p (t) || TREE_CODE (t) == C_MAYBE_CONST_EXPR)
+    {
+      t = TREE_OPERAND (t, 0);
+    }
+  if (!VAR_P (t)
+      && TREE_CODE (t) != PARM_DECL
+      && TREE_CODE (t) != COMPOUND_LITERAL_EXPR
+      && TREE_CODE (t) != TARGET_EXPR)
+    return;
+  if (!VAR_P (t) || !DECL_HARD_REGISTER (t))
+    TREE_ADDRESSABLE (t) = 1;
+  if (TREE_CODE (t) == COMPOUND_LITERAL_EXPR)
+    TREE_ADDRESSABLE (COMPOUND_LITERAL_EXPR_DECL (t)) = 1;
+  else if (TREE_CODE (t) == TARGET_EXPR)
+    TREE_ADDRESSABLE (TARGET_EXPR_SLOT (t)) = 1;
+}
+
+/* Return true if TYPE is a vector type that should be subject to the GNU
+   vector extensions (as opposed to a vector type that is used only for
+   the purposes of defining target-specific built-in functions).  */
+
+inline bool
+gnu_vector_type_p (const_tree type)
+{
+  return TREE_CODE (type) == VECTOR_TYPE && !TYPE_INDIVISIBLE_P (type);
+}
+
+/* Return nonzero if REF is an lvalue valid for this language.
+   Lvalues can be assigned, unless their type has TYPE_READONLY.
+   Lvalues can have their address taken, unless they have C_DECL_REGISTER.  */
+
+bool
+lvalue_p (const_tree ref)
+{
+  const enum tree_code code = TREE_CODE (ref);
+
+  switch (code)
+    {
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+    case COMPONENT_REF:
+      return lvalue_p (TREE_OPERAND (ref, 0));
+
+    case C_MAYBE_CONST_EXPR:
+      return lvalue_p (TREE_OPERAND (ref, 1));
+
+    case COMPOUND_LITERAL_EXPR:
+    case STRING_CST:
+      return true;
+
+    case MEM_REF:
+    case TARGET_MEM_REF:
+      /* MEM_REFs can appear from -fgimple parsing or folding, so allow them
+	 here as well.  */
+    case INDIRECT_REF:
+    case ARRAY_REF:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+    case ERROR_MARK:
+      return (TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE
+	      && TREE_CODE (TREE_TYPE (ref)) != METHOD_TYPE);
+
+    case BIND_EXPR:
+      return TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE;
+
+    default:
+      return false;
+    }
+}
+
+bool
+convert_vector_to_array_for_subscript (tree *vecp)
+{
+  bool ret = false;
+  if (gnu_vector_type_p (TREE_TYPE (*vecp)))
+    {
+      tree type = TREE_TYPE (*vecp);
+
+      ret = !lvalue_p (*vecp);
+
+      /* We are building an ARRAY_REF so mark the vector as addressable
+	to not run into the gimplifiers premature setting of DECL_GIMPLE_REG_P
+	 for function parameters.  */
+      /* NOTE: that was the missing piece for making vector access work with
+	optimizations enabled.  */
+      common_mark_addressable_vec (*vecp);
+
+      *vecp = build1 (VIEW_CONVERT_EXPR,
+		      build_array_type_nelts (TREE_TYPE (type),
+					      TYPE_VECTOR_SUBPARTS (type)),
+		      *vecp);
+    }
+  return ret;
+}
+
+/* Construct a playback::lvalue instance (wrapping a tree) for a
+   vector access.  */
+
+playback::lvalue *
+playback::context::
+new_vector_access (location *loc,
+		   rvalue *vector,
+		   rvalue *index)
+{
+  gcc_assert (vector);
+  gcc_assert (index);
+
+  /* For comparison, see:
+       c/c-typeck.cc: build_array_ref
+  */
+
+  tree t_vector = vector->as_tree ();
+  bool non_lvalue = convert_vector_to_array_for_subscript (&t_vector);
+  tree type = TREE_TYPE (TREE_TYPE (t_vector));
+  tree t_result = build4 (ARRAY_REF, type, t_vector, index->as_tree (),
+			  NULL_TREE, NULL_TREE);
+  if (non_lvalue)
+    t_result = non_lvalue (t_result);
+
+  if (loc)
+    set_tree_location (t_result, loc);
+  return new lvalue (this, t_result);
+}
+
 /* Construct a tree for a field access.  */
 
 tree
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 883159f03c3..648dd2bc058 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -104,7 +104,8 @@ public:
 		const char *name,
 		const auto_vec<param *> *params,
 		int is_variadic,
-		enum built_in_function builtin_id);
+		enum built_in_function builtin_id,
+		int is_target_builtin);
 
   lvalue *
   new_global (location *loc,
@@ -147,6 +148,12 @@ public:
 			  type *type,
 			  const auto_vec<rvalue *> &elements);
 
+  rvalue *
+  new_rvalue_vector_perm (location *loc,
+			  rvalue* elements1,
+			  rvalue* elements2,
+			  rvalue* mask);
+
   rvalue *
   new_unary_op (location *loc,
 		enum gcc_jit_unary_op op,
@@ -191,6 +198,11 @@ public:
 		    rvalue *ptr,
 		    rvalue *index);
 
+  lvalue *
+  new_vector_access (location *loc,
+		     rvalue *vector,
+		     rvalue *index);
+
   void
   set_str_option (enum gcc_jit_str_option opt,
 		  const char *value);
@@ -813,4 +825,6 @@ extern playback::context *active_playback_ctxt;
 
 } // namespace gcc
 
+extern hash_map<nofree_string_hash, tree> target_builtins;
+
 #endif /* JIT_PLAYBACK_H */
diff --git a/gcc/jit/jit-recording.cc b/gcc/jit/jit-recording.cc
index df0368ff8f7..0c3ea8ec423 100644
--- a/gcc/jit/jit-recording.cc
+++ b/gcc/jit/jit-recording.cc
@@ -933,14 +933,16 @@ recording::function_type *
 recording::context::new_function_type (recording::type *return_type,
 				       int num_params,
 				       recording::type **param_types,
-				       int is_variadic)
+				       int is_variadic,
+				       int is_target_builtin)
 {
   recording::function_type *fn_type
     = new function_type (this,
 			 return_type,
 			 num_params,
 			 param_types,
-			 is_variadic);
+			 is_variadic,
+			 is_target_builtin);
   record (fn_type);
   return fn_type;
 }
@@ -962,7 +964,8 @@ recording::context::new_function_ptr_type (recording::location *, /* unused loc
     = new_function_type (return_type,
 			 num_params,
 			 param_types,
-			 is_variadic);
+			 is_variadic,
+			 false);
 
   /* Return a pointer-type to the function type.  */
   return fn_type->get_pointer ();
@@ -1005,7 +1008,7 @@ recording::context::new_function (recording::location *loc,
 			     loc, kind, return_type,
 			     new_string (name),
 			     num_params, params, is_variadic,
-			     builtin_id);
+			     builtin_id, false);
   record (result);
   m_functions.safe_push (result);
 
@@ -1044,6 +1047,53 @@ recording::context::get_builtin_function (const char *name)
   return bm->get_builtin_function (name);
 }
 
+/* Create a recording::function instance for a target-specific builtin.
+
+   Implements the post-error-checking part of
+   gcc_jit_context_get_target_builtin_function.  */
+
+recording::function *
+recording::context::get_target_builtin_function (const char *name)
+{
+  const char *asm_name = name;
+  if (target_function_types.count (name) == 0)
+  {
+    fprintf (stderr, "Cannot find target builtin %s\n", name);
+    return NULL;
+  }
+
+  recording::function_type* func_type = target_function_types[name]
+    ->copy (this)->dyn_cast_function_type ();
+  const vec<type *>& param_types = func_type->get_param_types ();
+  recording::param **params = new recording::param *[param_types.length ()];
+
+  int i;
+  recording::type *param_type;
+  FOR_EACH_VEC_ELT (param_types, i, param_type)
+    {
+      char buf[16];
+      snprintf (buf, 16, "arg%d", i);
+      params[i] = new_param (NULL,
+			     param_type,
+			     buf);
+  }
+
+  recording::function *result =
+    new recording::function (this,
+	  NULL,
+	  GCC_JIT_FUNCTION_IMPORTED,
+	  func_type->get_return_type (),
+	  new_string (asm_name),
+	  param_types.length (),
+	  params,
+	  func_type->is_variadic (),
+	  BUILT_IN_NONE,
+	  true);
+  record (result);
+
+  return result;
+}
+
 /* Create a recording::global instance and add it to this context's list
    of mementos.
 
@@ -1108,6 +1158,19 @@ recording::context::new_rvalue_from_vector (location *loc,
   return result;
 }
 
+recording::rvalue *
+recording::context::new_rvalue_vector_perm (location *loc,
+			  rvalue *elements1,
+			  rvalue *elements2,
+			  rvalue *mask)
+{
+  recording::rvalue *result
+    = new memento_of_new_rvalue_vector_perm (this, loc, elements1, elements2,
+					     mask);
+  record (result);
+  return result;
+}
+
 recording::rvalue *
 recording::context::new_ctor (recording::location *loc,
 			      recording::type *type,
@@ -1309,6 +1372,22 @@ recording::context::new_array_access (recording::location *loc,
   return result;
 }
 
+/* Create a recording::vector_access instance and add it to this context's list
+   of mementos.
+
+   Implements the post-error-checking part of
+   gcc_jit_context_new_vector_access.  */
+
+recording::lvalue *
+recording::context::new_vector_access (recording::location *loc,
+				      recording::rvalue *vector,
+				      recording::rvalue *index)
+{
+  recording::lvalue *result = new vector_access (this, loc, vector, index);
+  record (result);
+  return result;
+}
+
 /* Create a recording::case_ instance and add it to this context's list
    of mementos.
 
@@ -2372,6 +2451,8 @@ recording::memento_of_get_type::get_size ()
     case GCC_JIT_TYPE_FLOAT:
       size = FLOAT_TYPE_SIZE;
       break;
+    case GCC_JIT_TYPE_BFLOAT16:
+      return GET_MODE_UNIT_SIZE (BFmode);
     case GCC_JIT_TYPE_DOUBLE:
       size = DOUBLE_TYPE_SIZE;
       break;
@@ -2431,6 +2512,7 @@ recording::memento_of_get_type::dereference ()
     case GCC_JIT_TYPE_INT64_T:
     case GCC_JIT_TYPE_INT128_T:
     case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_BFLOAT16:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
     case GCC_JIT_TYPE_COMPLEX_FLOAT:
@@ -2495,6 +2577,7 @@ recording::memento_of_get_type::is_int () const
       return true;
 
     case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_BFLOAT16:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
       return false;
@@ -2553,6 +2636,7 @@ recording::memento_of_get_type::is_signed () const
     case GCC_JIT_TYPE_UINT128_T:
 
     case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_BFLOAT16:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
 
@@ -2612,6 +2696,7 @@ recording::memento_of_get_type::is_float () const
       return false;
 
     case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_BFLOAT16:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
       return true;
@@ -2675,6 +2760,7 @@ recording::memento_of_get_type::is_bool () const
       return false;
 
     case GCC_JIT_TYPE_FLOAT:
+    case GCC_JIT_TYPE_BFLOAT16:
     case GCC_JIT_TYPE_DOUBLE:
     case GCC_JIT_TYPE_LONG_DOUBLE:
       return false;
@@ -2754,6 +2840,7 @@ static const char * const get_type_strings[] = {
   "__int32_t",    /* GCC_JIT_TYPE_INT32_T */
   "__int64_t",    /* GCC_JIT_TYPE_INT64_T */
   "__int128_t",   /* GCC_JIT_TYPE_INT128_T */
+  "bfloat16",     /* GCC_JIT_TYPE_BFLOAT16 */
 
 };
 
@@ -2800,6 +2887,7 @@ static const char * const get_type_enum_strings[] = {
   "GCC_JIT_TYPE_INT32_T",
   "GCC_JIT_TYPE_INT64_T",
   "GCC_JIT_TYPE_INT128_T",
+  "GCC_JIT_TYPE_BFLOAT16",
 };
 
 void
@@ -3098,11 +3186,13 @@ recording::function_type::function_type (context *ctxt,
 					 type *return_type,
 					 int num_params,
 					 type **param_types,
-					 int is_variadic)
+					 int is_variadic,
+					 int is_target_builtin)
 : type (ctxt),
   m_return_type (return_type),
   m_param_types (),
-  m_is_variadic (is_variadic)
+  m_is_variadic (is_variadic),
+  m_is_target_builtin (is_target_builtin)
 {
   for (int i = 0; i< num_params; i++)
     m_param_types.safe_push (param_types[i]);
@@ -4044,7 +4134,8 @@ recording::function::function (context *ctxt,
 			       int num_params,
 			       recording::param **params,
 			       int is_variadic,
-			       enum built_in_function builtin_id)
+			       enum built_in_function builtin_id,
+			       int is_target_builtin)
 : memento (ctxt),
   m_loc (loc),
   m_kind (kind),
@@ -4055,7 +4146,8 @@ 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_is_target_builtin (is_target_builtin)
 {
   for (int i = 0; i< num_params; i++)
     {
@@ -4114,7 +4206,8 @@ recording::function::replay_into (replayer *r)
 				     m_name->c_str (),
 				     &params,
 				     m_is_variadic,
-				     m_builtin_id));
+				     m_builtin_id,
+				     m_is_target_builtin));
 }
 
 /* Create a recording::local instance and add it to
@@ -4347,7 +4440,8 @@ recording::function::get_address (recording::location *loc)
 	= m_ctxt->new_function_type (m_return_type,
 				     m_params.length (),
 				     param_types.address (),
-				     m_is_variadic);
+				     m_is_variadic,
+				     m_is_target_builtin);
       m_fn_ptr_type = fn_type->get_pointer ();
     }
   gcc_assert (m_fn_ptr_type);
@@ -5390,6 +5484,91 @@ recording::memento_of_new_rvalue_from_vector::write_reproducer (reproducer &r)
 	   elements_id);
 }
 
+/* The implementation of class
+   gcc::jit::recording::memento_of_new_rvalue_vector_perm.  */
+
+/* The constructor for
+   gcc::jit::recording::memento_of_new_rvalue_vector_perm.  */
+
+recording::memento_of_new_rvalue_vector_perm::
+memento_of_new_rvalue_vector_perm (context *ctxt,
+				   location *loc,
+				   rvalue *elements1,
+				   rvalue *elements2,
+				   rvalue *mask)
+: rvalue (ctxt, loc, elements1->get_type ()),
+  m_elements1 (elements1),
+  m_elements2 (elements2),
+  m_mask (mask)
+{
+}
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::memento_of_new_rvalue_vector_perm.  */
+
+void
+recording::memento_of_new_rvalue_vector_perm::replay_into (replayer *r)
+{
+  playback::rvalue *playback_elements1 = m_elements1->playback_rvalue ();
+  playback::rvalue *playback_elements2 = m_elements2->playback_rvalue ();
+  playback::rvalue *playback_mask = m_mask->playback_rvalue ();
+
+  set_playback_obj (r->new_rvalue_vector_perm (playback_location (r, m_loc),
+		    playback_elements1,
+		    playback_elements2,
+		    playback_mask));
+}
+
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::memento_of_new_rvalue_from_vector.  */
+
+    void
+recording::memento_of_new_rvalue_vector_perm::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_elements1);
+  v->visit (m_elements2);
+  v->visit (m_mask);
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   vectors.  */
+
+    recording::string *
+recording::memento_of_new_rvalue_vector_perm::make_debug_string ()
+{
+    /* Now build a string.  */
+    string *result = string::from_printf (m_ctxt,
+		"shufflevector (%s, %s, %s)",
+		m_elements1->get_debug_string (),
+		m_elements2->get_debug_string (),
+		m_mask->get_debug_string ());
+
+    return result;
+
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   vectors.  */
+
+    void
+recording::memento_of_new_rvalue_vector_perm::write_reproducer (reproducer &r)
+{
+    const char *id = r.make_identifier (this, "vector");
+  r.write ("  gcc_jit_rvalue *%s =\n"
+	   "    gcc_jit_context_new_rvalue_vector_perm (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                            %s, /* gcc_jit_location *loc */\n"
+	   "                                            %s, /* gcc_jit_rvalue **elements1*/\n"
+	   "                                            %s, /* gcc_jit_rvalue **elements2*/\n"
+	   "                                            %s); /* gcc_jit_rvalue **mask*/\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_rvalue (m_elements1),
+	   r.get_identifier_as_rvalue (m_elements2),
+	   r.get_identifier_as_rvalue (m_mask));
+}
+
+
 void
 recording::ctor::visit_children (rvalue_visitor *v)
 {
@@ -6260,6 +6439,62 @@ recording::array_access::write_reproducer (reproducer &r)
 	   r.get_identifier_as_rvalue (m_index));
 }
 
+/* The implementation of class gcc::jit::recording::vector_access.  */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::vector_access.  */
+
+void
+recording::vector_access::replay_into (replayer *r)
+{
+  set_playback_obj (
+    r->new_vector_access (playback_location (r, m_loc),
+			  m_vector->playback_rvalue (),
+			  m_index->playback_rvalue ()));
+}
+
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::vector_access.  */
+
+void
+recording::vector_access::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_vector);
+  v->visit (m_index);
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   array accesses.  */
+
+recording::string *
+recording::vector_access::make_debug_string ()
+{
+  enum precedence prec = get_precedence ();
+  return string::from_printf (m_ctxt,
+			      "%s[%s]",
+			      m_vector->get_debug_string_parens (prec),
+			      m_index->get_debug_string_parens (prec));
+}
+
+/* Implementation of recording::memento::write_reproducer for
+   vector_access.  */
+
+void
+recording::vector_access::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "lvalue");
+  r.write ("  gcc_jit_lvalue *%s = \n"
+	   "    gcc_jit_context_new_vector_access (%s, /* gcc_jit_context *ctxt */\n"
+	   "                                       %s, /*gcc_jit_location *loc */\n"
+	   "                                       %s, /* gcc_jit_rvalue *vector */\n"
+	   "                                       %s); /* gcc_jit_rvalue *index */\n",
+	   id,
+	   r.get_identifier (get_context ()),
+	   r.get_identifier (m_loc),
+	   r.get_identifier_as_rvalue (m_vector),
+	   r.get_identifier_as_rvalue (m_index));
+}
+
 /* The implementation of class gcc::jit::recording::access_field_of_lvalue.  */
 
 /* Implementation of pure virtual hook recording::memento::replay_into
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 2dc9448d7c9..4d704e800c3 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -24,12 +24,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "jit-common.h"
 #include "jit-logging.h"
 
+#include <string>
+#include <unordered_map>
+
 class timer;
 
+extern std::unordered_map<std::string, gcc::jit::recording::function_type*>
+  target_function_types;
+
 namespace gcc {
 
 namespace jit {
-
 extern const char * const unary_op_reproducer_strings[];
 extern const char * const binary_op_reproducer_strings[];
 
@@ -116,7 +121,8 @@ public:
   new_function_type (type *return_type,
 		     int num_params,
 		     type **param_types,
-		     int is_variadic);
+		     int is_variadic,
+		     int is_target_builtin);
 
   type *
   new_function_ptr_type (location *loc,
@@ -143,6 +149,9 @@ public:
   function *
   get_builtin_function (const char *name);
 
+  function *
+  get_target_builtin_function (const char *name);
+
   lvalue *
   new_global (location *loc,
 	      enum gcc_jit_global_kind kind,
@@ -173,6 +182,12 @@ public:
 			  vector_type *type,
 			  rvalue **elements);
 
+  rvalue *
+  new_rvalue_vector_perm (location *loc,
+			  rvalue *elements1,
+			  rvalue *elements2,
+			  rvalue *mask);
+
   rvalue *
   new_unary_op (location *loc,
 		enum gcc_jit_unary_op op,
@@ -215,6 +230,11 @@ public:
 		    rvalue *ptr,
 		    rvalue *index);
 
+  lvalue *
+  new_vector_access (location *loc,
+		     rvalue *vector,
+		     rvalue *index);
+
   case_ *
   new_case (rvalue *min_value,
 	    rvalue *max_value,
@@ -539,6 +559,8 @@ public:
      these types.  */
   virtual size_t get_size () { gcc_unreachable (); }
 
+  virtual type* copy (context* ctxt) = 0;
+
   /* Dynamic casts.  */
   virtual function_type *dyn_cast_function_type () { return NULL; }
   virtual function_type *as_a_function_type() { gcc_unreachable (); return NULL; }
@@ -613,6 +635,11 @@ public:
 
   size_t get_size () final override;
 
+  type* copy (context* ctxt) final override
+  {
+    return ctxt->get_type (m_kind);
+  }
+
   bool accepts_writes_from (type *rtype) final override
   {
     if (m_kind == GCC_JIT_TYPE_VOID_PTR)
@@ -664,6 +691,13 @@ public:
 
   type *dereference () final override { return m_other_type; }
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new memento_of_get_pointer (m_other_type->copy (ctxt));
+    ctxt->record (result);
+    return result;
+  }
+
   size_t get_size () final override;
 
   bool accepts_writes_from (type *rtype) final override;
@@ -724,6 +758,13 @@ public:
     return false;
   }
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new memento_of_get_const (m_other_type->copy (ctxt));
+    ctxt->record (result);
+    return result;
+  }
+
   /* Strip off the "const", giving the underlying type.  */
   type *unqualified () final override { return m_other_type; }
 
@@ -757,6 +798,13 @@ public:
     return m_other_type->is_same_type_as (other->is_volatile ());
   }
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new memento_of_get_volatile (m_other_type->copy (ctxt));
+    ctxt->record (result);
+    return result;
+  }
+
   /* Strip off the "volatile", giving the underlying type.  */
   type *unqualified () final override { return m_other_type; }
 
@@ -777,11 +825,23 @@ public:
   : decorated_type (other_type),
     m_alignment_in_bytes (alignment_in_bytes) {}
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new memento_of_get_aligned (m_other_type->copy (ctxt),
+					       m_alignment_in_bytes);
+    ctxt->record (result);
+    return result;
+  }
+
   /* Strip off the alignment, giving the underlying type.  */
   type *unqualified () final override { return m_other_type; }
 
   void replay_into (replayer *) final override;
 
+  vector_type *dyn_cast_vector_type () final override {
+    return m_other_type->dyn_cast_vector_type ();
+  }
+
 private:
   string * make_debug_string () final override;
   void write_reproducer (reproducer &r) final override;
@@ -798,6 +858,13 @@ public:
   : decorated_type (other_type),
     m_num_units (num_units) {}
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new vector_type (m_other_type->copy (ctxt), m_num_units);
+    ctxt->record (result);
+    return result;
+  }
+
   size_t get_num_units () const { return m_num_units; }
 
   vector_type *dyn_cast_vector_type () final override { return this; }
@@ -840,6 +907,14 @@ class array_type : public type
 
   type *dereference () final override;
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new array_type (ctxt, m_loc, m_element_type->copy (ctxt),
+				   m_num_elements);
+    ctxt->record (result);
+    return result;
+  }
+
   bool is_int () const final override { return false; }
   bool is_float () const final override { return false; }
   bool is_bool () const final override { return false; }
@@ -867,7 +942,8 @@ public:
 		 type *return_type,
 		 int num_params,
 		 type **param_types,
-		 int is_variadic);
+		 int is_variadic,
+		 int is_target_builtin);
 
   type *dereference () final override;
   function_type *dyn_cast_function_type () final override { return this; }
@@ -875,6 +951,20 @@ public:
 
   bool is_same_type_as (type *other) final override;
 
+  type* copy (context* ctxt) final override
+  {
+    auto_vec<type *> new_params{};
+    for (size_t i = 0; i < m_param_types.length (); i++)
+      new_params.safe_push (m_param_types[i]->copy (ctxt));
+
+    type* result = new function_type (ctxt, m_return_type->copy (ctxt),
+				      m_param_types.length (),
+				      new_params.address (),
+				      m_is_variadic, m_is_target_builtin);
+    ctxt->record (result);
+    return result;
+  }
+
   bool is_int () const final override { return false; }
   bool is_float () const final override { return false; }
   bool is_bool () const final override { return false; }
@@ -903,6 +993,7 @@ private:
   type *m_return_type;
   auto_vec<type *> m_param_types;
   int m_is_variadic;
+  int m_is_target_builtin;
 };
 
 class field : public memento
@@ -1004,9 +1095,11 @@ public:
     return static_cast <playback::compound_type *> (m_playback_obj);
   }
 
-private:
+protected:
   location *m_loc;
   string *m_name;
+
+private:
   fields *m_fields;
 };
 
@@ -1019,6 +1112,13 @@ public:
 
   struct_ *dyn_cast_struct () final override { return this; }
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new struct_ (ctxt, m_loc, m_name);
+    ctxt->record (result);
+    return result;
+  }
+
   type *
   as_type () { return this; }
 
@@ -1066,6 +1166,13 @@ public:
 
   void replay_into (replayer *r) final override;
 
+  type* copy (context* ctxt) final override
+  {
+    type* result = new union_ (ctxt, m_loc, m_name);
+    ctxt->record (result);
+    return result;
+  }
+
   bool is_union () const final override { return true; }
 
 private:
@@ -1274,7 +1381,8 @@ public:
 	    int num_params,
 	    param **params,
 	    int is_variadic,
-	    enum built_in_function builtin_id);
+	    enum built_in_function builtin_id,
+	    int is_target_builtin);
 
   void replay_into (replayer *r) final override;
 
@@ -1329,6 +1437,7 @@ private:
   auto_vec<local *> m_locals;
   auto_vec<block *> m_blocks;
   type *m_fn_ptr_type;
+  int m_is_target_builtin;
 };
 
 class block : public memento
@@ -1597,6 +1706,33 @@ private:
   auto_vec<rvalue *> m_elements;
 };
 
+class memento_of_new_rvalue_vector_perm : public rvalue
+{
+public:
+  memento_of_new_rvalue_vector_perm (context *ctxt,
+				     location *loc,
+				     rvalue *elements1,
+				     rvalue *elements2,
+				     rvalue *mask);
+
+  void replay_into (replayer *r) final override;
+
+  void visit_children (rvalue_visitor *) final override;
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+  enum precedence get_precedence () const final override
+  {
+    return PRECEDENCE_PRIMARY;
+  }
+
+private:
+  rvalue *m_elements1;
+  rvalue *m_elements2;
+  rvalue *m_mask;
+};
+
 class ctor : public rvalue
 {
 public:
@@ -1879,6 +2015,36 @@ private:
   rvalue *m_index;
 };
 
+class vector_access : public lvalue
+{
+public:
+  vector_access (context *ctxt,
+		 location *loc,
+		 rvalue *vector,
+		 rvalue *index)
+  : lvalue (ctxt, loc, vector->get_type ()->dyn_cast_vector_type ()
+			     ->get_element_type ()),
+    m_vector (vector),
+    m_index (index)
+  {}
+
+  void replay_into (replayer *r) final override;
+
+  void visit_children (rvalue_visitor *v) final override;
+
+private:
+  string * make_debug_string () final override;
+  void write_reproducer (reproducer &r) final override;
+  enum precedence get_precedence () const final override
+  {
+    return PRECEDENCE_POSTFIX;
+  }
+
+private:
+  rvalue *m_vector;
+  rvalue *m_index;
+};
+
 class access_field_of_lvalue : public lvalue
 {
 public:
diff --git a/gcc/jit/libgccjit.cc b/gcc/jit/libgccjit.cc
index 2def58f6aa7..8056d0c7052 100644
--- a/gcc/jit/libgccjit.cc
+++ b/gcc/jit/libgccjit.cc
@@ -1745,6 +1745,24 @@ gcc_jit_context_new_array_constructor (gcc_jit_context *ctxt,
     reinterpret_cast<gcc::jit::recording::rvalue**>(values));
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::get_target_builtin_function method, in
+   jit-recording.c.  */
+
+gcc_jit_function *
+gcc_jit_context_get_target_builtin_function (gcc_jit_context *ctxt,
+					     const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  RETURN_NULL_IF_FAIL (name, ctxt, NULL, "NULL name");
+
+  return static_cast <gcc_jit_function *> (
+    ctxt->get_target_builtin_function (name));
+}
+
 /* Public entrypoint.  See description in libgccjit.h.  */
 
 extern gcc_jit_lvalue *
@@ -2422,6 +2440,9 @@ gcc_jit_context_new_cast (gcc_jit_context *ctxt,
   /* LOC can be NULL.  */
   RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
   RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  gcc::jit::recording::vector_type *vector_type = type->dyn_cast_vector_type ();
+  RETURN_NULL_IF_FAIL (vector_type == NULL, ctxt, loc,
+		       "cannot cast vector types");
   RETURN_NULL_IF_FAIL_PRINTF3 (
     is_valid_cast (rvalue->get_type (), type),
     ctxt, loc,
@@ -2488,6 +2509,39 @@ gcc_jit_context_new_array_access (gcc_jit_context *ctxt,
   return (gcc_jit_lvalue *)ctxt->new_array_access (loc, ptr, index);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::new_vector_access method in
+   jit-recording.cc.  */
+
+extern gcc_jit_lvalue *
+gcc_jit_context_new_vector_access (gcc_jit_context *ctxt,
+				   gcc_jit_location *loc,
+				   gcc_jit_rvalue *vector,
+				   gcc_jit_rvalue *index)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+  /* LOC can be NULL.  */
+  RETURN_NULL_IF_FAIL (vector, ctxt, loc, "NULL vector");
+  RETURN_NULL_IF_FAIL (index, ctxt, loc, "NULL index");
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    vector->get_type ()->dyn_cast_vector_type (),
+    ctxt, loc,
+    "vector: %s (type: %s) is not a vector",
+    vector->get_debug_string (),
+    vector->get_type ()->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    index->get_type ()->is_numeric (),
+    ctxt, loc,
+    "index: %s (type: %s) is not of numeric type",
+    index->get_debug_string (),
+    index->get_type ()->get_debug_string ());
+
+  return (gcc_jit_lvalue *)ctxt->new_vector_access (loc, vector, index);
+}
+
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
@@ -4056,6 +4110,79 @@ gcc_jit_context_new_rvalue_from_vector (gcc_jit_context *ctxt,
      (gcc::jit::recording::rvalue **)elements);
 }
 
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::context::new_rvalue_vector_perm method, in
+   jit-recording.cc.  */
+
+gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_vector_perm (gcc_jit_context *ctxt,
+					gcc_jit_location *loc,
+					gcc_jit_rvalue *elements1,
+					gcc_jit_rvalue *elements2,
+					gcc_jit_rvalue *mask)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL ctxt");
+  JIT_LOG_FUNC (ctxt->get_logger ());
+
+  /* LOC can be NULL.  */
+
+  gcc::jit::recording::type *elements1_type = elements1->get_type ();
+  gcc::jit::recording::type *elements2_type = elements2->get_type ();
+  RETURN_NULL_IF_FAIL_PRINTF4 (
+    compatible_types (elements1->get_type ()->unqualified (),
+		      elements2->get_type ()->unqualified ()),
+    ctxt, loc,
+    "mismatching types for vector perm:"
+    " elements1: %s (type: %s) elements2: %s (type: %s)",
+    elements1->get_debug_string (),
+    elements1_type->get_debug_string (),
+    elements2->get_debug_string (),
+    elements2_type->get_debug_string ());
+
+  gcc::jit::recording::type *mask_type = mask->get_type ();
+  gcc::jit::recording::vector_type *mask_vector_type =
+    mask_type->dyn_cast_vector_type ();
+  gcc::jit::recording::vector_type *elements1_vector_type =
+    elements1_type->dyn_cast_vector_type ();
+
+  size_t mask_len = mask_vector_type->get_num_units ();
+  size_t elements1_len = elements1_vector_type->get_num_units ();
+
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    mask_len == elements1_len,
+    ctxt, loc,
+    "mismatching length for mask:"
+    " elements1 length: %ld mask length: %ld",
+    mask_len,
+    elements1_len);
+
+  gcc::jit::recording::type *mask_element_type =
+    mask_vector_type->get_element_type ();
+
+  RETURN_NULL_IF_FAIL (
+    mask_element_type->is_int (),
+    ctxt, loc,
+    "elements of mask must be of an integer type");
+
+  gcc::jit::recording::type *elements1_element_type =
+    elements1_vector_type->get_element_type ();
+  size_t mask_element_size = mask_element_type->get_size ();
+  size_t elements1_element_size = elements1_element_type->get_size ();
+
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    mask_element_size == elements1_element_size,
+    ctxt, loc,
+    "mismatching size for mask element type:"
+    " elements1 element type: %ld mask element type: %ld",
+    mask_element_size,
+    elements1_element_size);
+
+  return (gcc_jit_rvalue *)ctxt->new_rvalue_vector_perm (loc, elements1,
+							 elements2, mask);
+}
+
 /* A mutex around the cached state in parse_basever.
    Ideally this would be within parse_basever, but the mutex is only needed
    by libgccjit.  */
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 057d3e58e73..821ae0f7a56 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -604,7 +604,9 @@ enum gcc_jit_types
   GCC_JIT_TYPE_INT16_T,
   GCC_JIT_TYPE_INT32_T,
   GCC_JIT_TYPE_INT64_T,
-  GCC_JIT_TYPE_INT128_T
+  GCC_JIT_TYPE_INT128_T,
+
+  GCC_JIT_TYPE_BFLOAT16,
 };
 
 extern gcc_jit_type *
@@ -1021,6 +1023,17 @@ extern gcc_jit_lvalue *
 gcc_jit_global_set_initializer_rvalue (gcc_jit_lvalue *global,
 				       gcc_jit_rvalue *init_value);
 
+/* Create a reference to a machine-specific builtin function (sometimes called
+   intrinsic functions).
+
+   This API entrypoint was added in LIBGCCJIT_ABI_25; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_TARGET_BUILTIN
+*/
+extern gcc_jit_function *
+gcc_jit_context_get_target_builtin_function (gcc_jit_context *ctxt,
+					     const char *name);
+
 #define LIBGCCJIT_HAVE_gcc_jit_global_set_initializer
 
 /* Set an initial value for a global, which must be an array of
@@ -1292,6 +1305,20 @@ gcc_jit_context_new_array_access (gcc_jit_context *ctxt,
 				  gcc_jit_rvalue *ptr,
 				  gcc_jit_rvalue *index);
 
+#define LIBGCCJIT_HAVE_TARGET_BUILTIN
+
+/* Get the element at INDEX in VECTOR.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_25; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_TARGET_BUILTIN
+*/
+extern gcc_jit_lvalue *
+gcc_jit_context_new_vector_access (gcc_jit_context *ctxt,
+				   gcc_jit_location *loc,
+				   gcc_jit_rvalue *vector,
+				   gcc_jit_rvalue *index);
+
 /* Field access is provided separately for both lvalues and rvalues.  */
 
 /* Accessing a field of an lvalue of struct type, analogous to:
@@ -1806,6 +1833,21 @@ gcc_jit_context_new_rvalue_from_vector (gcc_jit_context *ctxt,
 					size_t num_elements,
 					gcc_jit_rvalue **elements);
 
+/* Build a permutation vector rvalue from an 3 arrays of elements.
+
+   "vec_type" should be a vector type, created using gcc_jit_type_get_vector.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_25; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_TARGET_BUILTIN
+*/
+extern gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_vector_perm (gcc_jit_context *ctxt,
+					gcc_jit_location *loc,
+					gcc_jit_rvalue *elements1,
+					gcc_jit_rvalue *elements2,
+					gcc_jit_rvalue *mask);
+
 #define LIBGCCJIT_HAVE_gcc_jit_version
 
 /* Functions to retrieve libgccjit version.
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 25463b94fe8..57750e298e4 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -271,3 +271,10 @@ LIBGCCJIT_ABI_24 {
     gcc_jit_lvalue_set_alignment;
     gcc_jit_lvalue_get_alignment;
 } LIBGCCJIT_ABI_23;
+
+LIBGCCJIT_ABI_25 {
+  global:
+    gcc_jit_context_get_target_builtin_function;
+    gcc_jit_context_new_rvalue_vector_perm;
+    gcc_jit_context_new_vector_access;
+} LIBGCCJIT_ABI_24;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 80606076e78..988e14ae9bc 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -319,6 +319,9 @@
 /* test-setting-alignment.c: This can't be in the testcases array as it
    is target-specific.  */
 
+/* test-target-builtins.c: This can't be in the testcases array as it
+   is target-specific.  */
+
 /* test-string-literal.c */
 #define create_code create_code_string_literal
 #define verify_code verify_code_string_literal
diff --git a/gcc/testsuite/jit.dg/test-target-builtins.c b/gcc/testsuite/jit.dg/test-target-builtins.c
new file mode 100644
index 00000000000..0ffb48e97f6
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-target-builtins.c
@@ -0,0 +1,77 @@
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#define TEST_PROVIDES_MAIN
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  CHECK_NON_NULL (gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_ia32_xgetbv"));
+  gcc_jit_function *builtin_eh_pointer = gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_eh_pointer");
+  CHECK_NON_NULL (builtin_eh_pointer);
+
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *void_ptr =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID_PTR);
+  gcc_jit_function *func_main =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  int_type,
+				  "main",
+				  0, NULL,
+				  0);
+  gcc_jit_rvalue *zero = gcc_jit_context_zero (ctxt, int_type);
+  gcc_jit_block *block = gcc_jit_function_new_block (func_main, NULL);
+  gcc_jit_lvalue *variable = gcc_jit_function_new_local(func_main, NULL, void_ptr, "variable");
+  gcc_jit_block_add_assignment (block, NULL, variable,
+    gcc_jit_context_new_call (ctxt, NULL, builtin_eh_pointer, 1, &zero));
+  gcc_jit_block_end_with_return (block, NULL, zero);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_NON_NULL (result);
+}
+
+int
+main (int argc, char **argv)
+{
+  /*  This is the same as the main provided by harness.h, but it first create a dummy context and compile
+      in order to add the target builtins to libgccjit's internal state.  */
+  gcc_jit_context *ctxt;
+  ctxt = gcc_jit_context_acquire ();
+  if (!ctxt)
+    {
+      fail ("gcc_jit_context_acquire failed");
+      return -1;
+    }
+  gcc_jit_result *result;
+  result = gcc_jit_context_compile (ctxt);
+  gcc_jit_result_release (result);
+  gcc_jit_context_release (ctxt);
+
+  int i;
+
+  for (i = 1; i <= 5; i++)
+    {
+      snprintf (test, sizeof (test),
+		"%s iteration %d of %d",
+                extract_progname (argv[0]),
+                i, 5);
+
+      //printf ("ITERATION %d\n", i);
+      test_jit (argv[0], NULL);
+      //printf ("\n");
+    }
+
+  totals ();
+
+  return 0;
+}
-- 
2.26.2.7.g19db9cfb68.dirty


  reply	other threads:[~2023-02-12  0:32 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-12  0:30 Antoni Boucher
2023-02-12  0:32 ` Antoni Boucher [this message]
2023-02-12  1:37 ` Andrew Pinski
2023-11-23 22:17   ` Antoni Boucher
2023-11-23 22:21     ` Antoni Boucher
2024-01-10 23:29   ` Antoni Boucher
2024-01-10 23:44     ` David Malcolm
2024-01-10 23:58       ` Antoni Boucher
2024-02-08 13:59         ` Antoni Boucher
2024-02-15 14:32           ` Antoni Boucher
2024-02-29 15:34             ` Antoni Boucher
2024-04-19 12:35               ` Antoni Boucher

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=75551971f85c70f8e3df8584d81ced627e293177.camel@zoho.com \
    --to=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).