[committed] jit: add gcc_jit_type_get_vector

Message ID 1502326873-58234-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Aug. 10, 2017, 1:01 a.m.
On Wed, 2017-08-09 at 20:42 +1200, Michael Cree wrote:
> On Mon, Aug 07, 2017 at 10:28:57AM -0400, David Malcolm wrote:
> > On Mon, 2017-08-07 at 20:12 +1200, Michael Cree wrote:
> > 
> > Hi Michael
> > 
> > > I am wondering if libgccjit supports vector types, i.e., can one
> > > attach attribute __vector_size__ to the base types?  There does
> > > not
> > > seem to be any indication of this in the manual.
> > 
> > Currently this isn't supported.
> > 
> > Is this a blocker for you using libgccjit?  
> 
> Yeah, it is.  The idea is to generate image processing operators
> on the fly.  There are opportunities for various optimisations that
> can only be decided on at runtime.
> 
> > What would the ideal API
> > look like? 
> > 
> > Maybe something like:
> > 
> >   extern gcc_jit_type *
> >   gcc_jit_type_get_vector (gcc_jit_type *type, unsigned nunits);
> >  
> > with various requirements (type must be integral/floating point;
> > nunits
> > must be a power of two).
> 
> I suspect that would do the job nicely.
> 
> Cheers
> Michael.

I implemented the above (although I switched the 2nd arg to be
"size_t num_units").

It looks like you may not need to explicitly use builtins to
access machine specific simd intrinsics; for example, on x86_64
when I tried multiplying two of these together for float, with
GCC_JIT_BINARY_OP_MULT, which led to this gimple:

jit_v4f_mult (const vector(4) <float:32> * a, const vector(4) <float:32> * b, vector(4) <float:32> * c)
{
  initial:
  _1 = *a;
  _2 = *b;
  _3 = _1 * _2;
  *c = _3;
  return;
}

on this x86_64 box it compiled to:

	movaps	(%rdi), %xmm0
	mulps	(%rsi), %xmm0
	movaps	%xmm0, (%rdx)
	ret

(i.e. using the "mulps" SIMD instruction).

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu;
takes jit.sum from 9349 passes to 9759.

Committed to trunk as r251018.

gcc/jit/ChangeLog:
	* docs/cp/topics/types.rst (Vector types): New section.
	* docs/topics/compatibility.rst (LIBGCCJIT_ABI_8): New tag.
	* docs/topics/types.rst (gcc_jit_context_get_type): Fix typo in
	example.
	(Vector types): New section.
	* docs/_build/texinfo/libgccjit.texi: Regenerate.
	* jit-playback.c (gcc::jit::playback::type::get_vector): New
	method.
	* jit-playback.h (gcc::jit::playback::type::get_vector): New
	method.
	* jit-recording.c: In namespace gcc::jit::recording::
	(type::get_vector): New method.
	(memento_of_get_aligned::write_reproducer): Fix typo
	in leading comment.
	(memento_of_get_vector::replay_into): New method.
	(memento_of_get_vector::make_debug_string): New method.
	(memento_of_get_vector::write_reproducer): New method.
	* jit-recording.h: In namespace gcc::jit::recording::
	(type::get_vector): New
	 method.
	(class memento_of_get_vector): New class.
	* libgccjit++.h (gccjit::type::get_vector): New method.
	* libgccjit.c (gcc_jit_type_get_vector): New public entrypoint.
	* libgccjit.h (LIBGCCJIT_HAVE_gcc_jit_type_get_vector): New
	define.
	(gcc_jit_type_get_vector): New decl.
	* libgccjit.map (LIBGCCJIT_ABI_8): New ABI tag.

gcc/testsuite/ChangeLog:
	* jit.dg/all-non-failing-tests.h: Add note about
	test-vector-types.cc.
	* jit.dg/test-error-gcc_jit_type_get_vector-bad-type.c: New test
	case.
	* jit.dg/test-error-gcc_jit_type_get_vector-non-power-of-two.c:
	New test case.
	* jit.dg/test-vector-types.cc: New test case.
---
 gcc/jit/docs/cp/topics/types.rst                   |  14 ++
 gcc/jit/docs/topics/compatibility.rst              |   7 +
 gcc/jit/docs/topics/types.rst                      |  43 ++++-
 gcc/jit/jit-playback.c                             |  11 ++
 gcc/jit/jit-playback.h                             |   1 +
 gcc/jit/jit-recording.c                            |  56 ++++++-
 gcc/jit/jit-recording.h                            |  26 ++-
 gcc/jit/libgccjit++.h                              |   8 +
 gcc/jit/libgccjit.c                                |  28 ++++
 gcc/jit/libgccjit.h                                |  15 ++
 gcc/jit/libgccjit.map                              |   5 +
 gcc/testsuite/jit.dg/all-non-failing-tests.h       |   2 +
 .../test-error-gcc_jit_type_get_vector-bad-type.c  |  30 ++++
 ...rror-gcc_jit_type_get_vector-non-power-of-two.c |  29 ++++
 gcc/testsuite/jit.dg/test-vector-types.cc          | 185 +++++++++++++++++++++
 15 files changed, 456 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-bad-type.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-non-power-of-two.c
 create mode 100644 gcc/testsuite/jit.dg/test-vector-types.cc

Patch

diff --git a/gcc/jit/docs/cp/topics/types.rst b/gcc/jit/docs/cp/topics/types.rst
index e85a492..1df896e 100644
--- a/gcc/jit/docs/cp/topics/types.rst
+++ b/gcc/jit/docs/cp/topics/types.rst
@@ -109,6 +109,20 @@  Pointers, `const`, and `volatile`
    Param "loc" is optional.
 
 
+Vector types
+------------
+
+.. function::  gccjit::type gccjit::type::get_vector (size_t num_units)
+
+   Given type "T", get type:
+
+   .. code-block:: c
+
+      T  __attribute__ ((vector_size (sizeof(T) * num_units))
+
+   T must be integral or floating point; num_units must be a power of two.
+
+
 Structures and unions
 ---------------------
 
diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst
index 5a13653..1d5fbc2 100644
--- a/gcc/jit/docs/topics/compatibility.rst
+++ b/gcc/jit/docs/topics/compatibility.rst
@@ -149,3 +149,10 @@  entrypoints:
 -------------------
 ``LIBGCCJIT_ABI_7`` covers the addition of
 :func:`gcc_jit_type_get_aligned`
+
+.. _LIBGCCJIT_ABI_8:
+
+``LIBGCCJIT_ABI_8``
+-------------------
+``LIBGCCJIT_ABI_8`` covers the addition of
+:func:`gcc_jit_type_get_vector`
diff --git a/gcc/jit/docs/topics/types.rst b/gcc/jit/docs/topics/types.rst
index 119f10e..c279222 100644
--- a/gcc/jit/docs/topics/types.rst
+++ b/gcc/jit/docs/topics/types.rst
@@ -35,7 +35,7 @@  Types can be created in several ways:
 
   .. code-block:: c
 
-      gcc_jit_type *int_type = gcc_jit_context_get_type (GCC_JIT_TYPE_INT);
+      gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
 
   See :func:`gcc_jit_context_get_type` for the available types.
 
@@ -136,6 +136,47 @@  Pointers, `const`, and `volatile`
 
       #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_aligned
 
+Vector types
+------------
+
+.. function::  gcc_jit_type *\
+               gcc_jit_type_get_vector (gcc_jit_type *type, \
+                                        size_t num_units)
+
+   Given type "T", get type:
+
+   .. code-block:: c
+
+      T  __attribute__ ((vector_size (sizeof(T) * num_units))
+
+   T must be integral or floating point; num_units must be a power of two.
+
+   This can be used to construct a vector type in which operations
+   are applied element-wise.  The compiler will automatically
+   use SIMD instructions where possible.  See:
+   https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
+
+   For example, assuming 4-byte ``ints``, then:
+
+   .. code-block:: c
+
+      typedef int v4si __attribute__ ((vector_size (16)));
+
+   can be obtained using:
+
+   .. code-block:: c
+
+      gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt,
+                                                         GCC_JIT_TYPE_INT);
+      gcc_jit_type *v4si_type = gcc_jit_type_get_vector (int_type, 4);
+
+   This API entrypoint was added in :ref:`LIBGCCJIT_ABI_8`; you can test
+   for its presence using
+
+   .. code-block:: c
+
+      #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_vector
+
 
 Structures and unions
 ---------------------
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 76cc88f..19b6fe2 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -1110,6 +1110,17 @@  get_aligned (size_t alignment_in_bytes) const
   return new type (t_new_type);
 }
 
+/* Construct a playback::type instance (wrapping a tree)
+   for the given vector type.  */
+
+playback::type *
+playback::type::
+get_vector (size_t num_units) const
+{
+  tree t_new_type = build_vector_type (m_inner, num_units);
+  return new type (t_new_type);
+}
+
 /* Construct a playback::lvalue instance (wrapping a tree) for a
    field access.  */
 
diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h
index 0a83390..7dc7315 100644
--- a/gcc/jit/jit-playback.h
+++ b/gcc/jit/jit-playback.h
@@ -392,6 +392,7 @@  public:
   }
 
   type *get_aligned (size_t alignment_in_bytes) const;
+  type *get_vector (size_t num_units) const;
 
 private:
   tree m_inner;
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index 723ddb3..ea4ebb1 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -1988,6 +1988,20 @@  recording::type::get_aligned (size_t alignment_in_bytes)
   return result;
 }
 
+/* Given a type, get a vector version of the type.
+
+   Implements the post-error-checking part of
+   gcc_jit_type_get_vector.  */
+
+recording::type *
+recording::type::get_vector (size_t num_units)
+{
+  recording::type *result
+    = new memento_of_get_vector (this, num_units);
+  m_ctxt->record (result);
+  return result;
+}
+
 const char *
 recording::type::access_as_type (reproducer &r)
 {
@@ -2457,7 +2471,7 @@  recording::memento_of_get_aligned::make_debug_string ()
 			      m_alignment_in_bytes);
 }
 
-/* Implementation of recording::memento::write_reproducer for volatile
+/* Implementation of recording::memento::write_reproducer for aligned
    types. */
 
 void
@@ -2471,6 +2485,46 @@  recording::memento_of_get_aligned::write_reproducer (reproducer &r)
 	   m_alignment_in_bytes);
 }
 
+/* The implementation of class gcc::jit::recording::memento_of_get_vector.  */
+
+/* Implementation of pure virtual hook recording::memento::replay_into
+   for recording::memento_of_get_vector.  */
+
+void
+recording::memento_of_get_vector::replay_into (replayer *)
+{
+  set_playback_obj
+    (m_other_type->playback_type ()->get_vector (m_num_units));
+}
+
+/* Implementation of recording::memento::make_debug_string for
+   results of get_vector.  */
+
+recording::string *
+recording::memento_of_get_vector::make_debug_string ()
+{
+  return string::from_printf
+    (m_ctxt,
+     "%s  __attribute__((vector_size(sizeof (%s) * %zi)))",
+     m_other_type->get_debug_string (),
+     m_other_type->get_debug_string (),
+     m_num_units);
+}
+
+/* Implementation of recording::memento::write_reproducer for volatile
+   types. */
+
+void
+recording::memento_of_get_vector::write_reproducer (reproducer &r)
+{
+  const char *id = r.make_identifier (this, "type");
+  r.write ("  gcc_jit_type *%s =\n"
+	   "    gcc_jit_type_get_vector (%s, %zi);\n",
+	   id,
+	   r.get_identifier_as_type (m_other_type),
+	   m_num_units);
+}
+
 /* The implementation of class gcc::jit::recording::array_type */
 
 /* Implementation of pure virtual hook recording::type::dereference for
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index 5faf35e..248765d 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -474,6 +474,7 @@  public:
   type *get_const ();
   type *get_volatile ();
   type *get_aligned (size_t alignment_in_bytes);
+  type *get_vector (size_t num_units);
 
   /* Get the type obtained when dereferencing this type.
 
@@ -600,8 +601,8 @@  private:
   type *m_other_type;
 };
 
-/* A decorated version of a type, for get_const, get_volatile and
-   get_aligned.  */
+/* A decorated version of a type, for get_const, get_volatile,
+   get_aligned, and get_vector.  */
 
 class decorated_type : public type
 {
@@ -683,6 +684,27 @@  private:
   size_t m_alignment_in_bytes;
 };
 
+/* Result of "gcc_jit_type_get_vector".  */
+class memento_of_get_vector : public decorated_type
+{
+public:
+  memento_of_get_vector (type *other_type, size_t num_units)
+  : decorated_type (other_type),
+    m_num_units (num_units) {}
+
+  /* Strip off the alignment, giving the underlying type.  */
+  type *unqualified () FINAL OVERRIDE { return m_other_type; }
+
+  void replay_into (replayer *) FINAL OVERRIDE;
+
+private:
+  string * make_debug_string () FINAL OVERRIDE;
+  void write_reproducer (reproducer &r) FINAL OVERRIDE;
+
+private:
+  size_t m_num_units;
+};
+
 class array_type : public type
 {
  public:
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index ea277f8..a83ccf6 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -333,6 +333,7 @@  namespace gccjit
     type get_const ();
     type get_volatile ();
     type get_aligned (size_t alignment_in_bytes);
+    type get_vector (size_t num_units);
 
     // Shortcuts for getting values of numeric types:
     rvalue zero ();
@@ -1306,6 +1307,13 @@  type::get_aligned (size_t alignment_in_bytes)
 					 alignment_in_bytes));
 }
 
+inline type
+type::get_vector (size_t num_units)
+{
+  return type (gcc_jit_type_get_vector (get_inner_type (),
+					num_units));
+}
+
 inline rvalue
 type::zero ()
 {
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 9b003e3..6e352c6 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -2994,3 +2994,31 @@  gcc_jit_type_get_aligned (gcc_jit_type *type,
 
   return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
 }
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
+   gcc::jit::recording::type::get_vector method, in
+   jit-recording.c.  */
+
+gcc_jit_type *
+gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units)
+{
+  RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
+
+  gcc::jit::recording::context *ctxt = type->m_ctxt;
+
+  JIT_LOG_FUNC (ctxt->get_logger ());
+
+  RETURN_NULL_IF_FAIL_PRINTF1
+    (type->is_int () || type->is_float (), ctxt, NULL,
+     "type is not integral or floating point: %s",
+     type->get_debug_string ());
+
+  RETURN_NULL_IF_FAIL_PRINTF1
+    (pow2_or_zerop (num_units), ctxt, NULL,
+     "num_units not a power of two: %zi",
+     num_units);
+
+  return (gcc_jit_type *)type->get_vector (num_units);
+}
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 898ee98..b863b07 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -1403,6 +1403,21 @@  extern gcc_jit_type *
 gcc_jit_type_get_aligned (gcc_jit_type *type,
 			  size_t alignment_in_bytes);
 
+#define LIBGCCJIT_HAVE_gcc_jit_type_get_vector
+
+/* Given type "T", get type:
+
+     T  __attribute__ ((vector_size (sizeof(T) * num_units))
+
+   T must be integral/floating point; num_units must be a power of two.
+
+   This API entrypoint was added in LIBGCCJIT_ABI_8; you can test for its
+   presence using
+     #ifdef LIBGCCJIT_HAVE_gcc_jit_type_get_vector
+*/
+extern gcc_jit_type *
+gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 160f4cd..08760e3 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -155,3 +155,8 @@  LIBGCCJIT_ABI_7 {
   global:
     gcc_jit_type_get_aligned;
 } LIBGCCJIT_ABI_6;
+
+LIBGCCJIT_ABI_8 {
+  global:
+    gcc_jit_type_get_vector;
+} LIBGCCJIT_ABI_7;
diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h
index 58e0c30..4af704a 100644
--- a/gcc/testsuite/jit.dg/all-non-failing-tests.h
+++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h
@@ -228,6 +228,8 @@ 
    of gcc_jit_context_set_bool_allow_unreachable_blocks affects the whole
    context.  */
 
+/* test-vector-types.cc: We don't use this, since it's C++.  */
+
 /* test-volatile.c */
 #define create_code create_code_volatile
 #define verify_code verify_code_volatile
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-bad-type.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-bad-type.c
new file mode 100644
index 0000000..59aed33
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-bad-type.c
@@ -0,0 +1,30 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Trigger an API error by passing a bad type.  */
+  (void)gcc_jit_type_get_vector (void_type, 4);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  /* Ensure that the bad API usage prevents the API giving a bogus
+     result back.  */
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      ("gcc_jit_type_get_vector:"
+		       " type is not integral or floating point: void"));
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-non-power-of-two.c b/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-non-power-of-two.c
new file mode 100644
index 0000000..aa3bdbd
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-gcc_jit_type_get_vector-non-power-of-two.c
@@ -0,0 +1,29 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Trigger an API error by passing a bad number of units.  */
+  (void)gcc_jit_type_get_vector (int_type, 7);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  /* Ensure that the bad API usage prevents the API giving a bogus
+     result back.  */
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      ("gcc_jit_type_get_vector:"
+		       " num_units not a power of two: 7"));
+}
diff --git a/gcc/testsuite/jit.dg/test-vector-types.cc b/gcc/testsuite/jit.dg/test-vector-types.cc
new file mode 100644
index 0000000..3389e04
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-vector-types.cc
@@ -0,0 +1,185 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+typedef int v4si __attribute__ ((vector_size (16)));
+typedef unsigned int v4ui __attribute__ ((vector_size (16)));
+typedef float v4f __attribute__ ((vector_size (16)));
+
+static void
+create_vec_fn (gcc_jit_context *ctxt, const char *fnname,
+	       gcc_jit_type *the_type, enum gcc_jit_binary_op op)
+{
+  /* Create equivalent to:
+
+       static void
+       FNNAME (const T *a, const T *b, T *c)
+       {
+         *c = *a OP *b;
+       }
+
+     where T is "the_type" (e.g. v4si).  */
+
+  gcc_jit_type *ptr_type = gcc_jit_type_get_pointer (the_type);
+
+  gcc_jit_type *const_type = gcc_jit_type_get_const (the_type);
+  gcc_jit_type *ptr_to_const_type = gcc_jit_type_get_pointer (const_type);
+
+  gcc_jit_param *a =
+    gcc_jit_context_new_param (ctxt, NULL, ptr_to_const_type, "a");
+  gcc_jit_param *b =
+    gcc_jit_context_new_param (ctxt, NULL, ptr_to_const_type, "b");
+  gcc_jit_param *c =
+    gcc_jit_context_new_param (ctxt, NULL, ptr_type, "c");
+
+  gcc_jit_type *return_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  gcc_jit_param *params[3] = {a, b, c};
+  gcc_jit_function *func =
+    gcc_jit_context_new_function (ctxt, NULL,
+				  GCC_JIT_FUNCTION_EXPORTED,
+				  return_type,
+				  fnname,
+				  3, params, 0);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (func, "initial");
+
+  /* (*a OP *b) */
+  gcc_jit_rvalue *op_result =
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      op,
+      the_type,
+      gcc_jit_lvalue_as_rvalue (gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (a),
+							    NULL)),
+      gcc_jit_lvalue_as_rvalue (gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (b),
+							    NULL)));
+  /* *c = *a OP *b; */
+  gcc_jit_block_add_assignment (
+    initial, NULL,
+    gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (c), NULL),
+    op_result);
+  gcc_jit_block_end_with_void_return (initial, NULL);
+}
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *unsigned_type
+    = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_UNSIGNED_INT);
+  gcc_jit_type *float_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_FLOAT);
+
+  gcc_jit_type *v4si_type = gcc_jit_type_get_vector (int_type, 4);
+  gcc_jit_type *v4ui_type = gcc_jit_type_get_vector (unsigned_type, 4);
+  gcc_jit_type *v4f_type = gcc_jit_type_get_vector (float_type, 4);
+
+  create_vec_fn (ctxt, "jit_v4si_add",
+		 v4si_type, GCC_JIT_BINARY_OP_PLUS);
+  create_vec_fn (ctxt, "jit_v4si_sub",
+		 v4si_type, GCC_JIT_BINARY_OP_MINUS);
+  create_vec_fn (ctxt, "jit_v4si_mult",
+		 v4si_type, GCC_JIT_BINARY_OP_MULT);
+  create_vec_fn (ctxt, "jit_v4si_div",
+		 v4si_type, GCC_JIT_BINARY_OP_DIVIDE);
+
+  create_vec_fn (ctxt, "jit_v4ui_add",
+		 v4ui_type, GCC_JIT_BINARY_OP_PLUS);
+  create_vec_fn (ctxt, "jit_v4ui_sub",
+		 v4ui_type, GCC_JIT_BINARY_OP_MINUS);
+  create_vec_fn (ctxt, "jit_v4ui_mult",
+		 v4ui_type, GCC_JIT_BINARY_OP_MULT);
+  create_vec_fn (ctxt, "jit_v4ui_div",
+		 v4ui_type, GCC_JIT_BINARY_OP_DIVIDE);
+
+  create_vec_fn (ctxt, "jit_v4f_add",
+		 v4f_type, GCC_JIT_BINARY_OP_PLUS);
+  create_vec_fn (ctxt, "jit_v4f_sub",
+		 v4f_type, GCC_JIT_BINARY_OP_MINUS);
+  create_vec_fn (ctxt, "jit_v4f_mult",
+		 v4f_type, GCC_JIT_BINARY_OP_MULT);
+  create_vec_fn (ctxt, "jit_v4f_div",
+		 v4f_type, GCC_JIT_BINARY_OP_DIVIDE);
+}
+
+template <typename T>
+void
+check_add (const T &a, const T &b, const T &c)
+{
+  for (int i = 0; i < 4; i++)
+    CHECK_VALUE (c[i], a[i] + b[i]);
+}
+
+template <typename T>
+void
+check_sub (const T &a, const T &b, const T &c)
+{
+  for (int i = 0; i < 4; i++)
+    CHECK_VALUE (c[i], a[i] - b[i]);
+}
+
+template <typename T>
+void
+check_mult (const T &a, const T &b, const T &c)
+{
+  for (int i = 0; i < 4; i++)
+    CHECK_VALUE (c[i], a[i] * b[i]);
+}
+
+template <typename T>
+void
+check_div (const T &a, const T &b, const T &c)
+{
+  for (int i = 0; i < 4; i++)
+    CHECK_VALUE (c[i], a[i] / b[i]);
+}
+
+template <typename T>
+void
+verify_vec_code (gcc_jit_context *ctxt, gcc_jit_result *result,
+		 const char *fnname,
+		 void (*check_cb) (const T &a, const T &b, const T &c))
+{
+  typedef void (*binop_type) (const T *a, const T *b, T *c);
+  CHECK_NON_NULL (result);
+  binop_type fn =
+    (binop_type)gcc_jit_result_get_code (result, fnname);
+  CHECK_NON_NULL (fn);
+
+  T a, b, c;
+
+  /* Init.  */
+  for (int i = 0; i < 4; i++)
+    {
+      a[i] = i + 5;
+      b[i] = (i + 4) * 3;
+    }
+
+  /* Run jit-compiled code and verify result.  */
+  fn (&a, &b, &c);
+  check_cb (a, b, c);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  verify_vec_code<v4si> (ctxt, result, "jit_v4si_add", check_add);
+  verify_vec_code<v4si> (ctxt, result, "jit_v4si_sub", check_sub);
+  verify_vec_code<v4si> (ctxt, result, "jit_v4si_mult", check_mult);
+  verify_vec_code<v4si> (ctxt, result, "jit_v4si_div", check_div);
+
+  verify_vec_code<v4ui> (ctxt, result, "jit_v4ui_add", check_add);
+  verify_vec_code<v4ui> (ctxt, result, "jit_v4ui_sub", check_sub);
+  verify_vec_code<v4ui> (ctxt, result, "jit_v4ui_mult", check_mult);
+  verify_vec_code<v4ui> (ctxt, result, "jit_v4ui_div", check_div);
+
+  verify_vec_code<v4f> (ctxt, result, "jit_v4f_add", check_add);
+  verify_vec_code<v4f> (ctxt, result, "jit_v4f_sub", check_sub);
+  verify_vec_code<v4f> (ctxt, result, "jit_v4f_mult", check_mult);
+  verify_vec_code<v4f> (ctxt, result, "jit_v4f_div", check_div);
+}