diff mbox

[jit] New API entrypoint: gcc_jit_context_get_builtin_function

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

Commit Message

David Malcolm Feb. 13, 2014, 6:16 p.m. UTC
Committed to branch dmalcolm/jit:

This commit adds the ability for client code to look up GCC builtins by
name, potentially allowing GCC to optimize the resulting function usage
based on what it knows about the behavior of each builtin.

Note that if the optimizer can't eliminate the call, the generated caller
code will still require machine code for the callee, and thus may need
the DSO implementing the "builtin" to already be linked into the client
process, or you'll get a linker error - so perhaps "builtin" is a bad
name?

Implementing this required creating function types (to handle
builtin-types.def), which are used internally by the new builtins_manager.
They're not yet exposed to client code.

gcc/jit/
	* libgccjit.h (gcc_jit_context_get_builtin_function): New.
	* libgccjit.map (gcc_jit_context_get_builtin_function): New.
	* libgccjit++.h (gccjit::context::get_builtin_function): New method.

	* Make-lang.in (jit_OBJS): Add jit/jit-builtins.o
	* jit-builtins.c: New source file, for managing builtin functions
	and their types.
	* jit-builtins.h: Likewise.

	* libgccjit.c (gcc_jit_context_new_function): Pass BUILT_IN_NONE for
	the new argument of new_function
	(gcc_jit_context_get_builtin_function): New.

	* internal-api.h: Add idempotency guards.
	(gcc::jit::recording::context::new_function): Add parameter
	for builtin functions.
	(gcc::jit::recording::context::get_builtin_function): New method.
	(gcc::jit::recording::context::m_builtins_manager): New field.
	(gcc::jit::recording::type::as_a_function_type): New virtual function.
	(gcc::jit::recording::function_type): New subclass of type.
	(gcc::jit::recording::function::function): Add parameter for
	builtin functions.
	(gcc::jit::recording::function::m_builtin_id): New field.
	(gcc::jit::recording::function::new_function_type): New method.
	(gcc::jit::playback::function::function):  Add parameter for
	builtin functions.
	* internal-api.c (gcc::jit::recording::context::context):
	NULL-initialize new field m_builtins_manager.
	(gcc::jit::recording::context::~context): Clean up the builtins
	manager, if one has been created.
	(gcc::jit::recording::context::new_function): Add parameter
	(gcc::jit::recording::context::get_builtin_function): New method.
	(gcc::jit::recording::function_type::function_type): Implement
	constructor for new subclass.
	(gcc::jit::recording::function_type::dereference): Implement
	method for new subclass.
	(gcc::jit::recording::function_type::replay_into): Likewise.
	(gcc::jit::recording::function_type::make_debug_string): Likewise.
	(gcc::jit::recording::function::function): Add parameter for
	builtin functions.
	(gcc::jit::recording::function::replay_into): Likewise for
	creation of playback object.
	(gcc::jit::recording::function::new_function_type): New method.
	(gcc::jit::playback::function::new_function):  Add parameter for
	builtin functions, using it to set up the fndecl accordingly.

gcc/testsuite/
	* jit.dg/harness.h (CHECK_DOUBLE_VALUE): New macro.
	(CHECK): New macro.
	* jit.dg/test-functions.c: New testcase, exercising
	gcc_jit_context_get_builtin_function.
	* jit.dg/test-combination.c: Add test-functions.c to the combined
	test.
---
 gcc/jit/ChangeLog.jit                   |  48 ++++
 gcc/jit/Make-lang.in                    |   3 +-
 gcc/jit/internal-api.c                  | 157 ++++++++++++-
 gcc/jit/internal-api.h                  |  55 ++++-
 gcc/jit/jit-builtins.c                  | 395 ++++++++++++++++++++++++++++++++
 gcc/jit/jit-builtins.h                  | 114 +++++++++
 gcc/jit/libgccjit++.h                   |   9 +
 gcc/jit/libgccjit.c                     |  13 +-
 gcc/jit/libgccjit.h                     |   4 +
 gcc/jit/libgccjit.map                   |   1 +
 gcc/testsuite/ChangeLog.jit             |   9 +
 gcc/testsuite/jit.dg/harness.h          |  29 +++
 gcc/testsuite/jit.dg/test-combination.c |   9 +
 gcc/testsuite/jit.dg/test-functions.c   | 175 ++++++++++++++
 14 files changed, 1009 insertions(+), 12 deletions(-)
 create mode 100644 gcc/jit/jit-builtins.c
 create mode 100644 gcc/jit/jit-builtins.h
 create mode 100644 gcc/testsuite/jit.dg/test-functions.c
diff mbox

Patch

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index adccd57..603dd96 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,51 @@ 
+2014-02-13  David Malcolm  <dmalcolm@redhat.com>
+
+	* libgccjit.h (gcc_jit_context_get_builtin_function): New.
+	* libgccjit.map (gcc_jit_context_get_builtin_function): New.
+	* libgccjit++.h (gccjit::context::get_builtin_function): New method.
+
+	* Make-lang.in (jit_OBJS): Add jit/jit-builtins.o
+	* jit-builtins.c: New source file, for managing builtin functions
+	and their types.
+	* jit-builtins.h: Likewise.
+
+	* libgccjit.c (gcc_jit_context_new_function): Pass BUILT_IN_NONE for
+	the new argument of new_function
+	(gcc_jit_context_get_builtin_function): New.
+
+	* internal-api.h: Add idempotency guards.
+	(gcc::jit::recording::context::new_function): Add parameter
+	for builtin functions.
+	(gcc::jit::recording::context::get_builtin_function): New method.
+	(gcc::jit::recording::context::m_builtins_manager): New field.
+	(gcc::jit::recording::type::as_a_function_type): New virtual function.
+	(gcc::jit::recording::function_type): New subclass of type.
+	(gcc::jit::recording::function::function): Add parameter for
+	builtin functions.
+	(gcc::jit::recording::function::m_builtin_id): New field.
+	(gcc::jit::recording::function::new_function_type): New method.
+	(gcc::jit::playback::function::function):  Add parameter for
+	builtin functions.
+	* internal-api.c (gcc::jit::recording::context::context):
+	NULL-initialize new field m_builtins_manager.
+	(gcc::jit::recording::context::~context): Clean up the builtins
+	manager, if one has been created.
+	(gcc::jit::recording::context::new_function): Add parameter
+	(gcc::jit::recording::context::get_builtin_function): New method.
+	(gcc::jit::recording::function_type::function_type): Implement
+	constructor for new subclass.
+	(gcc::jit::recording::function_type::dereference): Implement
+	method for new subclass.
+	(gcc::jit::recording::function_type::replay_into): Likewise.
+	(gcc::jit::recording::function_type::make_debug_string): Likewise.
+	(gcc::jit::recording::function::function): Add parameter for
+	builtin functions.
+	(gcc::jit::recording::function::replay_into): Likewise for
+	creation of playback object.
+	(gcc::jit::recording::function::new_function_type): New method.
+	(gcc::jit::playback::function::new_function):  Add parameter for
+	builtin functions, using it to set up the fndecl accordingly.
+
 2014-02-11  David Malcolm  <dmalcolm@redhat.com>
 
 	* libgccjit.c (IS_ASCII_ALPHA): New macro.
diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in
index 17d27ca..002436b 100644
--- a/gcc/jit/Make-lang.in
+++ b/gcc/jit/Make-lang.in
@@ -45,7 +45,8 @@  jit: libgccjit.so
 # Tell GNU make to ignore these if they exist.
 .PHONY: jit
 
-jit_OBJS = attribs.o jit/dummy-frontend.o jit/libgccjit.o jit/internal-api.o
+jit_OBJS = attribs.o jit/dummy-frontend.o jit/libgccjit.o jit/internal-api.o \
+	jit/jit-builtins.o
 
 # Use strict warnings for this front end.
 jit-warn = $(STRICT_WARN)
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index 0f140fe..dacb55d 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -20,6 +20,7 @@ 
 #include <pthread.h>
 
 #include "internal-api.h"
+#include "jit-builtins.h"
 
 namespace gcc {
 namespace jit {
@@ -61,7 +62,8 @@  recording::context::context (context *parent_ctxt)
   : m_parent_ctxt (parent_ctxt),
     m_error_count (0),
     m_mementos (),
-    m_FILE_type (NULL)
+    m_FILE_type (NULL),
+    m_builtins_manager(NULL)
 {
   m_first_error_str[0] = '\0';
 
@@ -97,6 +99,9 @@  recording::context::~context ()
     {
       delete m;
     }
+
+  if (m_builtins_manager)
+    delete m_builtins_manager;
 }
 
 void
@@ -307,17 +312,27 @@  recording::context::new_function (recording::location *loc,
 				  const char *name,
 				  int num_params,
 				  recording::param **params,
-				  int is_variadic)
+				  int is_variadic,
+				  enum built_in_function builtin_id)
 {
   recording::function *result =
     new recording::function (this,
 			     loc, kind, return_type,
 			     new_string (name),
-			     num_params, params, is_variadic);
+			     num_params, params, is_variadic,
+			     builtin_id);
   record (result);
   return result;
 }
 
+recording::function *
+recording::context::get_builtin_function (const char *name)
+{
+  if (!m_builtins_manager)
+    m_builtins_manager = new builtins_manager (this);
+  return m_builtins_manager->get_builtin_function (name);
+}
+
 recording::lvalue *
 recording::context::new_global (recording::location *loc,
 				recording::type *type,
@@ -797,6 +812,94 @@  recording::array_type::make_debug_string ()
 			      m_num_elements);
 }
 
+/* gcc::jit::recording::function_type */
+recording::function_type::function_type (context *ctxt,
+					 type *return_type,
+					 int num_params,
+					 type **param_types,
+					 int is_variadic)
+: type (ctxt),
+  m_return_type (return_type),
+  m_param_types (),
+  m_is_variadic (is_variadic)
+{
+  for (int i = 0; i< num_params; i++)
+    m_param_types.safe_push (param_types[i]);
+}
+
+recording::type *
+recording::function_type::dereference ()
+{
+  return NULL;
+}
+
+void
+recording::function_type::replay_into (replayer *r)
+{
+  /* Convert m_param_types to a vec of playback type.  */
+  vec <playback::type *> param_types;
+  int i;
+  recording::type *type;
+  param_types.create (m_param_types.length ());
+  FOR_EACH_VEC_ELT (m_param_types, i, type)
+    param_types.safe_push (type->playback_type ());
+
+  set_playback_obj (r->new_function_type (m_return_type->playback_type (),
+					  &param_types,
+					  m_is_variadic));
+}
+
+recording::string *
+recording::function_type::make_debug_string ()
+{
+  /* First, build a buffer for the arguments.  */
+  /* Calculate length of said buffer.  */
+  size_t sz = 1; /* nil terminator */
+  for (unsigned i = 0; i< m_param_types.length (); i++)
+    {
+      sz += strlen (m_param_types[i]->get_debug_string ());
+      sz += 2; /* ", " separator */
+    }
+  if (m_is_variadic)
+    sz += 5; /* ", ..." separator and ellipsis */
+
+  /* Now allocate and populate the buffer.  */
+  char *argbuf = new char[sz];
+  size_t len = 0;
+
+  for (unsigned i = 0; i< m_param_types.length (); i++)
+    {
+      strcpy (argbuf + len, m_param_types[i]->get_debug_string ());
+      len += strlen (m_param_types[i]->get_debug_string ());
+      if (i + 1 < m_param_types.length ())
+	{
+	  strcpy (argbuf + len, ", ");
+	  len += 2;
+	}
+    }
+  if (m_is_variadic)
+    {
+      if (m_param_types.length ())
+	{
+	  strcpy (argbuf + len, ", ");
+	  len += 2;
+	}
+      strcpy (argbuf + len, "...");
+      len += 3;
+    }
+  argbuf[len] = '\0';
+
+  /* ...and use it to get the string for the call as a whole.  */
+  string *result = string::from_printf (m_ctxt,
+					"%s (%s)",
+					m_return_type->get_debug_string (),
+					argbuf);
+
+  delete[] argbuf;
+
+  return result;
+}
+
 /* gcc::jit::recording::field:: */
 void
 recording::field::replay_into (replayer *r)
@@ -928,14 +1031,16 @@  recording::function::function (context *ctxt,
 			       recording::string *name,
 			       int num_params,
 			       recording::param **params,
-			       int is_variadic)
+			       int is_variadic,
+			       enum built_in_function builtin_id)
 : memento (ctxt),
   m_loc (loc),
   m_kind (kind),
   m_return_type (return_type),
   m_name (name),
   m_params (),
-  m_is_variadic (is_variadic)
+  m_is_variadic (is_variadic),
+  m_builtin_id (builtin_id)
 {
   for (int i = 0; i< num_params; i++)
     m_params.safe_push (params[i]);
@@ -957,7 +1062,8 @@  recording::function::replay_into (replayer *r)
 				     m_return_type->playback_type (),
 				     m_name->c_str (),
 				     &params,
-				     m_is_variadic));
+				     m_is_variadic,
+				     m_builtin_id));
 }
 
 recording::lvalue *
@@ -1864,6 +1970,34 @@  new_struct_type (location *loc,
   return new type (t);
 }
 
+playback::type *
+playback::context::
+new_function_type (type *return_type,
+		   vec<type *> *param_types,
+		   int is_variadic)
+{
+  int i;
+  type *param_type;
+
+  tree *arg_types = (tree *)xcalloc(param_types->length (), sizeof(tree*));
+
+  FOR_EACH_VEC_ELT (*param_types, i, param_type)
+    arg_types[i] = param_type->as_tree ();
+
+  tree fn_type;
+  if (is_variadic)
+    fn_type =
+      build_varargs_function_type_array (return_type->as_tree (),
+					 param_types->length (),
+					 arg_types);
+  else
+    fn_type = build_function_type_array (return_type->as_tree (),
+					 param_types->length (),
+					 arg_types);
+  free (arg_types);
+
+  return new type (fn_type);
+}
 
 playback::param *
 playback::context::
@@ -1888,7 +2022,8 @@  new_function (location *loc,
 	      type *return_type,
 	      const char *name,
 	      vec<param *> *params,
-	      int is_variadic)
+	      int is_variadic,
+	      enum built_in_function builtin_id)
 {
   int i;
   param *param;
@@ -1921,6 +2056,14 @@  new_function (location *loc,
   DECL_IGNORED_P (resdecl) = 1;
   DECL_RESULT (fndecl) = resdecl;
 
+  if (builtin_id)
+    {
+      DECL_BUILT_IN_CLASS (fndecl) = BUILT_IN_NORMAL;
+      DECL_FUNCTION_CODE (fndecl) = builtin_id;
+      gcc_assert (loc == NULL);
+      DECL_SOURCE_LOCATION (fndecl) = BUILTINS_LOCATION;
+    }
+
   if (kind != GCC_JIT_FUNCTION_IMPORTED)
     {
       tree param_decl_list = NULL;
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index f285039..45ab9ea 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -1,3 +1,6 @@ 
+#ifndef INTERNAL_API_H
+#define INTERNAL_API_H
+
 #include "libgccjit.h"
 
 #include "tree.h"
@@ -74,10 +77,12 @@  class result;
 
 namespace recording {
   class context;
+  class builtins_manager; // declared within jit-builtins.h
   class memento;
   class string;
   class location;
   class type;
+  class function_type;
   class field;
   class struct_;
   class function;
@@ -174,7 +179,11 @@  public:
 		const char *name,
 		int num_params,
 		param **params,
-		int is_variadic);
+		int is_variadic,
+		enum built_in_function builtin_id);
+
+  function *
+  get_builtin_function (const char *name);
 
   lvalue *
   new_global (location *loc,
@@ -289,6 +298,8 @@  private:
 
   type *m_basic_types[NUM_GCC_JIT_TYPES];
   type *m_FILE_type;
+
+  builtins_manager *m_builtins_manager; // lazily created
 };
 
 
@@ -400,6 +411,9 @@  public:
      The caller is responsible for setting an error.  */
   virtual type *dereference () = 0;
 
+  /* Dynamic cast.  */
+  virtual function_type *as_a_function_type() { gcc_unreachable (); return NULL; }
+
   /* Is it typesafe to copy to this type from rtype?  */
   virtual bool accepts_writes_from (type *rtype)
   {
@@ -537,6 +551,32 @@  class array_type : public type
   int m_num_elements;
 };
 
+class function_type : public type
+{
+public:
+  function_type (context *ctxt,
+		 type *return_type,
+		 int num_params,
+		 type **param_types,
+		 int is_variadic);
+
+  type *dereference ();
+  function_type *as_a_function_type () { return this; }
+  void replay_into (replayer *);
+
+  type * get_return_type () const { return m_return_type; }
+  vec<type *> get_param_types () const { return m_param_types; }
+  int is_variadic () const { return m_is_variadic; }
+
+ private:
+  string * make_debug_string ();
+
+private:
+  type *m_return_type;
+  vec<type *> m_param_types;
+  int m_is_variadic;
+};
+
 class field : public memento
 {
 public:
@@ -695,7 +735,8 @@  public:
 	    string *name,
 	    int num_params,
 	    param **params,
-	    int is_variadic);
+	    int is_variadic,
+	    enum built_in_function builtin_id);
 
   void replay_into (replayer *r);
 
@@ -776,6 +817,7 @@  private:
   string *m_name;
   vec<param *> m_params;
   int m_is_variadic;
+  enum built_in_function m_builtin_id;
 };
 
 class label : public memento
@@ -1444,6 +1486,11 @@  public:
 		   const char *name,
 		   vec<field *> *fields);
 
+  type *
+  new_function_type (type *return_type,
+		     vec<type *> *param_types,
+		     int is_variadic);
+
   param *
   new_param (location *loc,
 	     type *type,
@@ -1455,7 +1502,8 @@  public:
 		type *return_type,
 		const char *name,
 		vec<param *> *params,
-		int is_variadic);
+		int is_variadic,
+		enum built_in_function builtin_id);
 
   lvalue *
   new_global (location *loc,
@@ -1895,3 +1943,4 @@  extern playback::context *active_playback_ctxt;
 
 } // namespace gcc
 
+#endif /* INTERNAL_API_H */
diff --git a/gcc/jit/jit-builtins.c b/gcc/jit/jit-builtins.c
new file mode 100644
index 0000000..afc773d
--- /dev/null
+++ b/gcc/jit/jit-builtins.c
@@ -0,0 +1,395 @@ 
+/* jit-builtins.c -- Handling of builtin functions during JIT-compilation.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "ansidecl.h"
+#include "coretypes.h"
+#include "opts.h"
+#include "tree.h"
+#include "target.h"
+
+#include "internal-api.h"
+#include "jit-builtins.h"
+
+namespace gcc {
+
+namespace jit {
+
+namespace recording {
+
+const char *const prefix = "__builtin_";
+const size_t prefix_len = strlen (prefix);
+
+/* Create "builtin_data", a const table of the data within builtins.def.  */
+struct builtin_data
+{
+  const char *name;
+  enum jit_builtin_type type;
+  bool both_p;
+  bool fallback_p;
+
+  const char *get_asm_name () const
+  {
+    if (both_p && fallback_p)
+      return name + prefix_len;
+    else
+      return name;
+  }
+};
+
+#define DEF_BUILTIN(X, NAME, C, TYPE, LT, BOTH_P, FALLBACK_P, NA, AT, IM, COND)\
+  {NAME, TYPE, BOTH_P, FALLBACK_P},
+static const struct builtin_data builtin_data[] =
+{
+#include "builtins.def"
+};
+#undef DEF_BUILTIN
+
+static bool
+matches_builtin (const char *in_name,
+		 const struct builtin_data& bd)
+{
+  const bool debug = 0;
+  gcc_assert (bd.name);
+
+  if (debug)
+    fprintf (stderr, "seen builtin: %s\n", bd.name);
+
+  if (0 == strcmp (bd.name, in_name))
+    {
+      return true;
+    }
+
+  if (bd.both_p)
+    {
+      /* Then the macros in builtins.def gave a "__builtin_"
+	 prefix to bd.name, but we should also recognize the form
+	 without the prefix.  */
+      gcc_assert (0 == strncmp (bd.name, prefix, prefix_len));
+      if (debug)
+	fprintf (stderr, "testing without prefix as: %s\n",
+		 bd.name + prefix_len);
+      if (0 == strcmp (bd.name + prefix_len, in_name))
+	{
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+static bool
+find_builtin_by_name (const char *in_name,
+		      enum built_in_function *out_id)
+{
+  /* Locate builtin.  This currently works by performing repeated
+     strcmp against every possible candidate, which is likely to
+     inefficient.
+
+     We start at index 1 to skip the initial entry (BUILT_IN_NONE), which
+     has a NULL name.  */
+  for (unsigned int i = 1;
+       i < sizeof (builtin_data) / sizeof (builtin_data[0]);
+       i++)
+    {
+      const struct builtin_data& bd = builtin_data[i];
+      if (matches_builtin (in_name, bd))
+	{
+	  /* Found a match.  */
+	  *out_id = static_cast<enum built_in_function> (i);
+	  return true;
+	}
+    }
+
+  /* Not found.  */
+  return false;
+}
+
+// class builtins_manager
+
+builtins_manager::builtins_manager (context *ctxt)
+  : m_ctxt (ctxt)
+{
+  memset (m_types, 0, sizeof (m_types));
+  memset (m_builtin_functions, 0, sizeof (m_builtin_functions));
+}
+
+function *
+builtins_manager::get_builtin_function (const char *name)
+{
+  enum built_in_function builtin_id;
+  if (!find_builtin_by_name (name, &builtin_id))
+    {
+      m_ctxt->add_error ("builtin \"%s\" not found", name);
+      return NULL;
+    }
+
+  gcc_assert (builtin_id >= 0);
+  gcc_assert (builtin_id < END_BUILTINS);
+
+  /* Lazily build the functions, caching them so that repeated calls for
+     the same id on a context give back the same object.  */
+  if (!m_builtin_functions[builtin_id])
+    {
+      m_builtin_functions[builtin_id] = make_builtin_function (builtin_id);
+      m_ctxt->record (m_builtin_functions[builtin_id]);
+    }
+
+  return m_builtin_functions[builtin_id];
+}
+
+function *
+builtins_manager::make_builtin_function (enum built_in_function builtin_id)
+{
+  const struct builtin_data& bd = builtin_data[builtin_id];
+  enum jit_builtin_type type_id = bd.type;
+  function_type *func_type = get_type (type_id)->as_a_function_type ();
+  if (!func_type)
+    return NULL;
+
+  vec<type *> param_types = func_type->get_param_types ();
+  recording::param **params = new recording::param *[param_types.length ()];
+
+  int i;
+  type *param_type;
+  FOR_EACH_VEC_ELT (param_types, i, param_type)
+    {
+      char buf[16];
+      snprintf (buf, 16, "arg%d", i);
+      params[i] = m_ctxt->new_param (NULL,
+				     param_type,
+				     buf);
+    }
+  const char *asm_name = bd.get_asm_name ();
+  function *result =
+    new function (m_ctxt,
+		  NULL,
+		  GCC_JIT_FUNCTION_IMPORTED, // FIXME
+		  func_type->get_return_type (),
+		  m_ctxt->new_string (asm_name),
+		  param_types.length (),
+		  params,
+		  func_type->is_variadic (),
+		  builtin_id);
+  delete[] params;
+  return result;
+}
+
+type *
+builtins_manager::get_type (enum jit_builtin_type type_id)
+{
+  if (!m_types[type_id])
+    m_types[type_id] = make_type (type_id);
+  return m_types[type_id];
+}
+
+type *
+builtins_manager::make_type (enum jit_builtin_type type_id)
+{
+  /* Use builtin-types.def to construct a switch statement, with each
+     case deferring to one of the methods below:
+       - DEF_PRIMITIVE_TYPE is handled as a call to make_primitive_type.
+       - the various DEF_FUNCTION_TYPE_n are handled by variadic calls
+	 to make_fn_type.
+       - similarly for DEF_FUNCTION_TYPE_VAR_n, but setting the
+	"is_variadic" argument.
+       - DEF_POINTER_TYPE is handled by make_ptr_type.
+     That should handle everything, but just in case we also suppy a
+     gcc_unreachable default clause.  */
+  switch (type_id)
+    {
+#define DEF_PRIMITIVE_TYPE(ENUM, VALUE) \
+      case ENUM: return make_primitive_type (ENUM);
+#define DEF_FUNCTION_TYPE_0(ENUM, RETURN) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 0);
+#define DEF_FUNCTION_TYPE_1(ENUM, RETURN, ARG1) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 1, ARG1);
+#define DEF_FUNCTION_TYPE_2(ENUM, RETURN, ARG1, ARG2) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 2, ARG1, ARG2);
+#define DEF_FUNCTION_TYPE_3(ENUM, RETURN, ARG1, ARG2, ARG3) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 3, ARG1, ARG2, ARG3);
+#define DEF_FUNCTION_TYPE_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 4, ARG1, ARG2, ARG3, ARG4);
+#define DEF_FUNCTION_TYPE_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5)	\
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 5, ARG1, ARG2, ARG3, ARG4, ARG5);
+#define DEF_FUNCTION_TYPE_6(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
+			    ARG6)					\
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 6, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6);
+#define DEF_FUNCTION_TYPE_7(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
+			    ARG6, ARG7)					\
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 7, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7);
+#define DEF_FUNCTION_TYPE_8(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, \
+			    ARG6, ARG7, ARG8)				\
+      case ENUM: return make_fn_type (ENUM, RETURN, 0, 8, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, \
+				      ARG7, ARG8);
+#define DEF_FUNCTION_TYPE_VAR_0(ENUM, RETURN) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 0);
+#define DEF_FUNCTION_TYPE_VAR_1(ENUM, RETURN, ARG1) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 1, ARG1);
+#define DEF_FUNCTION_TYPE_VAR_2(ENUM, RETURN, ARG1, ARG2) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 2, ARG1, ARG2);
+#define DEF_FUNCTION_TYPE_VAR_3(ENUM, RETURN, ARG1, ARG2, ARG3) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 3, ARG1, ARG2, ARG3);
+#define DEF_FUNCTION_TYPE_VAR_4(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 4, ARG1, ARG2, ARG3, ARG4);
+#define DEF_FUNCTION_TYPE_VAR_5(ENUM, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) \
+      case ENUM: return make_fn_type (ENUM, RETURN, 1, 5, ARG1, ARG2, ARG3, ARG4, ARG5);
+#define DEF_POINTER_TYPE(ENUM, TYPE) \
+      case ENUM: return make_ptr_type (ENUM, TYPE);
+
+#include "builtin-types.def"
+
+#undef DEF_PRIMITIVE_TYPE
+#undef DEF_FUNCTION_TYPE_1
+#undef DEF_FUNCTION_TYPE_2
+#undef DEF_FUNCTION_TYPE_3
+#undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_5
+#undef DEF_FUNCTION_TYPE_6
+#undef DEF_FUNCTION_TYPE_VAR_0
+#undef DEF_FUNCTION_TYPE_VAR_1
+#undef DEF_FUNCTION_TYPE_VAR_2
+#undef DEF_FUNCTION_TYPE_VAR_3
+#undef DEF_FUNCTION_TYPE_VAR_4
+#undef DEF_FUNCTION_TYPE_VAR_5
+#undef DEF_POINTER_TYPE
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+type*
+builtins_manager::make_primitive_type (enum jit_builtin_type type_id)
+{
+  switch (type_id)
+    {
+    default:
+      // only some of these types are implemented so far:
+      m_ctxt->add_error ("unimplemented primitive type for builtin: %d", type_id);
+      return NULL;
+
+    case BT_VOID: return m_ctxt->get_type (GCC_JIT_TYPE_VOID);
+    case BT_BOOL: return m_ctxt->get_type (GCC_JIT_TYPE_BOOL);
+    case BT_INT: return m_ctxt->get_type (GCC_JIT_TYPE_INT);
+    case BT_UINT: return m_ctxt->get_type (GCC_JIT_TYPE_UNSIGNED_INT);
+    case BT_LONG: return m_ctxt->get_type (GCC_JIT_TYPE_LONG);
+    case BT_ULONG: return m_ctxt->get_type (GCC_JIT_TYPE_UNSIGNED_LONG);
+    case BT_LONGLONG: return m_ctxt->get_type (GCC_JIT_TYPE_LONG_LONG);
+    case BT_ULONGLONG:
+      return m_ctxt->get_type (GCC_JIT_TYPE_UNSIGNED_LONG_LONG);
+    // case BT_INT128:
+    // case BT_UINT128:
+    // case BT_INTMAX:
+    // case BT_UINTMAX:
+    case BT_UINT16: return m_ctxt->get_int_type (2, false);
+    case BT_UINT32: return m_ctxt->get_int_type (4, false);
+    case BT_UINT64: return m_ctxt->get_int_type (8, false);
+    // case BT_WORD:
+    // case BT_UNWINDWORD:
+    case BT_FLOAT: return m_ctxt->get_type (GCC_JIT_TYPE_FLOAT);
+    case BT_DOUBLE: return m_ctxt->get_type (GCC_JIT_TYPE_DOUBLE);
+    case BT_LONGDOUBLE: return m_ctxt->get_type (GCC_JIT_TYPE_LONG_DOUBLE);
+    // case BT_COMPLEX_FLOAT:
+    // case BT_COMPLEX_DOUBLE:
+    // case BT_COMPLEX_LONGDOUBLE:
+    case BT_PTR: return m_ctxt->get_type (GCC_JIT_TYPE_VOID_PTR);
+    case BT_FILEPTR: return m_ctxt->get_type (GCC_JIT_TYPE_FILE_PTR);
+    // case BT_CONST:
+    // case BT_VOLATILE_PTR:
+    // case BT_CONST_VOLATILE_PTR:
+    // case BT_PTRMODE:
+    // case BT_INT_PTR:
+    // case BT_FLOAT_PTR:
+    // case BT_DOUBLE_PTR:
+    // case BT_CONST_DOUBLE_PTR:
+    // case BT_LONGDOUBLE_PTR:
+    // case BT_PID:
+    // case BT_SIZE:
+    // case BT_SSIZE:
+    // case BT_WINT:
+    // case BT_STRING:
+    case BT_CONST_STRING: return m_ctxt->get_type (GCC_JIT_TYPE_CONST_CHAR_PTR);
+    // case BT_DFLOAT32:
+    // case BT_DFLOAT64:
+    // case BT_DFLOAT128:
+    // case BT_DFLOAT32_PTR:
+    // case BT_DFLOAT64_PTR:
+    // case BT_DFLOAT128_PTR:
+    // case BT_VALIST_REF:
+    // case BT_VALIST_ARG:
+    // case BT_I1:
+    // case BT_I2:
+    // case BT_I4:
+    // case BT_I8:
+    // case BT_I16:
+    }
+}
+
+
+function_type *
+builtins_manager::make_fn_type (enum jit_builtin_type,
+				enum jit_builtin_type return_type_id,
+				bool is_variadic,
+				int num_args, ...)
+{
+  va_list list;
+  int i;
+  type **param_types = new type *[num_args];
+  type *return_type = NULL;
+  function_type *result = NULL;
+
+  va_start (list, num_args);
+  for (i = 0; i < num_args; ++i)
+    {
+      enum jit_builtin_type arg_type_id =
+	(enum jit_builtin_type) va_arg (list, int);
+      param_types[i] = get_type (arg_type_id);
+      if (!param_types[i])
+	goto error;
+    }
+  va_end (list);
+
+  return_type = get_type (return_type_id);
+  if (!return_type)
+    goto error;
+
+  result = new function_type (m_ctxt,
+			      return_type,
+			      num_args,
+			      param_types,
+			      is_variadic);
+
+ error:
+  delete[] param_types;
+  return result;
+}
+
+type *
+builtins_manager::make_ptr_type (enum jit_builtin_type,
+				 enum jit_builtin_type other_type_id)
+{
+  type *base_type = get_type (other_type_id);
+  return base_type->get_pointer ();
+}
+
+} // namespace recording
+} // namespace jit
+} // namespace gcc
diff --git a/gcc/jit/jit-builtins.h b/gcc/jit/jit-builtins.h
new file mode 100644
index 0000000..5bcf166
--- /dev/null
+++ b/gcc/jit/jit-builtins.h
@@ -0,0 +1,114 @@ 
+/* jit-builtins.h -- Handling of builtin functions during JIT-compilation.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef JIT_BUILTINS_H
+#define JIT_BUILTINS_H
+
+#include "internal-api.h"
+
+namespace gcc {
+
+namespace jit {
+
+namespace recording {
+
+/* Create an enum of the builtin types.  */
+
+enum jit_builtin_type
+{
+#define DEF_PRIMITIVE_TYPE(NAME, VALUE) NAME,
+#define DEF_FUNCTION_TYPE_0(NAME, RETURN) NAME,
+#define DEF_FUNCTION_TYPE_1(NAME, RETURN, ARG1) NAME,
+#define DEF_FUNCTION_TYPE_2(NAME, RETURN, ARG1, ARG2) NAME,
+#define DEF_FUNCTION_TYPE_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME,
+#define DEF_FUNCTION_TYPE_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME,
+#define DEF_FUNCTION_TYPE_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5) NAME,
+#define DEF_FUNCTION_TYPE_6(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) NAME,
+#define DEF_FUNCTION_TYPE_7(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) NAME,
+#define DEF_FUNCTION_TYPE_8(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8) NAME,
+#define DEF_FUNCTION_TYPE_VAR_0(NAME, RETURN) NAME,
+#define DEF_FUNCTION_TYPE_VAR_1(NAME, RETURN, ARG1) NAME,
+#define DEF_FUNCTION_TYPE_VAR_2(NAME, RETURN, ARG1, ARG2) NAME,
+#define DEF_FUNCTION_TYPE_VAR_3(NAME, RETURN, ARG1, ARG2, ARG3) NAME,
+#define DEF_FUNCTION_TYPE_VAR_4(NAME, RETURN, ARG1, ARG2, ARG3, ARG4) NAME,
+#define DEF_FUNCTION_TYPE_VAR_5(NAME, RETURN, ARG1, ARG2, ARG3, ARG4, ARG6) \
+  NAME,
+#define DEF_POINTER_TYPE(NAME, TYPE) NAME,
+#include "builtin-types.def"
+#undef DEF_PRIMITIVE_TYPE
+#undef DEF_FUNCTION_TYPE_0
+#undef DEF_FUNCTION_TYPE_1
+#undef DEF_FUNCTION_TYPE_2
+#undef DEF_FUNCTION_TYPE_3
+#undef DEF_FUNCTION_TYPE_4
+#undef DEF_FUNCTION_TYPE_5
+#undef DEF_FUNCTION_TYPE_6
+#undef DEF_FUNCTION_TYPE_7
+#undef DEF_FUNCTION_TYPE_8
+#undef DEF_FUNCTION_TYPE_VAR_0
+#undef DEF_FUNCTION_TYPE_VAR_1
+#undef DEF_FUNCTION_TYPE_VAR_2
+#undef DEF_FUNCTION_TYPE_VAR_3
+#undef DEF_FUNCTION_TYPE_VAR_4
+#undef DEF_FUNCTION_TYPE_VAR_5
+#undef DEF_POINTER_TYPE
+  BT_LAST
+}; /* enum jit_builtin_type */
+
+/***********************************************************************/
+
+class builtins_manager
+{
+public:
+  builtins_manager (context *ctxt);
+
+  function *
+  get_builtin_function (const char *name);
+
+private:
+  function *make_builtin_function (enum built_in_function builtin_id);
+
+  type *get_type (enum jit_builtin_type type_id);
+
+  type *make_type (enum jit_builtin_type type_id);
+
+  type*
+  make_primitive_type (enum jit_builtin_type type_id);
+
+  function_type*
+  make_fn_type (enum jit_builtin_type type_id,
+		enum jit_builtin_type return_type_id,
+		bool is_variadic,
+		int num_args, ...);
+
+  type*
+  make_ptr_type (enum jit_builtin_type type_id,
+		 enum jit_builtin_type other_type_id);
+
+private:
+  context *m_ctxt;
+  type *m_types[BT_LAST];
+  function *m_builtin_functions[END_BUILTINS];
+};
+
+} // namespace recording
+} // namespace jit
+} // namespace gcc
+
+#endif /* JIT_BUILTINS_H */
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index de3939c..897b262 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -112,6 +112,8 @@  namespace gccjit
 			   int is_variadic,
 			   location loc = location ());
 
+    function get_builtin_function (const std::string &name);
+
     rvalue new_rvalue (type numeric_type,
 		       int value);
     rvalue zero (type numeric_type);
@@ -563,6 +565,13 @@  context::new_function (enum gcc_jit_function_kind kind,
 						 is_variadic));
 }
 
+inline function
+context::get_builtin_function (const std::string &name)
+{
+  return function (gcc_jit_context_get_builtin_function (m_inner_ctxt,
+							 name.c_str ()));
+}
+
 inline rvalue
 context::new_rvalue (type numeric_type,
 		     int value)
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index bca60bd..e568b3f 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -441,7 +441,18 @@  gcc_jit_context_new_function (gcc_jit_context *ctxt,
     ctxt->new_function (loc, kind, return_type, name,
 			num_params,
 			(gcc::jit::recording::param **)params,
-			is_variadic);
+			is_variadic,
+			BUILT_IN_NONE);
+}
+
+gcc_jit_function *
+gcc_jit_context_get_builtin_function (gcc_jit_context *ctxt,
+				      const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (name, ctxt, "NULL name");
+
+  return static_cast <gcc_jit_function *> (ctxt->get_builtin_function (name));
 }
 
 gcc_jit_object *
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index a4ef65e..3751bdd 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -439,6 +439,10 @@  gcc_jit_context_new_function (gcc_jit_context *ctxt,
 			      gcc_jit_param **params,
 			      int is_variadic);
 
+extern gcc_jit_function *
+gcc_jit_context_get_builtin_function (gcc_jit_context *ctxt,
+				      const char *name);
+
 /* Upcasting from function to object.  */
 extern gcc_jit_object *
 gcc_jit_function_as_object (gcc_jit_function *func);
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index d63f9a3..6dde3a1 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -4,6 +4,7 @@ 
     # Keep this list sorted alphabetically:
     gcc_jit_context_acquire;
     gcc_jit_context_compile;
+    gcc_jit_context_get_builtin_function;
     gcc_jit_context_get_first_error;
     gcc_jit_context_get_type;
     gcc_jit_context_get_int_type;
diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit
index 4d8f209..1aa8082 100644
--- a/gcc/testsuite/ChangeLog.jit
+++ b/gcc/testsuite/ChangeLog.jit
@@ -1,3 +1,12 @@ 
+2014-02-13  David Malcolm  <dmalcolm@redhat.com>
+
+	* jit.dg/harness.h (CHECK_DOUBLE_VALUE): New macro.
+	(CHECK): New macro.
+	* jit.dg/test-functions.c: New testcase, exercising
+	gcc_jit_context_get_builtin_function.
+	* jit.dg/test-combination.c: Add test-functions.c to the combined
+	test.
+
 2014-02-11  David Malcolm  <dmalcolm@redhat.com>
 
 	* jit.dg/test-types.c: Add test coverage for getting type
diff --git a/gcc/testsuite/jit.dg/harness.h b/gcc/testsuite/jit.dg/harness.h
index 7e8341a..aa98028 100644
--- a/gcc/testsuite/jit.dg/harness.h
+++ b/gcc/testsuite/jit.dg/harness.h
@@ -47,9 +47,38 @@  static char test[1024];
     }                                        \
   } while (0)
 
+#define CHECK_DOUBLE_VALUE(ACTUAL, EXPECTED) \
+  do {                                       \
+    double expected = (EXPECTED);	     \
+    double actual = (ACTUAL);		     \
+    if (abs (actual - expected) < 0.00001)   \
+      {                                      \
+	pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \
+      }                                      \
+    else                                     \
+      {                                      \
+	fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \
+	fprintf (stderr, "incorrect value: %f\n", actual); \
+	abort ();                            \
+    }                                        \
+  } while (0)
+
 #define CHECK_STRING_VALUE(ACTUAL, EXPECTED) \
   check_string_value ((ACTUAL), (EXPECTED));
 
+#define CHECK(COND) \
+  do {					\
+    if (COND)				\
+      {				\
+	pass ("%s: %s", test, #COND);	\
+      }				\
+    else				\
+      {				\
+	fail ("%s: %s", test, #COND);	\
+	abort ();			\
+      }				\
+  } while (0)
+
 /* Hooks that testcases should provide.  */
 extern void
 create_code (gcc_jit_context *ctxt, void * user_data);
diff --git a/gcc/testsuite/jit.dg/test-combination.c b/gcc/testsuite/jit.dg/test-combination.c
index ffdf4bb..7fc07ab 100644
--- a/gcc/testsuite/jit.dg/test-combination.c
+++ b/gcc/testsuite/jit.dg/test-combination.c
@@ -52,6 +52,13 @@ 
 #undef create_code
 #undef verify_code
 
+/* test-functions.c */
+#define create_code create_code_functions
+#define verify_code verify_code_functions
+#include "test-functions.c"
+#undef create_code
+#undef verify_code
+
 /* test-hello-world.c */
 #define create_code create_code_hello_world
 #define verify_code verify_code_hello_world
@@ -118,6 +125,7 @@  create_code (gcc_jit_context *ctxt, void * user_data)
   create_code_expressions (ctxt, user_data);
   create_code_factorial (ctxt, user_data);
   create_code_fibonacci (ctxt, user_data);
+  create_code_functions (ctxt, user_data);
   create_code_hello_world (ctxt, user_data);
   create_code_string_literal (ctxt, user_data);
   create_code_sum_of_squares (ctxt, user_data);
@@ -134,6 +142,7 @@  verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
   verify_code_expressions (ctxt, result);
   verify_code_factorial (ctxt, result);
   verify_code_fibonacci (ctxt, result);
+  verify_code_functions (ctxt, result);
   verify_code_hello_world (ctxt, result);
   verify_code_string_literal (ctxt, result);
   verify_code_sum_of_squares (ctxt, result);
diff --git a/gcc/testsuite/jit.dg/test-functions.c b/gcc/testsuite/jit.dg/test-functions.c
new file mode 100644
index 0000000..03f7590
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-functions.c
@@ -0,0 +1,175 @@ 
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+static void
+create_test_of_builtin_strcmp (gcc_jit_context *ctxt)
+{
+  /* Let's try to inject the equivalent of:
+       int
+       test_of_builtin_strcmp (const char *a, const char *b)
+       {
+         return __builtin_strcmp (a, b);
+       }
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+  gcc_jit_type *const_char_ptr_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR);
+
+  /* Get the built-in function.  */
+  gcc_jit_function *builtin_fn =
+    gcc_jit_context_get_builtin_function (ctxt, "strcmp");
+
+  CHECK_STRING_VALUE (
+    gcc_jit_object_get_debug_string (gcc_jit_function_as_object (builtin_fn)),
+    "strcmp");
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_a =
+    gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "a");
+  gcc_jit_param *param_b =
+    gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "b");
+  gcc_jit_param *params[2] = {param_a, param_b};
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  int_type,
+                                  "test_of_builtin_strcmp",
+                                  2, params,
+                                  0);
+  gcc_jit_rvalue *args[2] = {gcc_jit_param_as_rvalue (param_a),
+			     gcc_jit_param_as_rvalue (param_b)};
+  gcc_jit_rvalue *call =
+    gcc_jit_context_new_call (ctxt,
+                              NULL,
+                              builtin_fn,
+                              2, args);
+  CHECK_STRING_VALUE (
+    gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (call)),
+    "strcmp (a, b)");
+
+  gcc_jit_function_add_return (test_fn, NULL, call);
+}
+
+static void
+create_test_of_builtin_trig (gcc_jit_context *ctxt)
+{
+  /* Let's try to inject the equivalent of:
+       int
+       test_of_builtin_trig (double theta)
+       {
+         return 2 * sin (theta) * cos (theta);
+       }
+       (in theory, optimizable to sin (2 * theta))
+  */
+  gcc_jit_type *double_t =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
+
+  /* Get the built-in functions.  */
+  gcc_jit_function *builtin_sin =
+    gcc_jit_context_get_builtin_function (ctxt, "sin");
+  gcc_jit_function *builtin_cos =
+    gcc_jit_context_get_builtin_function (ctxt, "cos");
+
+  /* Build the test_fn.  */
+  gcc_jit_param *param_theta =
+    gcc_jit_context_new_param (ctxt, NULL, double_t, "theta");
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  double_t,
+                                  "test_of_builtin_trig",
+                                  1, &param_theta,
+                                  0);
+  gcc_jit_rvalue *args[1] = {gcc_jit_param_as_rvalue (param_theta)};
+  gcc_jit_rvalue *two =
+    gcc_jit_context_new_rvalue_from_int (ctxt, double_t, 2);
+  gcc_jit_rvalue *ret =
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT,
+      double_t,
+      two,
+      gcc_jit_context_new_binary_op (
+        ctxt, NULL,
+	GCC_JIT_BINARY_OP_MULT,
+	double_t,
+	gcc_jit_context_new_call (ctxt, NULL,
+				  builtin_sin,
+				  1, args),
+	gcc_jit_context_new_call (ctxt, NULL,
+				  builtin_cos,
+				  1, args)));
+  CHECK_STRING_VALUE (
+    gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (ret)),
+    "(double)2 * sin (theta) * cos (theta)");
+
+  gcc_jit_function_add_return (test_fn, NULL, ret);
+}
+
+static void
+create_use_of_builtins (gcc_jit_context *ctxt)
+{
+  create_test_of_builtin_strcmp (ctxt);
+  create_test_of_builtin_trig (ctxt);
+}
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  create_use_of_builtins (ctxt);
+}
+
+static void
+verify_test_of_builtin_strcmp (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef int (*fn_type) (const char *, const char *);
+  CHECK_NON_NULL (result);
+
+  fn_type test_of_builtin_strcmp =
+    (fn_type)gcc_jit_result_get_code (result, "test_of_builtin_strcmp");
+  CHECK_NON_NULL (test_of_builtin_strcmp);
+
+  /* Verify that it correctly called strcmp.  */
+  CHECK_VALUE (test_of_builtin_strcmp ("foo", "foo"), 0);
+  CHECK (test_of_builtin_strcmp ("foo", "bar") > 0);
+  CHECK (test_of_builtin_strcmp ("bar", "foo") < 0);
+}
+
+static void
+verify_test_of_builtin_trig (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  typedef double (*fn_type) (double);
+  CHECK_NON_NULL (result);
+
+  fn_type test_of_builtin_trig =
+    (fn_type)gcc_jit_result_get_code (result, "test_of_builtin_trig");
+  CHECK_NON_NULL (test_of_builtin_trig);
+
+  /* Verify that it correctly computes
+        sin (2 * theta)
+     (perhaps calling sin and cos). */
+  CHECK_DOUBLE_VALUE (test_of_builtin_trig (0.0         ),  0.0);
+  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4      ),  1.0);
+  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_2      ),  0.0);
+  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4 * 3.0), -1.0);
+  CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI        ),  0.0);
+}
+
+static void
+verify_use_of_builtins (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  verify_test_of_builtin_strcmp (ctxt, result);
+  verify_test_of_builtin_trig (ctxt, result);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  verify_use_of_builtins (ctxt, result);
+}