diff mbox

[10/27] New file: gcc/jit/libgccjit.c

Message ID 1414774977-25605-11-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Oct. 31, 2014, 5:02 p.m. UTC
This file implements the entrypoints of the library's public API.

It performs error-checking at this boundary, before calling into the
jit-recording.h internal API.

gcc/jit/
	* libgccjit.c: New.
---
 gcc/jit/libgccjit.c | 1506 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1506 insertions(+)
 create mode 100644 gcc/jit/libgccjit.c

Comments

Jeff Law Nov. 3, 2014, 8:32 p.m. UTC | #1
On 10/31/14 11:02, David Malcolm wrote:
> This file implements the entrypoints of the library's public API.
>
> It performs error-checking at this boundary, before calling into the
> jit-recording.h internal API.
>
> gcc/jit/
> 	* libgccjit.c: New.
> ---
>   gcc/jit/libgccjit.c | 1506 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 1506 insertions(+)
>   create mode 100644 gcc/jit/libgccjit.c
>
> +
> +#define IS_ASCII_ALPHA(CHAR) \
> +  (					\
> +    ((CHAR) >= 'a' && (CHAR) <='z')	\
> +    ||					\
> +    ((CHAR) >= 'A' && (CHAR) <= 'Z')	\
> +  )
> +
> +#define IS_ASCII_DIGIT(CHAR) \
> +  ((CHAR) >= '0' && (CHAR) <='9')
> +
> +#define IS_ASCII_ALNUM(CHAR) \
> +  (IS_ASCII_ALPHA (CHAR) || IS_ASCII_DIGIT (CHAR))
Can't we rely on the C library to give us equivalents?

> +
> +/* TODO: mark failure branches as unlikely? */
> +
Not likely worth the effort.  And it'd be better to somehow mark 
jit_error such that anytime a path unconditionally calls jit_error, the 
whole path is considered unlikely.

I think it was Ball & Larus that had a predictor of this nature, I don't 
recall its effectiveness offhand.  But I'd rather be marking the 
function than sprinlkling unlikely all over the actual codepaths.



> +static bool
> +compatible_types (gcc::jit::recording::type *ltype,
> +		  gcc::jit::recording::type *rtype)
All function definitions should have a block comment describing the 
function and its arguments.   This comment applies throughout the code 
and needs to be addressed prior to commit.  In fact I really can't even 
continue review of this code due to the lack of comments -- I rely 
heavily on them.

jeff
David Malcolm Nov. 4, 2014, 4:57 p.m. UTC | #2
On Mon, 2014-11-03 at 13:32 -0700, Jeff Law wrote:
> On 10/31/14 11:02, David Malcolm wrote:
> > This file implements the entrypoints of the library's public API.
> >
> > It performs error-checking at this boundary, before calling into the
> > jit-recording.h internal API.
> >
> > gcc/jit/
> > 	* libgccjit.c: New.
> > ---
> >   gcc/jit/libgccjit.c | 1506 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 1506 insertions(+)
> >   create mode 100644 gcc/jit/libgccjit.c
> >
> > +
> > +#define IS_ASCII_ALPHA(CHAR) \
> > +  (					\
> > +    ((CHAR) >= 'a' && (CHAR) <='z')	\
> > +    ||					\
> > +    ((CHAR) >= 'A' && (CHAR) <= 'Z')	\
> > +  )
> > +
> > +#define IS_ASCII_DIGIT(CHAR) \
> > +  ((CHAR) >= '0' && (CHAR) <='9')
> > +
> > +#define IS_ASCII_ALNUM(CHAR) \
> > +  (IS_ASCII_ALPHA (CHAR) || IS_ASCII_DIGIT (CHAR))
> Can't we rely on the C library to give us equivalents?

I've been burned in the past by the C library using locales, in
particular the two lowercase "i" variants in Turkish.

These macros are used by gcc_jit_context_new_function to enforce C's
naming restrictions, to avoid errors from the assembler.  The comment I
put there was:

  /* The assembler can only handle certain names, so for now, enforce
     C's rules for identifiers upon the name.
     Eventually we'll need some way to interact with e.g. C++ name mangling.  */

Am I right in thinking that for the assembler we need to enforce the C
naming rules specifically on *ASCII*.

(clearly another comment is needed here).

> > +
> > +/* TODO: mark failure branches as unlikely? */
> > +
> Not likely worth the effort.  And it'd be better to somehow mark 
> jit_error such that anytime a path unconditionally calls jit_error, the 
> whole path is considered unlikely.

(nods)

> I think it was Ball & Larus that had a predictor of this nature, I don't 
> recall its effectiveness offhand.  But I'd rather be marking the 
> function than sprinlkling unlikely all over the actual codepaths.

Presumably by marking it with __attribute__((cold)) ?  (with a suitable
macro in case we're not being compiled with a gcc that supports it).

I suspect doing so will be of more use in teaching me about gcc
internals and the effect on generated code that in measurable benefits
to said code in this case :)

> > +static bool
> > +compatible_types (gcc::jit::recording::type *ltype,
> > +		  gcc::jit::recording::type *rtype)
> All function definitions should have a block comment describing the 
> function and its arguments.   This comment applies throughout the code 
> and needs to be addressed prior to commit.  In fact I really can't even 
> continue review of this code due to the lack of comments -- I rely 
> heavily on them.

Sorry.  I'll post a followup with comments added.

Many of the functions are public API entrypoints, where there's a
comment in the public header.  Should I simply duplicate the comment
from there into the .c file, or put a comment like:

  /* Public entrypoint.  See description in libgccjit.h.  */

for each of these?  (perhaps with additional text giving implementation
notes?)


Thanks for all the reviews.  Looks like this and patch 16 are now the
only non-approved parts of the kit (I didn't see a review of 16).

Dave
Jeff Law Nov. 4, 2014, 9:39 p.m. UTC | #3
On 11/04/14 09:57, David Malcolm wrote:
>>> +#define IS_ASCII_DIGIT(CHAR) \
>>> +  ((CHAR) >= '0' && (CHAR) <='9')
>>> +
>>> +#define IS_ASCII_ALNUM(CHAR) \
>>> +  (IS_ASCII_ALPHA (CHAR) || IS_ASCII_DIGIT (CHAR))
>> Can't we rely on the C library to give us equivalents?
>
> I've been burned in the past by the C library using locales, in
> particular the two lowercase "i" variants in Turkish.
>
> These macros are used by gcc_jit_context_new_function to enforce C's
> naming restrictions, to avoid errors from the assembler.  The comment I
> put there was:
>
>    /* The assembler can only handle certain names, so for now, enforce
>       C's rules for identifiers upon the name.
>       Eventually we'll need some way to interact with e.g. C++ name mangling.  */
>
> Am I right in thinking that for the assembler we need to enforce the C
> naming rules specifically on *ASCII*.
>
> (clearly another comment is needed here).
I guess you've got to do it somewhere.  Presumably there isn't something 
already in GCC that enforces an input character set?  I guess I just 
dislike seeing something that feels like it ought to already be available.

>
> Presumably by marking it with __attribute__((cold)) ?  (with a suitable
> macro in case we're not being compiled with a gcc that supports it).
Yup.  That's precisely what you want since that gives the predictors 
enough information to mark paths as unlikely without having to mark each 
path yourself.

>
> Sorry.  I'll post a followup with comments added.
Thanks.  I probably rely more on those for this kind of review than 
anything, so the lack of them really stood out.

>
> Many of the functions are public API entrypoints, where there's a
> comment in the public header.  Should I simply duplicate the comment
> from there into the .c file, or put a comment like:
Good question.  Normally in the past we'd have you duplicate the 
comment, but with this new usage scenario that may not make a lot of 
sense since one or the other will likely get out of sync at some point.

At this point a snarky comment about generating documentation and the 
interface from a single definition would be appropriate.


>
>    /* Public entrypoint.  See description in libgccjit.h.  */
>
> for each of these?  (perhaps with additional text giving implementation
> notes?)
Let's go with this.  If folks want the comment duplicated, they can 
argue for it after the fact :-)

>
>
> Thanks for all the reviews.  Looks like this and patch 16 are now the
> only non-approved parts of the kit (I didn't see a review of 16).
Right.  I didn't get to #16 yesterday.

jeff
David Malcolm Nov. 5, 2014, 7:34 p.m. UTC | #4
On Tue, 2014-11-04 at 14:39 -0700, Jeff Law wrote:
> On 11/04/14 09:57, David Malcolm wrote:
> >>> +#define IS_ASCII_DIGIT(CHAR) \
> >>> +  ((CHAR) >= '0' && (CHAR) <='9')
> >>> +
> >>> +#define IS_ASCII_ALNUM(CHAR) \
> >>> +  (IS_ASCII_ALPHA (CHAR) || IS_ASCII_DIGIT (CHAR))
> >> Can't we rely on the C library to give us equivalents?
> >
> > I've been burned in the past by the C library using locales, in
> > particular the two lowercase "i" variants in Turkish.
> >
> > These macros are used by gcc_jit_context_new_function to enforce C's
> > naming restrictions, to avoid errors from the assembler.  The comment I
> > put there was:
> >
> >    /* The assembler can only handle certain names, so for now, enforce
> >       C's rules for identifiers upon the name.
> >       Eventually we'll need some way to interact with e.g. C++ name mangling.  */
> >
> > Am I right in thinking that for the assembler we need to enforce the C
> > naming rules specifically on *ASCII*.
> >
> > (clearly another comment is needed here).
> I guess you've got to do it somewhere.  Presumably there isn't something 
> already in GCC that enforces an input character set?  I guess I just 
> dislike seeing something that feels like it ought to already be available.
> 
> >
> > Presumably by marking it with __attribute__((cold)) ?  (with a suitable
> > macro in case we're not being compiled with a gcc that supports it).
> Yup.  That's precisely what you want since that gives the predictors 
> enough information to mark paths as unlikely without having to mark each 
> path yourself.
> 
> >
> > Sorry.  I'll post a followup with comments added.
> Thanks.  I probably rely more on those for this kind of review than 
> anything, so the lack of them really stood out.
> 
> >
> > Many of the functions are public API entrypoints, where there's a
> > comment in the public header.  Should I simply duplicate the comment
> > from there into the .c file, or put a comment like:
> Good question.  Normally in the past we'd have you duplicate the 
> comment, but with this new usage scenario that may not make a lot of 
> sense since one or the other will likely get out of sync at some point.
> 
> At this point a snarky comment about generating documentation and the 
> interface from a single definition would be appropriate.
> 
> 
> >
> >    /* Public entrypoint.  See description in libgccjit.h.  */
> >
> > for each of these?  (perhaps with additional text giving implementation
> > notes?)
> Let's go with this.  If folks want the comment duplicated, they can 
> argue for it after the fact :-)
> 
> >
> >
> > Thanks for all the reviews.  Looks like this and patch 16 are now the
> > only non-approved parts of the kit (I didn't see a review of 16).
> Right.  I didn't get to #16 yesterday.

Thanks.

I've added comments throughout the file.

I didn't bother adding __attribute__((cold)), instead simply dropping
that "TODO".

Attached is the current state of the file gcc/jit/libgccjit.c (on the
branch) for review.

OK for trunk? (conditional on all the rest being approved, and usual
bootstrap&regrtesting; I've merely verified a non-bootstrap compile and
successful make check-jit so far).

There were a few other changes relative to what you've approved, which
I'll post for review shortly.

Dave
Jeff Law Nov. 7, 2014, 7:47 p.m. UTC | #5
On 11/05/14 12:34, David Malcolm wrote:

>
> I've added comments throughout the file.
>
> I didn't bother adding __attribute__((cold)), instead simply dropping
> that "TODO".
Fine.

>
> Attached is the current state of the file gcc/jit/libgccjit.c (on the
> branch) for review.
>
> OK for trunk? (conditional on all the rest being approved, and usual
> bootstrap&regrtesting; I've merely verified a non-bootstrap compile and
> successful make check-jit so far).
>
> There were a few other changes relative to what you've approved, which
> I'll post for review shortly.
>
> Dave
>
>
> libgccjit.c
>
>
> /* Implementation of the C API; all wrappers into the internal C++ API
>     Copyright (C) 2013-2014 Free Software Foundation, Inc.
>     Contributed by David Malcolm<dmalcolm@redhat.com>.
This is fine.  With the comments, it became a lot clearer this was just 
the error checking wrappers and not a whole lot else.

The one thing this does make me wonder is should we add something about 
the error checking may change in significant ways from one release to 
the next, much like the ABI/API.

This seems important as the error checking in many ways specifies the 
language for the JIT and I suspect we haven't got all the corner cases 
sorted out yet (and probably can't until this gets into wider distribution).

jeff
David Malcolm Nov. 7, 2014, 8:28 p.m. UTC | #6
On Fri, 2014-11-07 at 12:47 -0700, Jeff Law wrote:
> On 11/05/14 12:34, David Malcolm wrote:
> 
> >
> > I've added comments throughout the file.
> >
> > I didn't bother adding __attribute__((cold)), instead simply dropping
> > that "TODO".
> Fine.
> 
> >
> > Attached is the current state of the file gcc/jit/libgccjit.c (on the
> > branch) for review.
> >
> > OK for trunk? (conditional on all the rest being approved, and usual
> > bootstrap&regrtesting; I've merely verified a non-bootstrap compile and
> > successful make check-jit so far).
> >
> > There were a few other changes relative to what you've approved, which
> > I'll post for review shortly.
> >
> > Dave
> >
> >
> > libgccjit.c
> >
> >
> > /* Implementation of the C API; all wrappers into the internal C++ API
> >     Copyright (C) 2013-2014 Free Software Foundation, Inc.
> >     Contributed by David Malcolm<dmalcolm@redhat.com>.
> This is fine.  With the comments, it became a lot clearer this was just 
> the error checking wrappers and not a whole lot else.

Thanks.  That was the last of the review requests, so I believe the jit
branch is now approved for merger.  I plan to do this early next week,
to give time to rebase against latest trunk and retest it.

> The one thing this does make me wonder is should we add something about 
> the error checking may change in significant ways from one release to 
> the next, much like the ABI/API.
> 
> This seems important as the error checking in many ways specifies the 
> language for the JIT and I suspect we haven't got all the corner cases 
> sorted out yet (and probably can't until this gets into wider distribution).

I agree that the JIT language is specified by the runtime error-checking
behaviour, but it's also specified by the types in the API.

We're in slightly better shape here than, say gcc's internal tree API,
in that the types in the API make a rigid separation between types vs
expressions, and it also captures some lvalue vs rvalue distinctions, so
client code is likely to not compile if it gets those things wrong.
I've also tried to name the params in such a way as to hint at
restrictions, e.g. gcc_jit_context_zero's second param is:
  gcc_jit_type *numeric_type
i.e. the "numeric_type" name of that param describes a requirement.

There are probably some under-specified behaviors in the JIT language as
it stands - perhaps in ordering of operations?

In any case, the current disclaimer reads:

"Note that libgccjit is currently of “Alpha” quality; the APIs are not
yet set in stone, and they shouldn’t be used in production yet."

I'm sure we'll want to reword that at some point.

One big potential change might be to create a stable *plugin* API,
unified with the JIT API (so we'd allow client code to use the same API
for both embedding GCC and being embedded within GCC).  Most of the
"gcc_jit_*" symbols would become just "gcc_*".

I've experimented with that, but I don't see myself getting it done in
time for the close of stage1 close (am frantically trying to finish the
gimple-classes work), so I'm thinking of that unified jit-and-plugin API
as a GCC 6 feature.

Dave
diff mbox

Patch

diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
new file mode 100644
index 0000000..286a85e
--- /dev/null
+++ b/gcc/jit/libgccjit.c
@@ -0,0 +1,1506 @@ 
+/* Implementation of the C API; all wrappers into the internal C++ API
+   Copyright (C) 2013-2014 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+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 "coretypes.h"
+#include "opts.h"
+
+#include "libgccjit.h"
+#include "jit-common.h"
+#include "jit-recording.h"
+
+#define IS_ASCII_ALPHA(CHAR) \
+  (					\
+    ((CHAR) >= 'a' && (CHAR) <='z')	\
+    ||					\
+    ((CHAR) >= 'A' && (CHAR) <= 'Z')	\
+  )
+
+#define IS_ASCII_DIGIT(CHAR) \
+  ((CHAR) >= '0' && (CHAR) <='9')
+
+#define IS_ASCII_ALNUM(CHAR) \
+  (IS_ASCII_ALPHA (CHAR) || IS_ASCII_DIGIT (CHAR))
+
+struct gcc_jit_context : public gcc::jit::recording::context
+{
+  gcc_jit_context (gcc_jit_context *parent_ctxt) :
+    context (parent_ctxt)
+  {}
+};
+
+struct gcc_jit_result : public gcc::jit::result
+{
+};
+
+struct gcc_jit_object : public gcc::jit::recording::memento
+{
+};
+
+struct gcc_jit_location : public gcc::jit::recording::location
+{
+};
+
+struct gcc_jit_type : public gcc::jit::recording::type
+{
+};
+
+struct gcc_jit_struct : public gcc::jit::recording::struct_
+{
+};
+
+struct gcc_jit_field : public gcc::jit::recording::field
+{
+};
+
+struct gcc_jit_function : public gcc::jit::recording::function
+{
+};
+
+struct gcc_jit_block : public gcc::jit::recording::block
+{
+};
+
+struct gcc_jit_rvalue : public gcc::jit::recording::rvalue
+{
+};
+
+struct gcc_jit_lvalue : public gcc::jit::recording::lvalue
+{
+};
+
+struct gcc_jit_param : public gcc::jit::recording::param
+{
+};
+
+/**********************************************************************
+ Error-handling.
+
+ We try to gracefully handle API usage errors by being defensive
+ at the API boundary.
+ **********************************************************************/
+
+#define JIT_BEGIN_STMT do {
+#define JIT_END_STMT   } while(0)
+
+/* TODO: mark failure branches as unlikely? */
+
+#define RETURN_VAL_IF_FAIL(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_MSG)	\
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: %s", __func__, (ERR_MSG));	\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_VAL_IF_FAIL_PRINTF1(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,			\
+		   __func__, (A0));				\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_VAL_IF_FAIL_PRINTF2(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1));				\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_VAL_IF_FAIL_PRINTF3(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1), (A2));			\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_VAL_IF_FAIL_PRINTF4(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1), (A2), (A3));			\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_VAL_IF_FAIL_PRINTF6(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1), (A2), (A3), (A4), (A5));	\
+	return (RETURN_EXPR);						\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_NULL_IF_FAIL(TEST_EXPR, CTXT, LOC, ERR_MSG) \
+  RETURN_VAL_IF_FAIL ((TEST_EXPR), NULL, (CTXT), (LOC), (ERR_MSG))
+
+#define RETURN_NULL_IF_FAIL_PRINTF1(TEST_EXPR, CTXT, LOC, ERR_FMT, A0) \
+  RETURN_VAL_IF_FAIL_PRINTF1 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0)
+
+#define RETURN_NULL_IF_FAIL_PRINTF2(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
+  RETURN_VAL_IF_FAIL_PRINTF2 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1)
+
+#define RETURN_NULL_IF_FAIL_PRINTF3(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2) \
+  RETURN_VAL_IF_FAIL_PRINTF3 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2)
+
+#define RETURN_NULL_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
+  RETURN_VAL_IF_FAIL_PRINTF4 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3)
+
+#define RETURN_NULL_IF_FAIL_PRINTF6(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
+  RETURN_VAL_IF_FAIL_PRINTF6 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5)
+
+#define RETURN_IF_FAIL(TEST_EXPR, CTXT, LOC, ERR_MSG)			\
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: %s", __func__, (ERR_MSG));		\
+	return;							\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_IF_FAIL_PRINTF1(TEST_EXPR, CTXT, LOC, ERR_FMT, A0) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0));					\
+	return;							\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_IF_FAIL_PRINTF2(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1));				\
+	return;							\
+      }								\
+  JIT_END_STMT
+
+#define RETURN_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
+  JIT_BEGIN_STMT							\
+    if (!(TEST_EXPR))							\
+      {								\
+	jit_error ((CTXT), (LOC), "%s: " ERR_FMT,				\
+		   __func__, (A0), (A1), (A2), (A3));			\
+	return;							\
+      }								\
+  JIT_END_STMT
+
+/* Check that BLOCK is non-NULL, and that it's OK to add statements to
+   it.  */
+#define RETURN_IF_NOT_VALID_BLOCK(BLOCK, LOC)				\
+  JIT_BEGIN_STMT							\
+    RETURN_IF_FAIL ((BLOCK), NULL, (LOC), "NULL block");		\
+    RETURN_IF_FAIL_PRINTF2 (						\
+      !(BLOCK)->has_been_terminated (),				\
+      (BLOCK)->get_context (),						\
+      (LOC),								\
+      "adding to terminated block: %s (already terminated by: %s)",	\
+      (BLOCK)->get_debug_string (),					\
+      (BLOCK)->get_last_statement ()->get_debug_string ());		\
+  JIT_END_STMT
+
+#define RETURN_NULL_IF_NOT_VALID_BLOCK(BLOCK, LOC)			\
+  JIT_BEGIN_STMT							\
+    RETURN_NULL_IF_FAIL ((BLOCK), NULL, (LOC), "NULL block");		\
+    RETURN_NULL_IF_FAIL_PRINTF2 (					\
+      !(BLOCK)->has_been_terminated (),				\
+      (BLOCK)->get_context (),						\
+      (LOC),								\
+      "adding to terminated block: %s (already terminated by: %s)",	\
+      (BLOCK)->get_debug_string (),					\
+      (BLOCK)->get_last_statement ()->get_debug_string ());		\
+  JIT_END_STMT
+
+static void
+jit_error (gcc::jit::recording::context *ctxt,
+	   gcc_jit_location *loc,
+	   const char *fmt, ...)
+  GNU_PRINTF(3, 4);
+
+static void
+jit_error (gcc::jit::recording::context *ctxt,
+	   gcc_jit_location *loc,
+	   const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+
+  if (ctxt)
+    ctxt->add_error_va (loc, fmt, ap);
+  else
+    {
+      /* No context?  Send to stderr.  */
+      vfprintf (stderr, fmt, ap);
+      fprintf (stderr, "\n");
+    }
+
+  va_end (ap);
+}
+
+static bool
+compatible_types (gcc::jit::recording::type *ltype,
+		  gcc::jit::recording::type *rtype)
+{
+  return ltype->accepts_writes_from (rtype);
+}
+
+gcc_jit_context *
+gcc_jit_context_acquire (void)
+{
+  return new gcc_jit_context (NULL);
+}
+
+void
+gcc_jit_context_release (gcc_jit_context *ctxt)
+{
+  delete ctxt;
+}
+
+gcc_jit_context *
+gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt)
+{
+  return new gcc_jit_context (parent_ctxt);
+}
+
+gcc_jit_location *
+gcc_jit_context_new_location (gcc_jit_context *ctxt,
+			      const char *filename,
+			      int line,
+			      int column)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+
+  return (gcc_jit_location *)ctxt->new_location (filename, line, column);
+}
+
+gcc_jit_object *
+gcc_jit_location_as_object (gcc_jit_location *loc)
+{
+  RETURN_NULL_IF_FAIL (loc, NULL, NULL, "NULL location");
+
+  return static_cast <gcc_jit_object *> (loc->as_object ());
+}
+
+gcc_jit_object *
+gcc_jit_type_as_object (gcc_jit_type *type)
+{
+  RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
+
+  return static_cast <gcc_jit_object *> (type->as_object ());
+}
+
+gcc_jit_type *
+gcc_jit_context_get_type (gcc_jit_context *ctxt,
+			  enum gcc_jit_types type)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  /* The inner function checks "type" for us.  */
+
+  return (gcc_jit_type *)ctxt->get_type (type);
+}
+
+gcc_jit_type *
+gcc_jit_context_get_int_type (gcc_jit_context *ctxt,
+			      int num_bytes, int is_signed)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (num_bytes >= 0, ctxt, NULL, "negative size");
+
+  return (gcc_jit_type *)ctxt->get_int_type (num_bytes, is_signed);
+}
+
+gcc_jit_type *
+gcc_jit_type_get_pointer (gcc_jit_type *type)
+{
+  RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
+
+  return (gcc_jit_type *)type->get_pointer ();
+}
+
+gcc_jit_type *
+gcc_jit_type_get_const (gcc_jit_type *type)
+{
+  RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
+
+  return (gcc_jit_type *)type->get_const ();
+}
+
+gcc_jit_type *
+gcc_jit_type_get_volatile (gcc_jit_type *type)
+{
+  RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
+
+  return (gcc_jit_type *)type->get_volatile ();
+}
+
+gcc_jit_type *
+gcc_jit_context_new_array_type (gcc_jit_context *ctxt,
+				gcc_jit_location *loc,
+				gcc_jit_type *element_type,
+				int num_elements)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (element_type, ctxt, loc, "NULL type");
+
+  return (gcc_jit_type *)ctxt->new_array_type (loc,
+					       element_type,
+					       num_elements);
+}
+
+gcc_jit_field *
+gcc_jit_context_new_field (gcc_jit_context *ctxt,
+			   gcc_jit_location *loc,
+			   gcc_jit_type *type,
+			   const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+
+  return (gcc_jit_field *)ctxt->new_field (loc, type, name);
+}
+
+gcc_jit_object *
+gcc_jit_field_as_object (gcc_jit_field *field)
+{
+  RETURN_NULL_IF_FAIL (field, NULL, NULL, "NULL field");
+
+  return static_cast <gcc_jit_object *> (field->as_object ());
+}
+
+gcc_jit_struct *
+gcc_jit_context_new_struct_type (gcc_jit_context *ctxt,
+				 gcc_jit_location *loc,
+				 const char *name,
+				 int num_fields,
+				 gcc_jit_field **fields)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+  if (num_fields)
+    RETURN_NULL_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
+  for (int i = 0; i < num_fields; i++)
+    {
+      RETURN_NULL_IF_FAIL (fields[i], ctxt, loc, "NULL field ptr");
+      RETURN_NULL_IF_FAIL_PRINTF2 (
+	NULL == fields[i]->get_container (),
+	ctxt, loc,
+	"%s is already a field of %s",
+	fields[i]->get_debug_string (),
+	fields[i]->get_container ()->get_debug_string ());
+    }
+
+  gcc::jit::recording::struct_ *result =
+    ctxt->new_struct_type (loc, name);
+  result->set_fields (loc,
+		      num_fields,
+		      (gcc::jit::recording::field **)fields);
+  return static_cast<gcc_jit_struct *> (result);
+}
+
+gcc_jit_struct *
+gcc_jit_context_new_opaque_struct (gcc_jit_context *ctxt,
+				   gcc_jit_location *loc,
+				   const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+
+  return (gcc_jit_struct *)ctxt->new_struct_type (loc, name);
+}
+
+gcc_jit_type *
+gcc_jit_struct_as_type (gcc_jit_struct *struct_type)
+{
+  RETURN_NULL_IF_FAIL (struct_type, NULL, NULL, "NULL struct_type");
+
+  return static_cast <gcc_jit_type *> (struct_type->as_type ());
+}
+
+void
+gcc_jit_struct_set_fields (gcc_jit_struct *struct_type,
+			   gcc_jit_location *loc,
+			   int num_fields,
+			   gcc_jit_field **fields)
+{
+  RETURN_IF_FAIL (struct_type, NULL, loc, "NULL struct_type");
+  gcc::jit::recording::context *ctxt = struct_type->m_ctxt;
+  RETURN_IF_FAIL_PRINTF1 (
+    NULL == struct_type->get_fields (), ctxt, loc,
+    "%s already has had fields set",
+    struct_type->get_debug_string ());
+  if (num_fields)
+    RETURN_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
+  for (int i = 0; i < num_fields; i++)
+    {
+      RETURN_IF_FAIL (fields[i], ctxt, loc, "NULL field ptr");
+      RETURN_IF_FAIL_PRINTF2 (
+	NULL == fields[i]->get_container (),
+	ctxt, loc,
+	"%s is already a field of %s",
+	fields[i]->get_debug_string (),
+	fields[i]->get_container ()->get_debug_string ());
+    }
+
+  struct_type->set_fields (loc, num_fields,
+			   (gcc::jit::recording::field **)fields);
+}
+
+gcc_jit_type *
+gcc_jit_context_new_union_type (gcc_jit_context *ctxt,
+				gcc_jit_location *loc,
+				const char *name,
+				int num_fields,
+				gcc_jit_field **fields)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+  if (num_fields)
+    RETURN_NULL_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
+  for (int i = 0; i < num_fields; i++)
+    {
+      RETURN_NULL_IF_FAIL (fields[i], ctxt, loc, "NULL field ptr");
+      RETURN_NULL_IF_FAIL_PRINTF2 (
+	NULL == fields[i]->get_container (),
+	ctxt, loc,
+	"%s is already a field of %s",
+	fields[i]->get_debug_string (),
+	fields[i]->get_container ()->get_debug_string ());
+    }
+
+  gcc::jit::recording::union_ *result =
+    ctxt->new_union_type (loc, name);
+  result->set_fields (loc,
+		      num_fields,
+		      (gcc::jit::recording::field **)fields);
+  return (gcc_jit_type *) (result);
+}
+
+gcc_jit_type *
+gcc_jit_context_new_function_ptr_type (gcc_jit_context *ctxt,
+				       gcc_jit_location *loc,
+				       gcc_jit_type *return_type,
+				       int num_params,
+				       gcc_jit_type **param_types,
+				       int is_variadic)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (return_type, ctxt, loc, "NULL return_type");
+  RETURN_NULL_IF_FAIL (
+    (num_params == 0) || param_types,
+    ctxt, loc,
+    "NULL param_types creating function pointer type");
+  for (int i = 0; i < num_params; i++)
+    RETURN_NULL_IF_FAIL_PRINTF1 (
+      param_types[i],
+      ctxt, loc,
+      "NULL parameter type %i creating function pointer type", i);
+
+  return (gcc_jit_type*)
+    ctxt->new_function_ptr_type (loc, return_type,
+				 num_params,
+				 (gcc::jit::recording::type **)param_types,
+				 is_variadic);
+}
+
+/* Constructing functions.  */
+gcc_jit_param *
+gcc_jit_context_new_param (gcc_jit_context *ctxt,
+			   gcc_jit_location *loc,
+			   gcc_jit_type *type,
+			   const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+
+  return (gcc_jit_param *)ctxt->new_param (loc, type, name);
+}
+
+gcc_jit_object *
+gcc_jit_param_as_object (gcc_jit_param *param)
+{
+  RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
+
+  return static_cast <gcc_jit_object *> (param->as_object ());
+}
+
+gcc_jit_lvalue *
+gcc_jit_param_as_lvalue (gcc_jit_param *param)
+{
+  RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
+
+  return (gcc_jit_lvalue *)param->as_lvalue ();
+}
+
+gcc_jit_rvalue *
+gcc_jit_param_as_rvalue (gcc_jit_param *param)
+{
+  RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
+
+  return (gcc_jit_rvalue *)param->as_rvalue ();
+}
+
+gcc_jit_function *
+gcc_jit_context_new_function (gcc_jit_context *ctxt,
+			      gcc_jit_location *loc,
+			      enum gcc_jit_function_kind kind,
+			      gcc_jit_type *return_type,
+			      const char *name,
+			      int num_params,
+			      gcc_jit_param **params,
+			      int is_variadic)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (return_type, ctxt, loc, "NULL return_type");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+  /* The assembler can only handle certain names, so for now, enforce
+     C's rules for identiers upon the name.
+     Eventually we'll need some way to interact with e.g. C++ name mangling.  */
+  {
+    /* Leading char: */
+    char ch = *name;
+    RETURN_NULL_IF_FAIL_PRINTF2 (
+	IS_ASCII_ALPHA (ch) || ch == '_',
+	ctxt, loc,
+	"name \"%s\" contains invalid character: '%c'",
+	name, ch);
+    /* Subsequent chars: */
+    for (const char *ptr = name + 1; (ch = *ptr); ptr++)
+      {
+	RETURN_NULL_IF_FAIL_PRINTF2 (
+	  IS_ASCII_ALNUM (ch) || ch == '_',
+	  ctxt, loc,
+	  "name \"%s\" contains invalid character: '%c'",
+	  name, ch);
+      }
+  }
+  RETURN_NULL_IF_FAIL_PRINTF1 (
+    (num_params == 0) || params,
+    ctxt, loc,
+    "NULL params creating function %s", name);
+  for (int i = 0; i < num_params; i++)
+    RETURN_NULL_IF_FAIL_PRINTF2 (
+      params[i],
+      ctxt, loc,
+      "NULL parameter %i creating function %s", i, name);
+
+  return (gcc_jit_function*)
+    ctxt->new_function (loc, kind, return_type, name,
+			num_params,
+			(gcc::jit::recording::param **)params,
+			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, "NULL context");
+  RETURN_NULL_IF_FAIL (name, ctxt, NULL, "NULL name");
+
+  return static_cast <gcc_jit_function *> (ctxt->get_builtin_function (name));
+}
+
+gcc_jit_object *
+gcc_jit_function_as_object (gcc_jit_function *func)
+{
+  RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
+
+  return static_cast <gcc_jit_object *> (func->as_object ());
+}
+
+gcc_jit_param *
+gcc_jit_function_get_param (gcc_jit_function *func, int index)
+{
+  RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
+  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_NULL_IF_FAIL (index >= 0, ctxt, NULL, "negative index");
+  int num_params = func->get_params ().length ();
+  RETURN_NULL_IF_FAIL_PRINTF3 (index < num_params,
+			       ctxt, NULL,
+			       "index of %d is too large (%s has %d params)",
+			       index,
+			       func->get_debug_string (),
+			       num_params);
+
+  return static_cast <gcc_jit_param *> (func->get_param (index));
+}
+
+void
+gcc_jit_function_dump_to_dot (gcc_jit_function *func,
+			      const char *path)
+{
+  RETURN_IF_FAIL (func, NULL, NULL, "NULL function");
+  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
+
+  func->dump_to_dot (path);
+}
+
+gcc_jit_block*
+gcc_jit_function_new_block (gcc_jit_function *func,
+			    const char *name)
+{
+  RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
+  RETURN_NULL_IF_FAIL (func->get_kind () != GCC_JIT_FUNCTION_IMPORTED,
+		       func->get_context (), NULL,
+		       "cannot add block to an imported function");
+  /* name can be NULL.  */
+
+  return (gcc_jit_block *)func->new_block (name);
+}
+
+gcc_jit_object *
+gcc_jit_block_as_object (gcc_jit_block *block)
+{
+  RETURN_NULL_IF_FAIL (block, NULL, NULL, "NULL block");
+
+  return static_cast <gcc_jit_object *> (block->as_object ());
+}
+
+gcc_jit_function *
+gcc_jit_block_get_function (gcc_jit_block *block)
+{
+  RETURN_NULL_IF_FAIL (block, NULL, NULL, "NULL block");
+
+  return static_cast <gcc_jit_function *> (block->get_function ());
+}
+
+gcc_jit_lvalue *
+gcc_jit_context_new_global (gcc_jit_context *ctxt,
+			    gcc_jit_location *loc,
+			    gcc_jit_type *type,
+			    const char *name)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+
+  return (gcc_jit_lvalue *)ctxt->new_global (loc, type, name);
+}
+
+gcc_jit_object *
+gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue)
+{
+  RETURN_NULL_IF_FAIL (lvalue, NULL, NULL, "NULL lvalue");
+
+  return static_cast <gcc_jit_object *> (lvalue->as_object ());
+}
+
+gcc_jit_rvalue *
+gcc_jit_lvalue_as_rvalue (gcc_jit_lvalue *lvalue)
+{
+  RETURN_NULL_IF_FAIL (lvalue, NULL, NULL, "NULL lvalue");
+
+  return (gcc_jit_rvalue *)lvalue->as_rvalue ();
+}
+
+gcc_jit_object *
+gcc_jit_rvalue_as_object (gcc_jit_rvalue *rvalue)
+{
+  RETURN_NULL_IF_FAIL (rvalue, NULL, NULL, "NULL rvalue");
+
+  return static_cast <gcc_jit_object *> (rvalue->as_object ());
+}
+
+gcc_jit_type *
+gcc_jit_rvalue_get_type (gcc_jit_rvalue *rvalue)
+{
+  RETURN_NULL_IF_FAIL (rvalue, NULL, NULL, "NULL rvalue");
+
+  return static_cast <gcc_jit_type *> (rvalue->get_type ());
+}
+
+#define RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE(CTXT, NUMERIC_TYPE) \
+  RETURN_NULL_IF_FAIL (NUMERIC_TYPE, CTXT, NULL, "NULL type"); \
+  RETURN_NULL_IF_FAIL_PRINTF1 (                                \
+    NUMERIC_TYPE->is_numeric (), ctxt, NULL,                   \
+    "not a numeric type: %s",                                  \
+    NUMERIC_TYPE->get_debug_string ());
+
+gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_from_int (gcc_jit_context *ctxt,
+				     gcc_jit_type *numeric_type,
+				     int value)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
+
+  return (gcc_jit_rvalue *)ctxt->new_rvalue_from_int (numeric_type, value);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_zero (gcc_jit_context *ctxt,
+		      gcc_jit_type *numeric_type)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
+
+  return gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 0);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_one (gcc_jit_context *ctxt,
+		     gcc_jit_type *numeric_type)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
+
+  return gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 1);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_from_double (gcc_jit_context *ctxt,
+					gcc_jit_type *numeric_type,
+					double value)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
+
+  return (gcc_jit_rvalue *)ctxt->new_rvalue_from_double (numeric_type, value);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_rvalue_from_ptr (gcc_jit_context *ctxt,
+				     gcc_jit_type *pointer_type,
+				     void *value)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (pointer_type, ctxt, NULL, "NULL type");
+  RETURN_NULL_IF_FAIL_PRINTF1 (
+    pointer_type->is_pointer (),
+    ctxt, NULL,
+    "not a pointer type (type: %s)",
+    pointer_type->get_debug_string ());
+
+  return (gcc_jit_rvalue *)ctxt->new_rvalue_from_ptr (pointer_type, value);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_null (gcc_jit_context *ctxt,
+		      gcc_jit_type *pointer_type)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (pointer_type, ctxt, NULL, "NULL type");
+  RETURN_NULL_IF_FAIL_PRINTF1 (
+    pointer_type->is_pointer (),
+    ctxt, NULL,
+    "not a pointer type (type: %s)",
+    pointer_type->get_debug_string ());
+
+  return gcc_jit_context_new_rvalue_from_ptr (ctxt, pointer_type, NULL);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_string_literal (gcc_jit_context *ctxt,
+				    const char *value)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_NULL_IF_FAIL (value, ctxt, NULL, "NULL value");
+
+  return (gcc_jit_rvalue *)ctxt->new_string_literal (value);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_unary_op (gcc_jit_context *ctxt,
+			      gcc_jit_location *loc,
+			      enum gcc_jit_unary_op op,
+			      gcc_jit_type *result_type,
+			      gcc_jit_rvalue *rvalue)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  /* op is checked by the inner function.  */
+  RETURN_NULL_IF_FAIL (result_type, ctxt, loc, "NULL result_type");
+  RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+
+  return (gcc_jit_rvalue *)ctxt->new_unary_op (loc, op, result_type, rvalue);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_binary_op (gcc_jit_context *ctxt,
+			       gcc_jit_location *loc,
+			       enum gcc_jit_binary_op op,
+			       gcc_jit_type *result_type,
+			       gcc_jit_rvalue *a, gcc_jit_rvalue *b)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  /* op is checked by the inner function.  */
+  RETURN_NULL_IF_FAIL (result_type, ctxt, loc, "NULL result_type");
+  RETURN_NULL_IF_FAIL (a, ctxt, loc, "NULL a");
+  RETURN_NULL_IF_FAIL (b, ctxt, loc, "NULL b");
+  RETURN_NULL_IF_FAIL_PRINTF4 (
+    a->get_type () == b->get_type (),
+    ctxt, loc,
+    "mismatching types for binary op:"
+    " a: %s (type: %s) b: %s (type: %s)",
+    a->get_debug_string (),
+    a->get_type ()->get_debug_string (),
+    b->get_debug_string (),
+    b->get_type ()->get_debug_string ());
+
+  return (gcc_jit_rvalue *)ctxt->new_binary_op (loc, op, result_type, a, b);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_comparison (gcc_jit_context *ctxt,
+				gcc_jit_location *loc,
+				enum gcc_jit_comparison op,
+				gcc_jit_rvalue *a, gcc_jit_rvalue *b)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  /* op is checked by the inner function.  */
+  RETURN_NULL_IF_FAIL (a, ctxt, loc, "NULL a");
+  RETURN_NULL_IF_FAIL (b, ctxt, loc, "NULL b");
+  RETURN_NULL_IF_FAIL_PRINTF4 (
+    a->get_type ()->unqualified () == b->get_type ()->unqualified (),
+    ctxt, loc,
+    "mismatching types for comparison:"
+    " a: %s (type: %s) b: %s (type: %s)",
+    a->get_debug_string (),
+    a->get_type ()->get_debug_string (),
+    b->get_debug_string (),
+    b->get_type ()->get_debug_string ());
+
+  return (gcc_jit_rvalue *)ctxt->new_comparison (loc, op, a, b);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_call (gcc_jit_context *ctxt,
+			  gcc_jit_location *loc,
+			  gcc_jit_function *func,
+			  int numargs , gcc_jit_rvalue **args)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (func, ctxt, loc, "NULL function");
+  if (numargs)
+    RETURN_NULL_IF_FAIL (args, ctxt, loc, "NULL args");
+
+  int min_num_params = func->get_params ().length ();
+  bool is_variadic = func->is_variadic ();
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    numargs >= min_num_params,
+    ctxt, loc,
+    "not enough arguments to function \"%s\""
+    " (got %i args, expected %i)",
+    func->get_name ()->c_str (),
+    numargs, min_num_params);
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    (numargs == min_num_params || is_variadic),
+    ctxt, loc,
+    "too many arguments to function \"%s\""
+    " (got %i args, expected %i)",
+    func->get_name ()->c_str (),
+    numargs, min_num_params);
+
+  for (int i = 0; i < min_num_params; i++)
+    {
+      gcc::jit::recording::param *param = func->get_param (i);
+      gcc_jit_rvalue *arg = args[i];
+
+      RETURN_NULL_IF_FAIL_PRINTF4 (
+	arg,
+	ctxt, loc,
+	"NULL argument %i to function \"%s\":"
+	" param %s (type: %s)",
+	i + 1,
+	func->get_name ()->c_str (),
+	param->get_debug_string (),
+	param->get_type ()->get_debug_string ());
+
+      RETURN_NULL_IF_FAIL_PRINTF6 (
+	compatible_types (param->get_type (),
+			  arg->get_type ()),
+	ctxt, loc,
+	"mismatching types for argument %d of function \"%s\":"
+	" assignment to param %s (type: %s) from %s (type: %s)",
+	i + 1,
+	func->get_name ()->c_str (),
+	param->get_debug_string (),
+	param->get_type ()->get_debug_string (),
+	arg->get_debug_string (),
+	arg->get_type ()->get_debug_string ());
+    }
+
+  return (gcc_jit_rvalue *)ctxt->new_call (loc,
+					   func,
+					   numargs,
+					   (gcc::jit::recording::rvalue **)args);
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_call_through_ptr (gcc_jit_context *ctxt,
+				      gcc_jit_location *loc,
+				      gcc_jit_rvalue *fn_ptr,
+				      int numargs, gcc_jit_rvalue **args)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (fn_ptr, ctxt, loc, "NULL fn_ptr");
+  if (numargs)
+    RETURN_NULL_IF_FAIL (args, ctxt, loc, "NULL args");
+
+  gcc::jit::recording::type *ptr_type = fn_ptr->get_type ()->dereference ();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    ptr_type, ctxt, loc,
+    "fn_ptr is not a ptr: %s"
+    " type: %s",
+    fn_ptr->get_debug_string (),
+    fn_ptr->get_type ()->get_debug_string ());
+
+  gcc::jit::recording::function_type *fn_type =
+    ptr_type->dyn_cast_function_type();
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    fn_type, ctxt, loc,
+    "fn_ptr is not a function ptr: %s"
+    " type: %s",
+    fn_ptr->get_debug_string (),
+    fn_ptr->get_type ()->get_debug_string ());
+
+  int min_num_params = fn_type->get_param_types ().length ();
+  bool is_variadic = fn_type->is_variadic ();
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    numargs >= min_num_params,
+    ctxt, loc,
+    "not enough arguments to fn_ptr: %s"
+    " (got %i args, expected %i)",
+    fn_ptr->get_debug_string (),
+    numargs, min_num_params);
+
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    (numargs == min_num_params || is_variadic),
+    ctxt, loc,
+    "too many arguments to fn_ptr: %s"
+    " (got %i args, expected %i)",
+    fn_ptr->get_debug_string (),
+    numargs, min_num_params);
+
+  for (int i = 0; i < min_num_params; i++)
+    {
+      gcc::jit::recording::type *param_type = fn_type->get_param_types ()[i];
+      gcc_jit_rvalue *arg = args[i];
+
+      RETURN_NULL_IF_FAIL_PRINTF3 (
+	arg,
+	ctxt, loc,
+	"NULL argument %i to fn_ptr: %s"
+	" (type: %s)",
+	i + 1,
+	fn_ptr->get_debug_string (),
+	param_type->get_debug_string ());
+
+      RETURN_NULL_IF_FAIL_PRINTF6 (
+	compatible_types (param_type,
+			  arg->get_type ()),
+	ctxt, loc,
+	"mismatching types for argument %d of fn_ptr: %s:"
+	" assignment to param %d (type: %s) from %s (type: %s)",
+	i + 1,
+	fn_ptr->get_debug_string (),
+	i + 1,
+	param_type->get_debug_string (),
+	arg->get_debug_string (),
+	arg->get_type ()->get_debug_string ());
+    }
+
+  return (gcc_jit_rvalue *)(
+	    ctxt->new_call_through_ptr (loc,
+					fn_ptr,
+					numargs,
+					(gcc::jit::recording::rvalue **)args));
+}
+
+static bool
+is_valid_cast (gcc::jit::recording::type *src_type,
+	       gcc_jit_type *dst_type)
+{
+  bool src_is_int = src_type->is_int ();
+  bool dst_is_int = dst_type->is_int ();
+  bool src_is_float = src_type->is_float ();
+  bool dst_is_float = dst_type->is_float ();
+  bool src_is_bool = src_type->is_bool ();
+  bool dst_is_bool = dst_type->is_bool ();
+
+  if (src_is_int)
+    if (dst_is_int || dst_is_float || dst_is_bool)
+      return true;
+
+  if (src_is_float)
+    if (dst_is_int || dst_is_float)
+      return true;
+
+  if (src_is_bool)
+    if (dst_is_int || dst_is_bool)
+      return true;
+
+  /* Permit casts between pointer types.  */
+  gcc::jit::recording::type *deref_src_type = src_type->is_pointer ();
+  gcc::jit::recording::type *deref_dst_type = dst_type->is_pointer ();
+  if (deref_src_type && deref_dst_type)
+    return true;
+
+  return false;
+}
+
+gcc_jit_rvalue *
+gcc_jit_context_new_cast (gcc_jit_context *ctxt,
+			  gcc_jit_location *loc,
+			  gcc_jit_rvalue *rvalue,
+			  gcc_jit_type *type)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+  RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    is_valid_cast (rvalue->get_type (), type),
+    ctxt, loc,
+    "cannot cast %s from type: %s to type: %s",
+    rvalue->get_debug_string (),
+    rvalue->get_type ()->get_debug_string (),
+    type->get_debug_string ());
+
+  return static_cast <gcc_jit_rvalue *> (ctxt->new_cast (loc, rvalue, type));
+}
+
+extern gcc_jit_lvalue *
+gcc_jit_context_new_array_access (gcc_jit_context *ctxt,
+				  gcc_jit_location *loc,
+				  gcc_jit_rvalue *ptr,
+				  gcc_jit_rvalue *index)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
+  RETURN_NULL_IF_FAIL (ptr, ctxt, loc, "NULL ptr");
+  RETURN_NULL_IF_FAIL (index, ctxt, loc, "NULL index");
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    ptr->get_type ()->dereference (),
+    ctxt, loc,
+    "ptr: %s (type: %s) is not a pointer or array",
+    ptr->get_debug_string (),
+    ptr->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_array_access (loc, ptr, index);
+}
+
+gcc_jit_context *
+gcc_jit_object_get_context (gcc_jit_object *obj)
+{
+  RETURN_NULL_IF_FAIL (obj, NULL, NULL, "NULL object");
+
+  return static_cast <gcc_jit_context *> (obj->get_context ());
+}
+
+const char *
+gcc_jit_object_get_debug_string (gcc_jit_object *obj)
+{
+  RETURN_NULL_IF_FAIL (obj, NULL, NULL, "NULL object");
+
+  return obj->get_debug_string ();
+}
+
+gcc_jit_lvalue *
+gcc_jit_lvalue_access_field (gcc_jit_lvalue *struct_,
+			     gcc_jit_location *loc,
+			     gcc_jit_field *field)
+{
+  RETURN_NULL_IF_FAIL (struct_, NULL, loc, "NULL struct");
+  gcc::jit::recording::context *ctxt = struct_->m_ctxt;
+  RETURN_NULL_IF_FAIL (field, ctxt, loc, "NULL field");
+  RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
+			       "field %s has not been placed in a struct",
+			       field->get_debug_string ());
+
+  return (gcc_jit_lvalue *)struct_->access_field (loc, field);
+}
+
+gcc_jit_rvalue *
+gcc_jit_rvalue_access_field (gcc_jit_rvalue *struct_,
+			     gcc_jit_location *loc,
+			     gcc_jit_field *field)
+{
+  RETURN_NULL_IF_FAIL (struct_, NULL, loc, "NULL struct");
+  gcc::jit::recording::context *ctxt = struct_->m_ctxt;
+  RETURN_NULL_IF_FAIL (field, ctxt, loc, "NULL field");
+  RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
+			       "field %s has not been placed in a struct",
+			       field->get_debug_string ());
+
+  return (gcc_jit_rvalue *)struct_->access_field (loc, field);
+}
+
+gcc_jit_lvalue *
+gcc_jit_rvalue_dereference_field (gcc_jit_rvalue *ptr,
+				  gcc_jit_location *loc,
+				  gcc_jit_field *field)
+{
+  RETURN_NULL_IF_FAIL (ptr, NULL, loc, "NULL ptr");
+  RETURN_NULL_IF_FAIL (field, NULL, loc, "NULL field");
+  gcc::jit::recording::type *underlying_type =
+    ptr->get_type ()->is_pointer ();
+  RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
+			       "field %s has not been placed in a struct",
+			       field->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF3 (
+    underlying_type,
+    ptr->m_ctxt, loc,
+    "dereference of non-pointer %s (type: %s) when accessing ->%s",
+    ptr->get_debug_string (),
+    ptr->get_type ()->get_debug_string (),
+    field->get_debug_string ());
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    (field->get_container ()->unqualified ()
+     == underlying_type->unqualified ()),
+    ptr->m_ctxt, loc,
+    "%s is not a field of %s",
+    field->get_debug_string (),
+    underlying_type->get_debug_string ());
+
+  return (gcc_jit_lvalue *)ptr->dereference_field (loc, field);
+}
+
+gcc_jit_lvalue *
+gcc_jit_rvalue_dereference (gcc_jit_rvalue *rvalue,
+			    gcc_jit_location *loc)
+{
+  RETURN_NULL_IF_FAIL (rvalue, NULL, loc, "NULL rvalue");
+
+  gcc::jit::recording::type *underlying_type =
+    rvalue->get_type ()->is_pointer ();
+
+  RETURN_NULL_IF_FAIL_PRINTF2 (
+    underlying_type,
+    rvalue->m_ctxt, loc,
+    "dereference of non-pointer %s (type: %s)",
+    rvalue->get_debug_string (),
+    rvalue->get_type ()->get_debug_string ());
+
+  return (gcc_jit_lvalue *)rvalue->dereference (loc);
+}
+
+gcc_jit_rvalue *
+gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
+			    gcc_jit_location *loc)
+{
+  RETURN_NULL_IF_FAIL (lvalue, NULL, loc, "NULL lvalue");
+
+  return (gcc_jit_rvalue *)lvalue->get_address (loc);
+}
+
+gcc_jit_lvalue *
+gcc_jit_function_new_local (gcc_jit_function *func,
+			    gcc_jit_location *loc,
+			    gcc_jit_type *type,
+			    const char *name)
+{
+  RETURN_NULL_IF_FAIL (func, NULL, loc, "NULL function");
+  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_NULL_IF_FAIL (func->get_kind () != GCC_JIT_FUNCTION_IMPORTED,
+		       ctxt, loc,
+		       "Cannot add locals to an imported function");
+  RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
+  RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
+
+  return (gcc_jit_lvalue *)func->new_local (loc, type, name);
+}
+
+void
+gcc_jit_block_add_eval (gcc_jit_block *block,
+			gcc_jit_location *loc,
+			gcc_jit_rvalue *rvalue)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+
+  return block->add_eval (loc, rvalue);
+}
+
+void
+gcc_jit_block_add_assignment (gcc_jit_block *block,
+			      gcc_jit_location *loc,
+			      gcc_jit_lvalue *lvalue,
+			      gcc_jit_rvalue *rvalue)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (lvalue, ctxt, loc, "NULL lvalue");
+  RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+  RETURN_IF_FAIL_PRINTF4 (
+    compatible_types (lvalue->get_type (),
+		      rvalue->get_type ()),
+    ctxt, loc,
+    "mismatching types:"
+    " assignment to %s (type: %s) from %s (type: %s)",
+    lvalue->get_debug_string (),
+    lvalue->get_type ()->get_debug_string (),
+    rvalue->get_debug_string (),
+    rvalue->get_type ()->get_debug_string ());
+
+  return block->add_assignment (loc, lvalue, rvalue);
+}
+
+void
+gcc_jit_block_add_assignment_op (gcc_jit_block *block,
+				 gcc_jit_location *loc,
+				 gcc_jit_lvalue *lvalue,
+				 enum gcc_jit_binary_op op,
+				 gcc_jit_rvalue *rvalue)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (lvalue, ctxt, loc, "NULL lvalue");
+  /* FIXME: op is checked by new_binary_op */
+  RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+
+  return block->add_assignment_op (loc, lvalue, op, rvalue);
+}
+
+static bool
+is_bool (gcc_jit_rvalue *boolval)
+{
+  gcc::jit::recording::type *actual_type = boolval->get_type ();
+  gcc::jit::recording::type *bool_type =
+    boolval->m_ctxt->get_type (GCC_JIT_TYPE_BOOL);
+  return actual_type == bool_type;
+}
+
+void
+gcc_jit_block_end_with_conditional (gcc_jit_block *block,
+				    gcc_jit_location *loc,
+				    gcc_jit_rvalue *boolval,
+				    gcc_jit_block *on_true,
+				    gcc_jit_block *on_false)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (boolval, ctxt, loc, "NULL boolval");
+  RETURN_IF_FAIL_PRINTF2 (
+   is_bool (boolval), ctxt, loc,
+   "%s (type: %s) is not of boolean type ",
+   boolval->get_debug_string (),
+   boolval->get_type ()->get_debug_string ());
+  RETURN_IF_FAIL (on_true, ctxt, loc, "NULL on_true");
+  RETURN_IF_FAIL (on_true, ctxt, loc, "NULL on_false");
+  RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == on_true->get_function (),
+    ctxt, loc,
+    "\"on_true\" block is not in same function:"
+    " source block %s is in function %s"
+    " whereas target block %s is in function %s",
+    block->get_debug_string (),
+    block->get_function ()->get_debug_string (),
+    on_true->get_debug_string (),
+    on_true->get_function ()->get_debug_string ());
+  RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == on_false->get_function (),
+    ctxt, loc,
+    "\"on_false\" block is not in same function:"
+    " source block %s is in function %s"
+    " whereas target block %s is in function %s",
+    block->get_debug_string (),
+    block->get_function ()->get_debug_string (),
+    on_false->get_debug_string (),
+    on_false->get_function ()->get_debug_string ());
+
+  return block->end_with_conditional (loc, boolval, on_true, on_false);
+}
+
+void
+gcc_jit_block_add_comment (gcc_jit_block *block,
+			   gcc_jit_location *loc,
+			   const char *text)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (text, ctxt, loc, "NULL text");
+
+  block->add_comment (loc, text);
+}
+
+void
+gcc_jit_block_end_with_jump (gcc_jit_block *block,
+			     gcc_jit_location *loc,
+			     gcc_jit_block *target)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  RETURN_IF_FAIL (target, ctxt, loc, "NULL target");
+  RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == target->get_function (),
+    ctxt, loc,
+    "target block is not in same function:"
+    " source block %s is in function %s"
+    " whereas target block %s is in function %s",
+    block->get_debug_string (),
+    block->get_function ()->get_debug_string (),
+    target->get_debug_string (),
+    target->get_function ()->get_debug_string ());
+
+  block->end_with_jump (loc, target);
+}
+
+void
+gcc_jit_block_end_with_return (gcc_jit_block *block,
+			       gcc_jit_location *loc,
+			       gcc_jit_rvalue *rvalue)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  gcc::jit::recording::function *func = block->get_function ();
+  RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
+  RETURN_IF_FAIL_PRINTF4 (
+    compatible_types (
+      func->get_return_type (),
+      rvalue->get_type ()),
+    ctxt, loc,
+    "mismatching types:"
+    " return of %s (type: %s) in function %s (return type: %s)",
+    rvalue->get_debug_string (),
+    rvalue->get_type ()->get_debug_string (),
+    func->get_debug_string (),
+    func->get_return_type ()->get_debug_string ());
+
+  return block->end_with_return (loc, rvalue);
+}
+
+void
+gcc_jit_block_end_with_void_return (gcc_jit_block *block,
+				    gcc_jit_location *loc)
+{
+  RETURN_IF_NOT_VALID_BLOCK (block, loc);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  gcc::jit::recording::function *func = block->get_function ();
+  RETURN_IF_FAIL_PRINTF2 (
+    func->get_return_type () == ctxt->get_type (GCC_JIT_TYPE_VOID),
+    ctxt, loc,
+    "mismatching types:"
+    " void return in function %s (return type: %s)",
+    func->get_debug_string (),
+    func->get_return_type ()->get_debug_string ());
+
+  return block->end_with_return (loc, NULL);
+}
+
+/**********************************************************************
+ Option-management
+ **********************************************************************/
+
+void
+gcc_jit_context_set_str_option (gcc_jit_context *ctxt,
+				enum gcc_jit_str_option opt,
+				const char *value)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  /* opt is checked by the inner function.
+     value can be NULL.  */
+
+  ctxt->set_str_option (opt, value);
+}
+
+void
+gcc_jit_context_set_int_option (gcc_jit_context *ctxt,
+				enum gcc_jit_int_option opt,
+				int value)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  /* opt is checked by the inner function.  */
+
+  ctxt->set_int_option (opt, value);
+}
+
+void
+gcc_jit_context_set_bool_option (gcc_jit_context *ctxt,
+				 enum gcc_jit_bool_option opt,
+				 int value)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  /* opt is checked by the inner function.  */
+
+  ctxt->set_bool_option (opt, value);
+}
+
+gcc_jit_result *
+gcc_jit_context_compile (gcc_jit_context *ctxt)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+
+  return (gcc_jit_result *)ctxt->compile ();
+}
+
+void
+gcc_jit_context_dump_to_file (gcc_jit_context *ctxt,
+			      const char *path,
+			      int update_locations)
+{
+  RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+  RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
+  ctxt->dump_to_file (path, update_locations);
+}
+
+const char *
+gcc_jit_context_get_first_error (gcc_jit_context *ctxt)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+
+  return ctxt->get_first_error ();
+}
+
+void *
+gcc_jit_result_get_code (gcc_jit_result *result,
+			 const char *fnname)
+{
+  RETURN_NULL_IF_FAIL (result, NULL, NULL, "NULL result");
+  RETURN_NULL_IF_FAIL (fnname, NULL, NULL, "NULL fnname");
+
+  return result->get_code (fnname);
+}
+
+void
+gcc_jit_result_release (gcc_jit_result *result)
+{
+  RETURN_IF_FAIL (result, NULL, NULL, "NULL result");
+
+  delete result;
+}