public inbox for jit@gcc.gnu.org
 help / color / mirror / Atom feed
From: Marc Nieper-Wisskirchen <marc@nieper-wisskirchen.de>
To: gcc-patches@gcc.gnu.org, jit@gcc.gnu.org
Cc: marc.nieper+gnu@gmail.com,
	Marc Nieper-Wisskirchen <marc@nieper-wisskirchen.de>
Subject: [PATCH] libgccjit: structs/unions: bring the C++ API more on par with C
Date: Mon, 31 Jan 2022 12:25:40 +0100	[thread overview]
Message-ID: <20220131112540.429699-1-marc@nieper-wisskirchen.de> (raw)

This patch adds yet missing C++ API entry points to create union types
and to set the fields of structs previously defined as opaque.  A
missing convenience method returning NULL pointer values is also added
and used in one of the two provided tests.

gcc/jit

	* docs/cp/topics/expressions.rst: Update doc.
	* docs/cp/topics/types.rst: Update doc.
	* libgccjit++.h (context::new_union_type): New method.
	(context::null): New method.
	(type::null): New method.
	(struct_::set_fields): New method.

gcc/testsuite

	* jit.dg/test-accessing-union.cc: New test.
	* jit.dg/test-linked-list.cc: New test.
---
 gcc/jit/docs/cp/topics/expressions.rst       |  10 ++
 gcc/jit/docs/cp/topics/types.rst             |  27 +++-
 gcc/jit/libgccjit++.h                        |  61 ++++++++-
 gcc/testsuite/jit.dg/test-accessing-union.cc |  86 +++++++++++++
 gcc/testsuite/jit.dg/test-linked-list.cc     | 128 +++++++++++++++++++
 5 files changed, 304 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-accessing-union.cc
 create mode 100644 gcc/testsuite/jit.dg/test-linked-list.cc

diff --git a/gcc/jit/docs/cp/topics/expressions.rst b/gcc/jit/docs/cp/topics/expressions.rst
index 239e004371e..3e9534790a3 100644
--- a/gcc/jit/docs/cp/topics/expressions.rst
+++ b/gcc/jit/docs/cp/topics/expressions.rst
@@ -86,6 +86,16 @@ Simple expressions
 
       ctxt.new_rvalue (numeric_type, 1)
 
+.. function::  gccjit::rvalue \
+               gccjit::context::null (gccjit::type pointer_type) const
+
+   Given a pointer type, get the rvalue for the null pointer.
+   Essentially this is just a shortcut for:
+
+   .. code-block:: c++
+
+      ctxt.new_rvalue (pointer_type, (void *) 0)
+
 .. function::  gccjit::rvalue \
                gccjit::context::new_rvalue (gccjit::type numeric_type, \
                                             double value) const
diff --git a/gcc/jit/docs/cp/topics/types.rst b/gcc/jit/docs/cp/topics/types.rst
index c695ceb3098..f41f504da6a 100644
--- a/gcc/jit/docs/cp/topics/types.rst
+++ b/gcc/jit/docs/cp/topics/types.rst
@@ -179,8 +179,6 @@ You can model C `struct` types by creating :class:`gccjit::struct_` and
     fields.push_back (field_next);
     node.set_fields (fields);
 
-.. FIXME: the above API doesn't seem to exist yet
-
 .. function:: gccjit::field \
               gccjit::context::new_field (gccjit::type type,\
                                           const char *name, \
@@ -196,10 +194,25 @@ You can model C `struct` types by creating :class:`gccjit::struct_` and
      Construct a new struct type, with the given name and fields.
 
 .. function:: gccjit::struct_ \
-              gccjit::context::new_opaque_struct (const std::string &name, \
-                                                  gccjit::location loc)
+   gccjit::context::new_opaque_struct (const std::string &name, \
+	                               gccjit::location loc)
 
      Construct a new struct type, with the given name, but without
-     specifying the fields.   The fields can be omitted (in which case the
-     size of the struct is not known), or later specified using
-     :c:func:`gcc_jit_struct_set_fields`.
+     specifying the fields.  The fields can be omitted (in which case
+     the size of the struct is not known), or later specified using
+     :func:`set_fields`.
+
+.. function:: void \
+	      gccjit::struct_::set_fields (std::vector <field> &fields, \
+	                                   gccjit::location loc)
+
+    Populate the fields of a formerly-opaque struct type.
+
+    This can only be called once on a given struct type.
+
+.. function:: gccjit::type \
+	      gccjit::context::new_union_type (const std::string &name, \
+	                                       std::vector<field> &fields,\
+					       gccjit::location loc)
+
+   Construct a new union type, with the given name and fields.
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 4b88e877bc9..17b10bc55c3 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -165,6 +165,10 @@ namespace gccjit
     struct_ new_opaque_struct_type (const std::string &name,
 				    location loc = location ());
 
+    type new_union_type (const std::string &name,
+			 std::vector <field> &fields,
+			 location loc = location ());
+
     param new_param (type type_,
 		     const std::string &name,
 		     location loc = location ());
@@ -189,6 +193,7 @@ namespace gccjit
 		       long value) const;
     rvalue zero (type numeric_type) const;
     rvalue one (type numeric_type) const;
+    rvalue null (type pointer_type) const;
     rvalue new_rvalue (type numeric_type,
 		       double value) const;
     rvalue new_rvalue (type pointer_type,
@@ -361,9 +366,10 @@ namespace gccjit
     type get_aligned (size_t alignment_in_bytes);
     type get_vector (size_t num_units);
 
-    // Shortcuts for getting values of numeric types:
+    // Shortcuts for getting values of numeric and pointer types:
     rvalue zero ();
     rvalue one ();
+    rvalue null ();
  };
 
   class struct_ : public type
@@ -373,6 +379,9 @@ namespace gccjit
     struct_ (gcc_jit_struct *inner);
 
     gcc_jit_struct *get_inner_struct () const;
+
+    void set_fields (std::vector <field> &fields,
+		     location loc = location ());
   };
 
   class function : public object
@@ -864,6 +873,26 @@ context::new_opaque_struct_type (const std::string &name,
 		    name.c_str ()));
 }
 
+inline type
+context::new_union_type (const std::string &name,
+			 std::vector<field> &fields,
+			 location loc)
+{
+  /* Treat std::vector as an array, relying on it not being resized: */
+  field *as_array_of_wrappers = &fields[0];
+
+  /* Treat the array as being of the underlying pointers, relying on
+     the wrapper type being such a pointer internally.	*/
+  gcc_jit_field **as_array_of_ptrs =
+    reinterpret_cast<gcc_jit_field **> (as_array_of_wrappers);
+
+  return type (gcc_jit_context_new_union_type (m_inner_ctxt,
+					       loc.get_inner_location (),
+					       name.c_str (),
+					       fields.size (),
+					       as_array_of_ptrs));
+}
+
 inline param
 context::new_param (type type_,
 		    const std::string &name,
@@ -955,6 +984,13 @@ context::one (type numeric_type) const
 				       numeric_type.get_inner_type ()));
 }
 
+inline rvalue
+context::null (type pointer_type) const
+{
+  return rvalue (gcc_jit_context_null (m_inner_ctxt,
+				       pointer_type.get_inner_type ()));
+}
+
 inline rvalue
 context::new_rvalue (type numeric_type,
 		     double value) const
@@ -1442,6 +1478,12 @@ type::one ()
   return get_context ().new_rvalue (*this, 1);
 }
 
+inline rvalue
+type::null ()
+{
+  return get_context ().null (*this);
+}
+
 // class struct_
 inline struct_::struct_ () : type (NULL) {}
 inline struct_::struct_ (gcc_jit_struct *inner) :
@@ -1456,6 +1498,23 @@ struct_::get_inner_struct () const
   return reinterpret_cast<gcc_jit_struct *> (get_inner_object ());
 }
 
+inline void
+struct_::set_fields (std::vector <field> &fields, location loc)
+{
+  /* Treat std::vector as an array, relying on it not being resized: */
+  field *as_array_of_wrappers = &fields[0];
+
+  /* Treat the array as being of the underlying pointers, relying on
+     the wrapper type being such a pointer internally.	*/
+  gcc_jit_field **as_array_of_ptrs =
+    reinterpret_cast<gcc_jit_field **> (as_array_of_wrappers);
+
+  gcc_jit_struct_set_fields (get_inner_struct (),
+			     loc.get_inner_location (),
+			     fields.size (),
+			     as_array_of_ptrs);
+}
+
 // class function
 inline function::function () : object () {}
 inline function::function (gcc_jit_function *inner)
diff --git a/gcc/testsuite/jit.dg/test-accessing-union.cc b/gcc/testsuite/jit.dg/test-accessing-union.cc
new file mode 100644
index 00000000000..8fa03fd209c
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-accessing-union.cc
@@ -0,0 +1,86 @@
+#include <cstdlib>
+#include <cstdio>
+#include <vector>
+
+#include "libgccjit++.h"
+
+#include "harness.h"
+
+union int_or_float
+{
+  int as_int;
+  float as_float;
+};
+
+void
+create_code (gcc_jit_context* c_ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+     float
+     test_union (int i)
+     {
+        union int_or_float u;
+	u.as_int = i;
+	return u.as_float;
+     }
+  */
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type int_type =
+    ctxt.get_type (GCC_JIT_TYPE_INT);
+  gccjit::type float_type =
+    ctxt.get_type (GCC_JIT_TYPE_FLOAT);
+  gccjit::field as_int =
+    ctxt.new_field (int_type, "as_int");
+  gccjit::field as_float =
+    ctxt.new_field (float_type, "as_float");
+  std::vector <gccjit::field> fields;
+  fields.push_back (as_int);
+  fields.push_back (as_float);
+  gccjit::type union_type =
+    ctxt.new_union_type ("int_or_float", fields);
+
+  /* Build the test function.  */
+  gccjit::param param_i =
+    ctxt.new_param (int_type, "i");
+  std::vector <gccjit::param> params;
+  params.push_back (param_i);
+  gccjit::function test_fn =
+    ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+		       float_type,
+		       "test_union",
+		       params,
+		       0);
+
+  gccjit::lvalue u =
+    test_fn.new_local (union_type, "u");
+
+  gccjit::block block = test_fn.new_block ();
+
+  /* u.as_int = i; */
+  block.add_assignment (
+    /* "u.as_int = ..." */
+    u.access_field (as_int),
+    param_i);
+
+  /* return u.as_float; */
+  block.end_with_return (u.access_field (as_float));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef float (*fn_type) (int i);
+  CHECK_NON_NULL (result);
+
+  fn_type test_union =
+    (fn_type)gcc_jit_result_get_code (result, "test_union");
+  CHECK_NON_NULL (test_union);
+
+  /* Call the JIT-generated function.  */
+  float f_result = test_union (42);
+
+  union int_or_float u;
+  u.as_float = f_result;
+
+  CHECK_VALUE (u.as_int, 42);
+}
diff --git a/gcc/testsuite/jit.dg/test-linked-list.cc b/gcc/testsuite/jit.dg/test-linked-list.cc
new file mode 100644
index 00000000000..933ab706b00
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-linked-list.cc
@@ -0,0 +1,128 @@
+#include <cstdlib>
+#include <cstdio>
+#include <vector>
+
+#include "libgccjit++.h"
+
+#include "harness.h"
+
+/* A doubly-linked list, to ensure that the JIT API can cope with
+   self-referential types.  */
+struct node
+{
+  struct node *prev;
+  struct node *next;
+  int value;
+};
+
+void
+create_code (gcc_jit_context *c_ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+     int
+     test_linked_list (struct node *n)
+     {
+	int total = 0;
+	while (n)
+	  {
+	    total += n->value;
+	    n = n->next;
+	  }
+	return total;
+     }
+  */
+  gccjit::context ctxt (c_ctxt);
+  gccjit::type t_int =
+    ctxt.get_type (GCC_JIT_TYPE_INT);
+  gccjit::struct_ t_node =
+    ctxt.new_opaque_struct_type ("node");
+  gccjit::type t_node_ptr =
+    t_node.get_pointer ();
+
+  gccjit::field f_prev =
+    ctxt.new_field (t_node_ptr, "prev");
+  gccjit::field f_next =
+    ctxt.new_field (t_node_ptr, "next");
+  gccjit::field f_value =
+    ctxt.new_field (t_int, "value");
+  std::vector <gccjit::field> fields;
+  fields.push_back (f_prev);
+  fields.push_back (f_next);
+  fields.push_back (f_value);
+  t_node.set_fields (fields);
+
+  /* Build the test function.  */
+  gccjit::param param_n =
+    ctxt.new_param (t_node_ptr, "n");
+  std::vector <gccjit::param> params;
+  params.push_back (param_n);
+  gccjit::function fn =
+    ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
+		       t_int,
+		       "test_linked_list",
+		       params,
+		       0);
+  /* int total; */
+  gccjit::lvalue total =
+    fn.new_local (t_int, "total");
+
+  gccjit::block initial = fn.new_block ("initial");
+  gccjit::block loop_test = fn.new_block ("loop_test");
+  gccjit::block loop_body = fn.new_block ("loop_body");
+  gccjit::block final = fn.new_block ("final");
+
+  /* total = 0; */
+  initial.add_assignment (
+    total,
+    ctxt.zero (t_int));
+  initial.end_with_jump (loop_test);
+
+  /* while (n) */
+  loop_test.end_with_conditional (param_n != t_node_ptr.null (),
+				  loop_body,
+				  final);
+
+  /* total += n->value; */
+  loop_body.add_assignment_op (total,
+			       GCC_JIT_BINARY_OP_PLUS,
+			       param_n.dereference_field (f_value));
+
+  /* n = n->next; */
+  loop_body.add_assignment (param_n,
+			    param_n.dereference_field (f_next));
+
+  loop_body.end_with_jump (loop_test);
+
+  /* return total; */
+  final.end_with_return (total);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  struct node a, b, c;
+  typedef int (*fn_type) (struct node *n);
+  CHECK_NON_NULL (result);
+
+  fn_type test_linked_list =
+    (fn_type)gcc_jit_result_get_code (result, "test_linked_list");
+  CHECK_NON_NULL (test_linked_list);
+
+  /* Construct a simple linked-list on the stack: a->b->c: */
+  a.prev = NULL;
+  a.next = &b;
+  a.value = 5;
+
+  b.prev = &a;
+  b.next = &c;
+  b.value = 3;
+
+  c.prev = &b;
+  c.next = NULL;
+  c.value = 7;
+
+  CHECK_VALUE (test_linked_list (NULL), 0);
+  CHECK_VALUE (test_linked_list (&a), 15);
+  CHECK_VALUE (test_linked_list (&b), 10);
+  CHECK_VALUE (test_linked_list (&c), 7);
+}
-- 
2.32.0


                 reply	other threads:[~2022-01-31 11:27 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20220131112540.429699-1-marc@nieper-wisskirchen.de \
    --to=marc@nieper-wisskirchen.de \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jit@gcc.gnu.org \
    --cc=marc.nieper+gnu@gmail.com \
    /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).