diff mbox

[jit] Major API change: blocks rather than labels

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

Commit Message

David Malcolm Feb. 27, 2014, 10:11 p.m. UTC
Committed to branch dmalcolm/jit:

In the old API, statements were added to functions, with an implicit
insertion position within each function.  Control flow was expressed by
creating and placing labels.

With this commit, the API changes to using basic blocks instead: blocks
are created within functions, and statements are added to blocks, rather
than to functions.

The old API worked, but I found it to be error-prone: in my ongoing
attempts to port GNU Octave's JIT compiler to libgccjit, long debugging
sessions indicated places where I'd created functions incorrectly, leading
to bogus code being created.  The block-based API can detect such mistakes
at JIT-compile time, failing fast with a clear error message for:
  * unterminated blocks (which will catch some kinds of missing return value)
  * missing return value (a function with a non-void return with no blocks)
  * unreachable blocks
  * adding something to a terminated block
  * jump to a block in another function

This commit also eliminates the loop part of the API: I haven't seen it
used in real code, only in testcases - the C++ wrapper API didn't support
it, and I hadn't noticed that it didn't.  I ported the three testcases
that used the loop API to instead directly work with blocks, and the result
seems clearer.

gcc/jit/
	* libgccjit.h (gcc_jit_label): Delete in favor of...
	(gcc_jit_block): New type.
	(gcc_jit_loop): Delete.

	(gcc_jit_function_new_forward_label): Delete in favor of...
	(gcc_jit_function_new_block): New.

	(gcc_jit_label_as_object): Delete in favor of...
	(gcc_jit_block_as_object): New.

	(gcc_jit_function_add_eval): Delete in favor of...
	(gcc_jit_block_add_eval): New.

	(gcc_jit_function_add_assignment): Delete in favor of...
	(gcc_jit_block_add_assignment): New.

	(gcc_jit_function_add_assignment_op): Delete in favor of...
	(gcc_jit_block_add_assignment_op): New.

	(gcc_jit_function_add_comment): Delete in favor of...
	(gcc_jit_block_add_comment): New.

	(gcc_jit_label_as_object): Delete in favor of...
	(gcc_jit_block_as_object): New.

	(gcc_jit_function_add_conditional): Delete in favor of...
	(gcc_jit_block_end_with_conditional): New.

	(gcc_jit_function_add_jump): Delete in favor of...
	(gcc_jit_block_end_with_jump): New.

	(gcc_jit_function_add_return): Delete in favor of...
	(gcc_jit_block_end_with_return): New.

	(gcc_jit_function_add_void_return): Delete in favor of...
	(gcc_jit_block_end_with_void_return): New.

	(gcc_jit_function_new_loop): Delete.
	(gcc_jit_function_new_loop_over_range): Delete.
	(gcc_jit_loop_as_object): Delete.
	(gcc_jit_loop_end): Delete.

	* libgccjit.map (gcc_jit_function_add_assignment): Delete in favor of...
	(gcc_jit_block_add_assignment): New.

	(gcc_jit_function_add_assignment_op): Delete in favor of...
	(gcc_jit_block_add_assignment_op): New.

	(gcc_jit_function_add_comment): Delete in favor of...
	(gcc_jit_block_add_comment): New.

	(gcc_jit_function_add_eval): Delete in favor of...
	(gcc_jit_block_add_eval): New.

	(gcc_jit_label_as_object): Delete in favor of...
	(gcc_jit_block_as_object): New.

	(gcc_jit_function_add_conditional): Delete in favor of...
	(gcc_jit_block_end_with_conditional): New.

	(gcc_jit_function_add_jump): Delete in favor of...
	(gcc_jit_block_end_with_jump): New.

	(gcc_jit_function_add_return): Delete in favor of...
	(gcc_jit_block_end_with_return): New.

	(gcc_jit_function_add_void_return): Delete in favor of...
	(gcc_jit_block_end_with_void_return): New.

	(gcc_jit_function_add_label): Delete in favor of...
	(gcc_jit_function_new_block): New.
	(gcc_jit_function_new_forward_label): Delete.
	(gcc_jit_function_place_forward_label): Delete.

	(gcc_jit_function_new_loop): Delete.
	(gcc_jit_function_new_loop_over_range): Delete.
	(gcc_jit_loop_as_object): Delete.
	(gcc_jit_loop_end): Delete.

	* libgccjit.c (gcc_jit_label): Delete in favor of...
	(gcc_jit_block): New type.
	(gcc_jit_loop): Delete.

	(RETURN_IF_NOT_FUNC_DEFINITION): Delete in favor of...
	(RETURN_IF_NOT_VALID_BLOCK): New macro.
	(RETURN_NULL_IF_NOT_FUNC_DEFINITION): Delete in favor of...
	(RETURN_NULL_IF_NOT_VALID_BLOCK): New macro.

	(gcc_jit_function_new_forward_label): Delete in favor of...
	(gcc_jit_function_new_block): New.

	(gcc_jit_label_as_object): Delete in favor of...
	(gcc_jit_block_as_object): New.

	(gcc_jit_rvalue_dereference_field): Ensure that field has been
	placed.

	(gcc_jit_function_add_label): Delete
	(gcc_jit_function_place_forward_label): Delete.

	(gcc_jit_function_add_eval): Delete in favor of...
	(gcc_jit_block_add_eval): New.

	(gcc_jit_function_add_assignment): Delete in favor of...
	(gcc_jit_block_add_assignment): New.

	(gcc_jit_function_add_assignment_op): Delete in favor of...
	(gcc_jit_block_add_assignment_op): New.

	(gcc_jit_function_add_conditional): Delete in favor of...
	(gcc_jit_block_end_with_conditional): New.

	(gcc_jit_function_add_comment): Delete in favor of...
	(gcc_jit_block_add_comment): New.

	(gcc_jit_function_add_jump): Delete in favor of...
	(gcc_jit_block_end_with_jump): New.

	(gcc_jit_function_add_return): Delete in favor of...
	(gcc_jit_block_end_with_return): New.

	(gcc_jit_function_add_void_return): Delete in favor of...
	(gcc_jit_block_end_with_void_return): New.

	(gcc_jit_function_new_loop): Delete.
	(gcc_jit_function_new_loop_over_range): Delete.
	(gcc_jit_loop_as_object): Delete.
	(gcc_jit_loop_end): Delete.

	* internal-api.h (gcc::jit::recording::label): Delete class in
	favor of...
	(gcc::jit::recording::block): New class.
	(gcc::jit::recording::loop): Delete class.
	(gcc::jit::recording::loop_end): Delete class.

	(gcc::jit::playback::label): Delete class in favor of...
	(gcc::jit::playback::block): New class.

	(gcc::jit::playback::loop): Delete class.

	(gcc::jit::recording::playback_label): Delete function in favor of...
	(gcc::jit::recording::playback_block): New function.

	(gcc::jit::recording::context::validate): New.

	(gcc::jit::recording::function::new_forward_label): Delete method.
	(gcc::jit::recording::function::add_eval): Delete method in favor
	of method of new gcc::jit::recording::block class.
	(gcc::jit::recording::function::add_assignment): Likewise.
	(gcc::jit::recording::function::add_assignment_op): Likewise.
	(gcc::jit::recording::function::add_comment): Likewise.
	(gcc::jit::recording::function::add_conditional): Likewise.
	(gcc::jit::recording::function::place_forward_label): Likewise.
	(gcc::jit::recording::function::add_jump): Likewise.
	(gcc::jit::recording::function::add_return): Likewise.

	(gcc::jit::recording::function::add_label): Delete method in favor of...
	(gcc::jit::recording::function::new_block): New method.

	(gcc::jit::recording::function::new_loop): Delete method.

	(gcc::jit::recording::function::validate): New method.
	(gcc::jit::recording::function::m_activity): Delete field in favor of...
	(gcc::jit::recording::function::m_blocks): New field.

	(gcc::jit::recording::statement::get_successor_blocks): New method.
	(gcc::jit::recording::statement::write_to_dump): Make public.
	(gcc::jit::recording::statement::statement): Accept a block rather
	than a function.
	(gcc::jit::recording::statement::playback_function): Delete.
	(gcc::jit::recording::statement::get_block): New.
	(gcc::jit::recording::statement::m_func): Delete in favor of...
	(gcc::jit::recording::statement::m_block): ...this.

	(gcc::jit::recording::eval::eval): Accept a block rather than a
	function.
	(gcc::jit::recording::assignment::assignment): Likewise.
	(gcc::jit::recording::assignment_op::assignment_op): Likewise.
	(gcc::jit::recording::comment::comment): Likewise.
	(gcc::jit::recording::return::return): Likewise.
	(gcc::jit::recording::conditional::conditional): Likewise; accept
	blocks rather than labels.
	(gcc::jit::recording::jump::jump): Likewise.
	(gcc::jit::recording::conditional::get_successor_blocks): New.
	(gcc::jit::recording::jump::get_successor_blocks): New.

	(gcc::jit::playback::function::new_forward_label): Delete method
	in favor of...
	(gcc::jit::playback::function::new_block): New method.
	(gcc::jit::playback::function::build_stmt_list): New method.
	(gcc::jit::playback::function::m_blocks): New field.

	* libgccjit++.h (gccjit::label): Delete class in favor of...
	(gccjit::block): New class.
	(gccjit::function::new_forward_label): Delete methods in favor of...
	(gccjit::function::new_block): New methods.
	(gccjit::function::add_comment): Delete methods in favor of methods
	of new class gccjit::block.
	(gccjit::function::add_conditional): Likewise.
	(gccjit::function::add_label): Likewise.
	(gccjit::function::place_forward_label): Likewise.
	(gccjit::function::add_jump): Likewise.
	(gccjit::function::add_return): Likewise.
	(gccjit::function::add_call): Likewise.

	* internal-api.c (gcc::jit::recording::playback_label): Delete in
	favor of...
	(gcc::jit::recording::playback_block): New.
	(gcc::jit::recording::context::compile): Validate.
	(gcc::jit::recording::context::validate): New.
	(gcc::jit::recording::function::function): Update.
	(gcc::jit::recording::function::new_forward_label): Delete.
	(gcc::jit::recording::function::add_eval): Delete.
	(gcc::jit::recording::function::add_assignment): Delete.
	(gcc::jit::recording::function::add_assignment_op): Delete.
	(gcc::jit::recording::function::new_block): New.
	(gcc::jit::recording::function::add_comment): Delete.
	(gcc::jit::ecording::function::add_conditional): Delete.
	(gcc::jit::recording::function::add_label): Delete.
	(gcc::jit::recording::function::place_forward_label): Delete.
	(gcc::jit::recording::function::add_jump): Delete.
	(gcc::jit::recording::function::add_return): Delete.
	(gcc::jit::recording::function::new_loop): Delete.
	(gcc::jit::recording::function::write_to_dump): Port to block-based
	representation.
	(gcc::jit::recording::function::validate): New.
	(gcc::jit::recording::block::add_eval): New.
	(gcc::jit::recording::block::add_assignment): New.
	(gcc::jit::recording::label::replay_into): Delete.
	(gcc::jit::recording::block::add_assignment_op): New.
	(gcc::jit::recording::block::add_comment): New.
	(gcc::jit::recording::block::end_with_conditional): New.
	(gcc::jit::recording::block::end_with_jump): New.
	(gcc::jit::recording::block::end_with_return): New.
	(gcc::jit::recording::block::write_to_dump): New.
	(gcc::jit::recording::block::validate): New.
	(gcc::jit::recording::block::get_last_statement): New.
	(gcc::jit::recording::block::get_successor_blocks): New.
	(gcc::jit::recording::block::replay_into): New.
	(gcc::jit::recording::label::make_debug_string): Delete.
	(gcc::jit::recording::block::make_debug_string): New.
	(gcc::jit::recording::statement::get_successor_blocks): New.
	(gcc::jit::recording::eval::replay_into): Port to block-based
	representation.
	(gcc::jit::recording::assignment::replay_into): Likewise.
	(gcc::jit::recording::assignment_op::replay_into): Likewise.
	(gcc::jit::recording::comment::replay_into): Likewise.
	(gcc::jit::recording::conditional::replay_into): Likewise.
	(gcc::jit::recording::jump::replay_into): Likewise.
	(gcc::jit::recording::return_::replay_into): Likewise.

	(gcc::jit::recording::conditional::get_successor_blocks): New.
	(gcc::jit::recording::place_label::place_label): Delete.
	(gcc::jit::recording::place_label::replay_into): Delete.
	(gcc::jit::recording::place_label::make_debug_string): Delete.
	(gcc::jit::recording::place_label::write_to_dump): Delete.

	(gcc::jit::recording::jump::get_successor_blocks): New.
	(gcc::jit::recording::return_::get_successor_blocks): New.

	(gcc::jit::recording::loop::replay_into): Delete.
	(gcc::jit::recording::loop::make_debug_string): Delete.
	(gcc::jit::recording::loop::end): Delete.
	(gcc::jit::recording::loop_end::replay_into): Delete.
	(gcc::jit::recording::loop_end::make_debug_string): Delete.

	(gcc::jit::playback::function::new_forward_label): Delete.
	(gcc::jit::playback::function::new_block): New.
	(gcc::jit::playback::function::build_stmt_list): New.
	(gcc::jit::playback::function::add_eval): Replace with...
	(gcc::jit::playback::block::add_eval): New.
	(gcc::jit::playback::function::add_assignment): Replace with...
	(gcc::jit::playback::block::add_assignment): New.
	(gcc::jit::playback::function::add_comment): Replace with...
	(gcc::jit::playback::block::add_comment): New, reimplementing,
	given that we no longer have labels.
	(gcc::jit::playback::function::add_conditional): Replace with...
	(gcc::jit::playback::block::add_conditional): New, reworking,
	given that on_false can no longer be NULL.
	(gcc::jit::playback::function::add_label): Delete.
	(gcc::jit::playback::function::place_forward_label): Delete.
	(gcc::jit::playback::function::add_jump): Replace with...
	(gcc::jit::playback::block::add_jump): New.
	(gcc::jit::playback::function::add_return): Replace with...
	(gcc::jit::playback::block::add_return): New.
	(gcc::jit::playback::function::new_loop): Delete.
	(gcc::jit::playback::label::label): Replace with...
	(gcc::jit::playback::block::block): ...this.

	(gcc::jit::playback::loop::loop): Delete.
	(gcc::jit::playback::loop::end): Delete.

	(gcc::jit::playback::context::replay): Call each function's
	build_stmt_list.

	* TODO.rst: Update

gcc/testsuite/
	* jit.dg/test-accessing-struct.c (create_code): Port to
	block-based API.
	* jit.dg/test-calling-external-function.c (create_code): Likewise.
	* jit.dg/test-error-accessing-field-in-other-struct.c (create_code):
	Likewise.
	* jit.dg/test-error-call-with-mismatching-args.c (create_code):
	Likewise.
	* jit.dg/test-error-call-with-not-enough-args.c (create_code):
	Likewise.
	* jit.dg/test-error-call-with-too-many-args.c (create_code):
	Likewise.
	* jit.dg/test-error-dereference-field-of-non-pointer.c (create_code):
	Likewise.
	* jit.dg/test-error-dereference-read: Likewise.
	* jit.dg/test-error-mismatching-types-in-assignment.c: Likewise.
	* jit.dg/test-error-return-within-void-function.c: Likewise.
	* jit.dg/test-expressions.c: Likewise.
	* jit.dg/test-factorial.c: Likewise.
	* jit.dg/test-functions.c: Likewise.
	* jit.dg/test-fuzzer.c: Likewise.
	* jit.dg/test-hello-world.c (create_code): Likewise.
	* jit.dg/test-nested-contexts.c: Likewise.
	* jit.dg/test-operator-overloading.cc: Likewise.
	* jit.dg/test-quadratic.c: Likewise.
	* jit.dg/test-quadratic.cc: Likewise.
	* jit.dg/test-reading-struct.c (create_code): Likewise.
	* jit.dg/test-string-literal.c (create_code): Likewise.
	* jit.dg/test-sum-of-squares.c (create_code): Likewise.
	* jit.dg/test-types.c (create_code): Likewise.
	* jit.dg/test-using-global.c (create_code): Likewise.

	* jit.dg/test-arrays.c (create_code): Likewise, eliminating use of
	loop API.
	* jit.dg/test-dot-product.c (create_code): Likewise.
	* jit.dg/test-linked-list.c (create_code): Likewise.

	* jit.dg/test-error-adding-to-terminated-block.c: New testcase.
	* jit.dg/test-error-block-in-wrong-function.c: Likewise.
	* jit.dg/test-error-missing-return.c: Likewise.
	* jit.dg/test-error-unreachable-block.c: Likewise.
	* jit.dg/test-error-unterminated-block.c: Likewise.

	* jit.dg/test-error-label-already-placed.c: Delete obsolete testcase.
	* jit.dg/test-error-unplaced-label.c: Likewise.
---
 gcc/jit/ChangeLog.jit                              | 299 ++++++++++
 gcc/jit/TODO.rst                                   |  13 +-
 gcc/jit/internal-api.c                             | 636 +++++++++++----------
 gcc/jit/internal-api.h                             | 399 ++++++-------
 gcc/jit/libgccjit++.h                              | 248 ++++----
 gcc/jit/libgccjit.c                                | 289 ++++------
 gcc/jit/libgccjit.h                                | 190 ++----
 gcc/jit/libgccjit.map                              |  26 +-
 gcc/testsuite/ChangeLog.jit                        |  47 ++
 gcc/testsuite/jit.dg/test-accessing-struct.c       |   6 +-
 gcc/testsuite/jit.dg/test-arrays.c                 |  53 +-
 .../jit.dg/test-calling-external-function.c        |   7 +-
 gcc/testsuite/jit.dg/test-dot-product.c            |  38 +-
 .../test-error-accessing-field-in-other-struct.c   |  12 +-
 .../jit.dg/test-error-adding-to-terminated-block.c |  48 ++
 .../jit.dg/test-error-block-in-wrong-function.c    |  65 +++
 .../jit.dg/test-error-call-with-mismatching-args.c |   6 +-
 .../jit.dg/test-error-call-with-not-enough-args.c  |   7 +-
 .../jit.dg/test-error-call-with-too-many-args.c    |   7 +-
 .../test-error-dereference-field-of-non-pointer.c  |   8 +-
 .../test-error-dereference-read-of-non-pointer.c   |   5 +-
 .../jit.dg/test-error-label-already-placed.c       |  59 --
 .../test-error-mismatching-types-in-assignment.c   |   9 +-
 gcc/testsuite/jit.dg/test-error-missing-return.c   |  40 ++
 .../test-error-return-within-void-function.c       |   8 +-
 gcc/testsuite/jit.dg/test-error-unplaced-label.c   |  50 --
 .../jit.dg/test-error-unreachable-block.c          |  50 ++
 .../jit.dg/test-error-unterminated-block.c         |  42 ++
 gcc/testsuite/jit.dg/test-expressions.c            |  28 +-
 gcc/testsuite/jit.dg/test-factorial.c              |  30 +-
 gcc/testsuite/jit.dg/test-fibonacci.c              |  38 +-
 gcc/testsuite/jit.dg/test-functions.c              |  25 +-
 gcc/testsuite/jit.dg/test-fuzzer.c                 |  20 +-
 gcc/testsuite/jit.dg/test-hello-world.c            |  11 +-
 gcc/testsuite/jit.dg/test-linked-list.c            |  38 +-
 gcc/testsuite/jit.dg/test-nested-contexts.c        | 147 ++---
 gcc/testsuite/jit.dg/test-operator-overloading.cc  |  84 +--
 gcc/testsuite/jit.dg/test-quadratic.c              | 152 ++---
 gcc/testsuite/jit.dg/test-quadratic.cc             |  87 +--
 gcc/testsuite/jit.dg/test-reading-struct.c         |  18 +-
 gcc/testsuite/jit.dg/test-string-literal.c         |   5 +-
 gcc/testsuite/jit.dg/test-sum-of-squares.c         |  52 +-
 gcc/testsuite/jit.dg/test-types.c                  |   7 +-
 gcc/testsuite/jit.dg/test-using-global.c           |   6 +-
 44 files changed, 1884 insertions(+), 1531 deletions(-)
 create mode 100644 gcc/testsuite/jit.dg/test-error-adding-to-terminated-block.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
 delete mode 100644 gcc/testsuite/jit.dg/test-error-label-already-placed.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-missing-return.c
 delete mode 100644 gcc/testsuite/jit.dg/test-error-unplaced-label.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-unreachable-block.c
 create mode 100644 gcc/testsuite/jit.dg/test-error-unterminated-block.c

Comments

David Malcolm Feb. 27, 2014, 10:25 p.m. UTC | #1
On Thu, 2014-02-27 at 17:11 -0500, David Malcolm wrote:

[...]

> With this commit, the API changes to using basic blocks instead: blocks
> are created within functions, and statements are added to blocks, rather
> than to functions.

[...]

I've also ported the "jittest" example to the new API, as of this
commit:
https://github.com/davidmalcolm/jittest/commit/af66efe0386e52a9292b7527174ae402c0af5e43

(though currently it falls foul of type-checking, due to int vs bool
issues in conditionals; upon hacking out the type-checking from
libgccjit it compiles and runs OK).

I haven't yet ported the Python bindings.

Dave
David Malcolm Feb. 28, 2014, 9:34 p.m. UTC | #2
On Thu, 2014-02-27 at 17:25 -0500, David Malcolm wrote:
> On Thu, 2014-02-27 at 17:11 -0500, David Malcolm wrote:
> 
> [...]
> 
> > With this commit, the API changes to using basic blocks instead: blocks
> > are created within functions, and statements are added to blocks, rather
> > than to functions.
> 
> [...]
> 
> I've also ported the "jittest" example to the new API, as of this
> commit:
> https://github.com/davidmalcolm/jittest/commit/af66efe0386e52a9292b7527174ae402c0af5e43
> 
> (though currently it falls foul of type-checking, due to int vs bool
> issues in conditionals; upon hacking out the type-checking from
> libgccjit it compiles and runs OK).

jittest is now fixed, as of:
https://github.com/davidmalcolm/jittest/commit/7af0765c018e15d600016d41f7b444273cc0389a
diff mbox

Patch

diff --git a/gcc/jit/ChangeLog.jit b/gcc/jit/ChangeLog.jit
index de352fa..c7b2395 100644
--- a/gcc/jit/ChangeLog.jit
+++ b/gcc/jit/ChangeLog.jit
@@ -1,3 +1,302 @@ 
+2014-02-27  David Malcolm  <dmalcolm@redhat.com>
+
+	* libgccjit.h (gcc_jit_label): Delete in favor of...
+	(gcc_jit_block): New type.
+	(gcc_jit_loop): Delete.
+
+	(gcc_jit_function_new_forward_label): Delete in favor of...
+	(gcc_jit_function_new_block): New.
+
+	(gcc_jit_label_as_object): Delete in favor of...
+	(gcc_jit_block_as_object): New.
+
+	(gcc_jit_function_add_eval): Delete in favor of...
+	(gcc_jit_block_add_eval): New.
+
+	(gcc_jit_function_add_assignment): Delete in favor of...
+	(gcc_jit_block_add_assignment): New.
+
+	(gcc_jit_function_add_assignment_op): Delete in favor of...
+	(gcc_jit_block_add_assignment_op): New.
+
+	(gcc_jit_function_add_comment): Delete in favor of...
+	(gcc_jit_block_add_comment): New.
+
+	(gcc_jit_label_as_object): Delete in favor of...
+	(gcc_jit_block_as_object): New.
+
+	(gcc_jit_function_add_conditional): Delete in favor of...
+	(gcc_jit_block_end_with_conditional): New.
+
+	(gcc_jit_function_add_jump): Delete in favor of...
+	(gcc_jit_block_end_with_jump): New.
+
+	(gcc_jit_function_add_return): Delete in favor of...
+	(gcc_jit_block_end_with_return): New.
+
+	(gcc_jit_function_add_void_return): Delete in favor of...
+	(gcc_jit_block_end_with_void_return): New.
+
+	(gcc_jit_function_new_loop): Delete.
+	(gcc_jit_function_new_loop_over_range): Delete.
+	(gcc_jit_loop_as_object): Delete.
+	(gcc_jit_loop_end): Delete.
+
+	* libgccjit.map (gcc_jit_function_add_assignment): Delete in favor of...
+	(gcc_jit_block_add_assignment): New.
+
+	(gcc_jit_function_add_assignment_op): Delete in favor of...
+	(gcc_jit_block_add_assignment_op): New.
+
+	(gcc_jit_function_add_comment): Delete in favor of...
+	(gcc_jit_block_add_comment): New.
+
+	(gcc_jit_function_add_eval): Delete in favor of...
+	(gcc_jit_block_add_eval): New.
+
+	(gcc_jit_label_as_object): Delete in favor of...
+	(gcc_jit_block_as_object): New.
+
+	(gcc_jit_function_add_conditional): Delete in favor of...
+	(gcc_jit_block_end_with_conditional): New.
+
+	(gcc_jit_function_add_jump): Delete in favor of...
+	(gcc_jit_block_end_with_jump): New.
+
+	(gcc_jit_function_add_return): Delete in favor of...
+	(gcc_jit_block_end_with_return): New.
+
+	(gcc_jit_function_add_void_return): Delete in favor of...
+	(gcc_jit_block_end_with_void_return): New.
+
+	(gcc_jit_function_add_label): Delete in favor of...
+	(gcc_jit_function_new_block): New.
+	(gcc_jit_function_new_forward_label): Delete.
+	(gcc_jit_function_place_forward_label): Delete.
+
+	(gcc_jit_function_new_loop): Delete.
+	(gcc_jit_function_new_loop_over_range): Delete.
+	(gcc_jit_loop_as_object): Delete.
+	(gcc_jit_loop_end): Delete.
+
+	* libgccjit.c (gcc_jit_label): Delete in favor of...
+	(gcc_jit_block): New type.
+	(gcc_jit_loop): Delete.
+
+	(RETURN_IF_NOT_FUNC_DEFINITION): Delete in favor of...
+	(RETURN_IF_NOT_VALID_BLOCK): New macro.
+	(RETURN_NULL_IF_NOT_FUNC_DEFINITION): Delete in favor of...
+	(RETURN_NULL_IF_NOT_VALID_BLOCK): New macro.
+
+	(gcc_jit_function_new_forward_label): Delete in favor of...
+	(gcc_jit_function_new_block): New.
+
+	(gcc_jit_label_as_object): Delete in favor of...
+	(gcc_jit_block_as_object): New.
+
+	(gcc_jit_rvalue_dereference_field): Ensure that field has been
+	placed.
+
+	(gcc_jit_function_add_label): Delete
+	(gcc_jit_function_place_forward_label): Delete.
+
+	(gcc_jit_function_add_eval): Delete in favor of...
+	(gcc_jit_block_add_eval): New.
+
+	(gcc_jit_function_add_assignment): Delete in favor of...
+	(gcc_jit_block_add_assignment): New.
+
+	(gcc_jit_function_add_assignment_op): Delete in favor of...
+	(gcc_jit_block_add_assignment_op): New.
+
+	(gcc_jit_function_add_conditional): Delete in favor of...
+	(gcc_jit_block_end_with_conditional): New.
+
+	(gcc_jit_function_add_comment): Delete in favor of...
+	(gcc_jit_block_add_comment): New.
+
+	(gcc_jit_function_add_jump): Delete in favor of...
+	(gcc_jit_block_end_with_jump): New.
+
+	(gcc_jit_function_add_return): Delete in favor of...
+	(gcc_jit_block_end_with_return): New.
+
+	(gcc_jit_function_add_void_return): Delete in favor of...
+	(gcc_jit_block_end_with_void_return): New.
+
+	(gcc_jit_function_new_loop): Delete.
+	(gcc_jit_function_new_loop_over_range): Delete.
+	(gcc_jit_loop_as_object): Delete.
+	(gcc_jit_loop_end): Delete.
+
+	* internal-api.h (gcc::jit::recording::label): Delete class in
+	favor of...
+	(gcc::jit::recording::block): New class.
+	(gcc::jit::recording::loop): Delete class.
+	(gcc::jit::recording::loop_end): Delete class.
+
+	(gcc::jit::playback::label): Delete class in favor of...
+	(gcc::jit::playback::block): New class.
+
+	(gcc::jit::playback::loop): Delete class.
+
+	(gcc::jit::recording::playback_label): Delete function in favor of...
+	(gcc::jit::recording::playback_block): New function.
+
+	(gcc::jit::recording::context::validate): New.
+
+	(gcc::jit::recording::function::new_forward_label): Delete method.
+	(gcc::jit::recording::function::add_eval): Delete method in favor
+	of method of new gcc::jit::recording::block class.
+	(gcc::jit::recording::function::add_assignment): Likewise.
+	(gcc::jit::recording::function::add_assignment_op): Likewise.
+	(gcc::jit::recording::function::add_comment): Likewise.
+	(gcc::jit::recording::function::add_conditional): Likewise.
+	(gcc::jit::recording::function::place_forward_label): Likewise.
+	(gcc::jit::recording::function::add_jump): Likewise.
+	(gcc::jit::recording::function::add_return): Likewise.
+
+	(gcc::jit::recording::function::add_label): Delete method in favor of...
+	(gcc::jit::recording::function::new_block): New method.
+
+	(gcc::jit::recording::function::new_loop): Delete method.
+
+	(gcc::jit::recording::function::validate): New method.
+	(gcc::jit::recording::function::m_activity): Delete field in favor of...
+	(gcc::jit::recording::function::m_blocks): New field.
+
+	(gcc::jit::recording::statement::get_successor_blocks): New method.
+	(gcc::jit::recording::statement::write_to_dump): Make public.
+	(gcc::jit::recording::statement::statement): Accept a block rather
+	than a function.
+	(gcc::jit::recording::statement::playback_function): Delete.
+	(gcc::jit::recording::statement::get_block): New.
+	(gcc::jit::recording::statement::m_func): Delete in favor of...
+	(gcc::jit::recording::statement::m_block): ...this.
+
+	(gcc::jit::recording::eval::eval): Accept a block rather than a
+	function.
+	(gcc::jit::recording::assignment::assignment): Likewise.
+	(gcc::jit::recording::assignment_op::assignment_op): Likewise.
+	(gcc::jit::recording::comment::comment): Likewise.
+	(gcc::jit::recording::return::return): Likewise.
+	(gcc::jit::recording::conditional::conditional): Likewise; accept
+	blocks rather than labels.
+	(gcc::jit::recording::jump::jump): Likewise.
+	(gcc::jit::recording::conditional::get_successor_blocks): New.
+	(gcc::jit::recording::jump::get_successor_blocks): New.
+
+	(gcc::jit::playback::function::new_forward_label): Delete method
+	in favor of...
+	(gcc::jit::playback::function::new_block): New method.
+	(gcc::jit::playback::function::build_stmt_list): New method.
+	(gcc::jit::playback::function::m_blocks): New field.
+
+	* libgccjit++.h (gccjit::label): Delete class in favor of...
+	(gccjit::block): New class.
+	(gccjit::function::new_forward_label): Delete methods in favor of...
+	(gccjit::function::new_block): New methods.
+	(gccjit::function::add_comment): Delete methods in favor of methods
+	of new class gccjit::block.
+	(gccjit::function::add_conditional): Likewise.
+	(gccjit::function::add_label): Likewise.
+	(gccjit::function::place_forward_label): Likewise.
+	(gccjit::function::add_jump): Likewise.
+	(gccjit::function::add_return): Likewise.
+	(gccjit::function::add_call): Likewise.
+
+	* internal-api.c (gcc::jit::recording::playback_label): Delete in
+	favor of...
+	(gcc::jit::recording::playback_block): New.
+	(gcc::jit::recording::context::compile): Validate.
+	(gcc::jit::recording::context::validate): New.
+	(gcc::jit::recording::function::function): Update.
+	(gcc::jit::recording::function::new_forward_label): Delete.
+	(gcc::jit::recording::function::add_eval): Delete.
+	(gcc::jit::recording::function::add_assignment): Delete.
+	(gcc::jit::recording::function::add_assignment_op): Delete.
+	(gcc::jit::recording::function::new_block): New.
+	(gcc::jit::recording::function::add_comment): Delete.
+	(gcc::jit::ecording::function::add_conditional): Delete.
+	(gcc::jit::recording::function::add_label): Delete.
+	(gcc::jit::recording::function::place_forward_label): Delete.
+	(gcc::jit::recording::function::add_jump): Delete.
+	(gcc::jit::recording::function::add_return): Delete.
+	(gcc::jit::recording::function::new_loop): Delete.
+	(gcc::jit::recording::function::write_to_dump): Port to block-based
+	representation.
+	(gcc::jit::recording::function::validate): New.
+	(gcc::jit::recording::block::add_eval): New.
+	(gcc::jit::recording::block::add_assignment): New.
+	(gcc::jit::recording::label::replay_into): Delete.
+	(gcc::jit::recording::block::add_assignment_op): New.
+	(gcc::jit::recording::block::add_comment): New.
+	(gcc::jit::recording::block::end_with_conditional): New.
+	(gcc::jit::recording::block::end_with_jump): New.
+	(gcc::jit::recording::block::end_with_return): New.
+	(gcc::jit::recording::block::write_to_dump): New.
+	(gcc::jit::recording::block::validate): New.
+	(gcc::jit::recording::block::get_last_statement): New.
+	(gcc::jit::recording::block::get_successor_blocks): New.
+	(gcc::jit::recording::block::replay_into): New.
+	(gcc::jit::recording::label::make_debug_string): Delete.
+	(gcc::jit::recording::block::make_debug_string): New.
+	(gcc::jit::recording::statement::get_successor_blocks): New.
+	(gcc::jit::recording::eval::replay_into): Port to block-based
+	representation.
+	(gcc::jit::recording::assignment::replay_into): Likewise.
+	(gcc::jit::recording::assignment_op::replay_into): Likewise.
+	(gcc::jit::recording::comment::replay_into): Likewise.
+	(gcc::jit::recording::conditional::replay_into): Likewise.
+	(gcc::jit::recording::jump::replay_into): Likewise.
+	(gcc::jit::recording::return_::replay_into): Likewise.
+
+	(gcc::jit::recording::conditional::get_successor_blocks): New.
+	(gcc::jit::recording::place_label::place_label): Delete.
+	(gcc::jit::recording::place_label::replay_into): Delete.
+	(gcc::jit::recording::place_label::make_debug_string): Delete.
+	(gcc::jit::recording::place_label::write_to_dump): Delete.
+
+	(gcc::jit::recording::jump::get_successor_blocks): New.
+	(gcc::jit::recording::return_::get_successor_blocks): New.
+
+	(gcc::jit::recording::loop::replay_into): Delete.
+	(gcc::jit::recording::loop::make_debug_string): Delete.
+	(gcc::jit::recording::loop::end): Delete.
+	(gcc::jit::recording::loop_end::replay_into): Delete.
+	(gcc::jit::recording::loop_end::make_debug_string): Delete.
+
+	(gcc::jit::playback::function::new_forward_label): Delete.
+	(gcc::jit::playback::function::new_block): New.
+	(gcc::jit::playback::function::build_stmt_list): New.
+	(gcc::jit::playback::function::add_eval): Replace with...
+	(gcc::jit::playback::block::add_eval): New.
+	(gcc::jit::playback::function::add_assignment): Replace with...
+	(gcc::jit::playback::block::add_assignment): New.
+	(gcc::jit::playback::function::add_comment): Replace with...
+	(gcc::jit::playback::block::add_comment): New, reimplementing,
+	given that we no longer have labels.
+	(gcc::jit::playback::function::add_conditional): Replace with...
+	(gcc::jit::playback::block::add_conditional): New, reworking,
+	given that on_false can no longer be NULL.
+	(gcc::jit::playback::function::add_label): Delete.
+	(gcc::jit::playback::function::place_forward_label): Delete.
+	(gcc::jit::playback::function::add_jump): Replace with...
+	(gcc::jit::playback::block::add_jump): New.
+	(gcc::jit::playback::function::add_return): Replace with...
+	(gcc::jit::playback::block::add_return): New.
+	(gcc::jit::playback::function::new_loop): Delete.
+	(gcc::jit::playback::label::label): Replace with...
+	(gcc::jit::playback::block::block): ...this.
+
+	(gcc::jit::playback::loop::loop): Delete.
+	(gcc::jit::playback::loop::end): Delete.
+
+	(gcc::jit::playback::context::replay): Call each function's
+	build_stmt_list.
+
+	* TODO.rst: Update
+
 2014-02-25  David Malcolm  <dmalcolm@redhat.com>
 
 	* libgccjit.h (gcc_jit_function_add_void_return): New.
diff --git a/gcc/jit/TODO.rst b/gcc/jit/TODO.rst
index 51a3cc9..227113a 100644
--- a/gcc/jit/TODO.rst
+++ b/gcc/jit/TODO.rst
@@ -63,8 +63,8 @@  Initial Release
   though would:
 
     extern void
-    gcc_jit_function_set_likelihood (gcc_jit_function *func,
-                                     int hotness);
+    gcc_jit_block_set_likelihood (gcc_jit_block *block,
+                                  int hotness);
 
   be better?  (for expressing how hot the current location is)
 
@@ -112,9 +112,12 @@  Initial Release
 
     * gcc_jit_function_new_local: type must not be void
 
-    * gcc_jit_function_add_assignment_op: check the types
+    * gcc_jit_block_add_assignment_op: check the types
 
-    * gcc_jit_loop_end: verify that loops are validly nested?
+* Currently each function has a single stmt_list, which is built in
+  postprocessing by walking the list of blocks.  Presumably we could
+  have each block have its own stmt_list, avoiding the need for this
+  traversal, and having the block structure show up within tree dumps.
 
 Bugs
 ====
@@ -143,8 +146,6 @@  Test suite
   thread pool working through multiple instances of the various underlying
   tests, each thread having a separate gcc_jit_context)
 
-* verify that nested loops work OK
-
 Future milestones
 =================
 * try porting llvmpipe to gcc
diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c
index 1cb4717..fa08e56 100644
--- a/gcc/jit/internal-api.c
+++ b/gcc/jit/internal-api.c
@@ -97,11 +97,11 @@  recording::playback_string (recording::string *str)
     return NULL;
 }
 
-playback::label *
-recording::playback_label (recording::label *lab)
+playback::block *
+recording::playback_block (recording::block *b)
 {
-  if (lab)
-    return lab->playback_label ();
+  if (b)
+    return b->playback_block ();
   else
     return NULL;
 }
@@ -534,6 +534,8 @@  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 result *
 recording::context::compile ()
 {
+  validate ();
+
   if (errors_occurred ())
     return NULL;
 
@@ -631,6 +633,18 @@  recording::context::dump_to_file (const char *path, bool update_locations)
     }
 }
 
+void
+recording::context::validate ()
+{
+  if (m_parent_ctxt)
+    m_parent_ctxt->validate ();
+
+  int i;
+  function *fn;
+  FOR_EACH_VEC_ELT (m_functions, i, fn)
+    fn->validate ();
+}
+
 /* gcc::jit::recording::memento:: */
 
 const char *
@@ -1182,7 +1196,7 @@  recording::function::function (context *ctxt,
   m_is_variadic (is_variadic),
   m_builtin_id (builtin_id),
   m_locals (),
-  m_activity ()
+  m_blocks ()
 {
   for (int i = 0; i< num_params; i++)
     m_params.safe_push (params[i]);
@@ -1219,114 +1233,15 @@  recording::function::new_local (recording::location *loc,
   return result;
 }
 
-recording::label*
-recording::function::new_forward_label (const char *name)
+recording::block*
+recording::function::new_block (const char *name)
 {
-  recording::label *result =
-    new recording::label (this, new_string (name));
-  m_ctxt->record (result);
-  return result;
-}
-
-void
-recording::function::add_eval (recording::location *loc,
-			       recording::rvalue *rvalue)
-{
-  statement *result = new eval (this, loc, rvalue);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_assignment (recording::location *loc,
-				     recording::lvalue *lvalue,
-				     recording::rvalue *rvalue)
-{
-  statement *result = new assignment (this, loc, lvalue, rvalue);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_assignment_op (recording::location *loc,
-					recording::lvalue *lvalue,
-					enum gcc_jit_binary_op op,
-					recording::rvalue *rvalue)
-{
-  statement *result = new assignment_op (this, loc, lvalue, op, rvalue);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_comment (recording::location *loc,
-				  const char *text)
-{
-  statement *result = new comment (this, loc, new_string (text));
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_conditional (recording::location *loc,
-				      recording::rvalue *boolval,
-				      recording::label *on_true,
-				      recording::label *on_false)
-{
-  statement *result = new conditional (this, loc, boolval, on_true, on_false);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-recording::label *
-recording::function::add_label (recording::location *loc,
-				const char *name)
-{
-  recording::label *lab = new_forward_label (name);
-  place_forward_label (loc, lab);
-  return lab;
-}
-
-void
-recording::function::place_forward_label (recording::location *loc,
-					  recording::label *lab)
-{
-  statement *result = new place_label (this, loc, lab);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_jump (recording::location *loc,
-			       recording::label *target)
-{
-  statement *result = new jump (this, loc, target);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
-
-void
-recording::function::add_return (recording::location *loc,
-				 recording::rvalue *rvalue)
-{
-  /* This is used by both gcc_jit_function_add_return and
-     gcc_jit_function_add_void_return; rvalue will be non-NULL for
-     the former and NULL for the latter.  */
-  statement *result = new return_ (this, loc, rvalue);
-  m_ctxt->record (result);
-  m_activity.safe_push (result);
-}
+  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
-recording::loop *
-recording::function::new_loop (recording::location *loc,
-			       recording::rvalue *boolval,
-			       recording::lvalue *iteration_var,
-			       recording::rvalue *step)
-{
-  recording::loop *result = new recording::loop (this, loc, boolval,
-						 iteration_var, step);
+  recording::block *result =
+    new recording::block (this, new_string (name));
   m_ctxt->record (result);
-  m_activity.safe_push (result);
+  m_blocks.safe_push (result);
   return result;
 }
 
@@ -1370,7 +1285,7 @@  recording::function::write_to_dump (dump &d)
     {
       int i;
       local *var = NULL;
-      memento *m;
+      block *b;
       d.write ("\n{\n");
 
       /* Write locals: */
@@ -1379,44 +1294,227 @@  recording::function::write_to_dump (dump &d)
       if (m_locals.length ())
 	d.write ("\n");
 
-      /* Write statements and labels: */
-      FOR_EACH_VEC_ELT (m_activity, i, m)
-	m->write_to_dump (d);
+      /* Write each block: */
+      FOR_EACH_VEC_ELT (m_blocks, i, b)
+	{
+	  if (i > 0)
+	    d.write ("\n");
+	  b->write_to_dump (d);
+	}
 
       d.write ("}\n\n");
     }
 }
 
+void
+recording::function::validate ()
+{
+  /* Complain about empty functions with non-void return type.  */
+  if (m_kind != GCC_JIT_FUNCTION_IMPORTED
+      && m_return_type != m_ctxt->get_type (GCC_JIT_TYPE_VOID))
+    if (0 == m_blocks.length ())
+      m_ctxt->add_error ("function %s returns non-void (type: %s)"
+			 " but has no blocks",
+			 get_debug_string (),
+			 m_return_type->get_debug_string ());
+
+  /* Check that all blocks are terminated.  */
+  int num_invalid_blocks = 0;
+  {
+    int i;
+    block *b;
+
+    FOR_EACH_VEC_ELT (m_blocks, i, b)
+      if (!b->validate ())
+	num_invalid_blocks++;
+  }
+
+  /* Check that all blocks are reachable.  */
+  if (m_blocks.length () > 0 && 0 == num_invalid_blocks)
+    {
+      /* Iteratively walk the graph of blocks, marking their "m_is_reachable"
+	 flag, starting at the initial block.  */
+      vec<block *> worklist;
+      worklist.create (m_blocks.length ());
+      worklist.safe_push (m_blocks[0]);
+      while (worklist.length () > 0)
+	{
+	  block *b = worklist.pop ();
+	  b->m_is_reachable = true;
+
+	  /* Add successor blocks that aren't yet marked to the worklist.  */
+	  /* We checked that each block has a terminating statement above .  */
+	  block *next1, *next2;
+	  int n = b->get_successor_blocks (&next1, &next2);
+	  switch (n)
+	    {
+	    default:
+	      gcc_unreachable ();
+	    case 2:
+	      if (!next2->m_is_reachable)
+		worklist.safe_push (next2);
+	      /* fallthrough */
+	    case 1:
+	      if (!next1->m_is_reachable)
+		worklist.safe_push (next1);
+	      break;
+	    case 0:
+	      break;
+	    }
+	}
+
+      /* Now complain about any blocks that haven't been marked.  */
+      {
+	int i;
+	block *b;
+	FOR_EACH_VEC_ELT (m_blocks, i, b)
+	  if (!b->m_is_reachable)
+	    m_ctxt->add_error ("unreachable block: %s",
+			       b->get_debug_string ());
+      }
+    }
+}
+
 recording::string *
 recording::function::make_debug_string ()
 {
   return m_name;
 }
 
-/* gcc::jit::recording::label:: */
+/* gcc::jit::recording::block:: */
+
+void
+recording::block::add_eval (recording::location *loc,
+			    recording::rvalue *rvalue)
+{
+  statement *result = new eval (this, loc, rvalue);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+}
+
+void
+recording::block::add_assignment (recording::location *loc,
+				  recording::lvalue *lvalue,
+				  recording::rvalue *rvalue)
+{
+  statement *result = new assignment (this, loc, lvalue, rvalue);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+}
+
+void
+recording::block::add_assignment_op (recording::location *loc,
+				     recording::lvalue *lvalue,
+				     enum gcc_jit_binary_op op,
+				     recording::rvalue *rvalue)
+{
+  statement *result = new assignment_op (this, loc, lvalue, op, rvalue);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+}
+
+void
+recording::block::add_comment (recording::location *loc,
+			       const char *text)
+{
+  statement *result = new comment (this, loc, new_string (text));
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+}
+
+void
+recording::block::end_with_conditional (recording::location *loc,
+					recording::rvalue *boolval,
+					recording::block *on_true,
+					recording::block *on_false)
+{
+  statement *result = new conditional (this, loc, boolval, on_true, on_false);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+}
+
+void
+recording::block::end_with_jump (recording::location *loc,
+				 recording::block *target)
+{
+  statement *result = new jump (this, loc, target);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+}
+
+void
+recording::block::end_with_return (recording::location *loc,
+				   recording::rvalue *rvalue)
+{
+  /* This is used by both gcc_jit_function_add_return and
+     gcc_jit_function_add_void_return; rvalue will be non-NULL for
+     the former and NULL for the latter.  */
+  statement *result = new return_ (this, loc, rvalue);
+  m_ctxt->record (result);
+  m_statements.safe_push (result);
+  m_has_been_terminated = true;
+}
 
 void
-recording::label::replay_into (replayer *r)
+recording::block::write_to_dump (dump &d)
 {
-  if (!m_has_been_placed)
+  d.write ("%s:\n", get_debug_string ());
+
+  int i;
+  statement *s;
+  FOR_EACH_VEC_ELT (m_statements, i, s)
+    s->write_to_dump (d);
+}
+
+bool
+recording::block::validate ()
+{
+  if (!has_been_terminated ())
     {
-      r->add_error ("unplaced label within %s: %s",
-		    m_func->get_debug_string (),
-		    get_debug_string ());
-      return;
+      m_func->get_context ()->add_error ("unterminated block in %s: %s",
+					 m_func->get_debug_string (),
+					 get_debug_string ());
+      return false;
     }
+
+  return true;
+}
+
+recording::statement *
+recording::block::get_last_statement () const
+{
+  if (m_statements.length ())
+    return m_statements[m_statements.length () - 1];
+  else
+    return NULL;
+}
+
+int
+recording::block::get_successor_blocks (block **next1, block **next2) const
+{
+  gcc_assert (m_has_been_terminated);
+  statement *last_statement = get_last_statement ();
+  gcc_assert (last_statement);
+  return last_statement->get_successor_blocks (next1, next2);
+}
+
+void
+recording::block::replay_into (replayer *)
+{
   set_playback_obj (m_func->playback_function ()
-		      ->new_forward_label (playback_string (m_name)));
+		      ->new_block (playback_string (m_name)));
 }
 
 recording::string *
-recording::label::make_debug_string ()
+recording::block::make_debug_string ()
 {
   if (m_name)
     return m_name;
   else
     return string::from_printf (m_ctxt,
-				"<UNNAMED LABEL %p>",
+				"<UNNAMED BLOCK %p>",
 				(void *)this);
 }
 
@@ -1779,6 +1877,16 @@  recording::local::write_to_dump (dump &d)
 
 // gcc::jit::recording::statement
 
+int
+recording::statement::get_successor_blocks (block **/*out_next1*/,
+					    block **/*out_next2*/) const
+{
+  /* The base class implementation is for non-terminating statements,
+     and thus should never be called.  */
+  gcc_unreachable ();
+  return 0;
+}
+
 void
 recording::statement::write_to_dump (dump &d)
 {
@@ -1790,7 +1898,7 @@  recording::statement::write_to_dump (dump &d)
 void
 recording::eval::replay_into (replayer *r)
 {
-  playback_function ()
+  playback_block (get_block ())
     ->add_eval (playback_location (r),
 		m_rvalue->playback_rvalue ());
 }
@@ -1806,7 +1914,7 @@  recording::eval::make_debug_string ()
 void
 recording::assignment::replay_into (replayer *r)
 {
-  playback_function ()
+  playback_block (get_block ())
     ->add_assignment (playback_location (r),
 		      m_lvalue->playback_lvalue (),
 		      m_rvalue->playback_rvalue ());
@@ -1834,7 +1942,7 @@  recording::assignment_op::replay_into (replayer *r)
 		      m_lvalue->playback_rvalue (),
 		      m_rvalue->playback_rvalue ());
 
-  playback_function ()
+  playback_block (get_block ())
     ->add_assignment (playback_location (r),
 		      m_lvalue->playback_lvalue (),
 		      binary_op);
@@ -1853,7 +1961,7 @@  recording::assignment_op::make_debug_string ()
 void
 recording::comment::replay_into (replayer *r)
 {
-  playback_function ()
+  playback_block (get_block ())
     ->add_comment (playback_location (r),
 		   m_text->c_str ());
 }
@@ -1869,11 +1977,20 @@  recording::comment::make_debug_string ()
 void
 recording::conditional::replay_into (replayer *r)
 {
-  playback_function ()
+  playback_block (get_block ())
     ->add_conditional (playback_location (r),
 		       m_boolval->playback_rvalue (),
-		       playback_label (m_on_true),
-		       playback_label (m_on_false));
+		       playback_block (m_on_true),
+		       playback_block (m_on_false));
+}
+
+int
+recording::conditional::get_successor_blocks (block **out_next1,
+					      block **out_next2) const
+{
+  *out_next1 = m_on_true;
+  *out_next2 = m_on_false;
+  return 2;
 }
 
 recording::string *
@@ -1892,46 +2009,20 @@  recording::conditional::make_debug_string ()
 				m_on_true->get_debug_string ());
 }
 
-recording::place_label::place_label (function *func,
-				     location *loc,
-				     label *lab)
-: statement (func, loc),
-  m_label (lab)
-{
-  if (lab->m_has_been_placed)
-    m_ctxt->add_error ("label %s has already been placed",
-		       lab->get_debug_string ());
-  lab->m_has_been_placed = true;
-}
-
 void
-recording::place_label::replay_into (replayer *r)
-{
-  playback_function ()
-    ->place_forward_label (playback_location (r),
-			   m_label->playback_label ());
-}
-
-recording::string *
-recording::place_label::make_debug_string ()
-{
-  return string::from_printf (m_ctxt,
-			      "%s:",
-			      m_label->get_debug_string ());
-}
-
-void
-recording::place_label::write_to_dump (dump &d)
+recording::jump::replay_into (replayer *r)
 {
-  d.write ("\n%s\n", get_debug_string ());
+  playback_block (get_block ())
+    ->add_jump (playback_location (r),
+		m_target->playback_block ());
 }
 
-void
-recording::jump::replay_into (replayer *r)
+int
+recording::jump::get_successor_blocks (block **out_next1,
+				       block **/*out_next2*/) const
 {
-  playback_function ()
-    ->add_jump (playback_location (r),
-		m_target->playback_label ());
+  *out_next1 = m_target;
+  return 1;
 }
 
 recording::string *
@@ -1945,11 +2036,18 @@  recording::jump::make_debug_string ()
 void
 recording::return_::replay_into (replayer *r)
 {
-  playback_function ()
+  playback_block (get_block ())
     ->add_return (playback_location (r),
 		  m_rvalue ? m_rvalue->playback_rvalue () : NULL);
 }
 
+int
+recording::return_::get_successor_blocks (block **/*out_next1*/,
+					  block **/*out_next2*/) const
+{
+  return 0;
+}
+
 recording::string *
 recording::return_::make_debug_string ()
 {
@@ -1962,52 +2060,6 @@  recording::return_::make_debug_string ()
 				"return;");
 }
 
-void
-recording::loop::replay_into (replayer *r)
-{
-  set_playback_obj (
-    m_func->playback_function ()
-      ->new_loop (playback_location (r, m_loc),
-		  m_boolval->playback_rvalue ()));
-}
-
-recording::string *
-recording::loop::make_debug_string ()
-{
-  return string::from_printf (m_ctxt,
-			      "loop_while (%s)",
-			      m_boolval->get_debug_string ());
-}
-
-void
-recording::loop::end (location *loc)
-{
-  if (m_iteration_var)
-    m_func->add_assignment (
-      loc,
-      m_iteration_var,
-      m_ctxt->new_binary_op (loc,
-			     GCC_JIT_BINARY_OP_PLUS,
-			     m_iteration_var->get_type (),
-			     m_iteration_var,
-			     m_step));
-  recording::loop_end *m = new loop_end (this, loc);
-  m_ctxt->record (m);
-}
-
-void
-recording::loop_end::replay_into (replayer *r)
-{
-  m_loop->playback_loop ()->end (playback_location (r, m_loc));
-}
-
-recording::string *
-recording::loop_end::make_debug_string ()
-{
-  return string::from_printf (m_ctxt,
-			      "end_loop (%p)", (void *)m_loop);
-}
-
 /**********************************************************************
  Playback.
  **********************************************************************/
@@ -2885,12 +2937,37 @@  new_local (location *loc,
   return new lvalue (m_ctxt, inner);
 }
 
-playback::label *
+playback::block *
 playback::function::
-new_forward_label (const char *name)
+new_block (const char *name)
 {
   gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
-  return new playback::label (this, name);
+
+  block *result = new playback::block (this, name);
+  m_blocks.safe_push (result);
+  return result;
+}
+
+void
+playback::function::
+build_stmt_list ()
+{
+  int i;
+  block *b;
+
+  FOR_EACH_VEC_ELT (m_blocks, i, b)
+    {
+      int j;
+      tree stmt;
+
+      b->m_label_expr = build1 (LABEL_EXPR,
+				void_type_node,
+				b->as_label_decl ());
+      tsi_link_after (&m_stmt_iter, b->m_label_expr, TSI_CONTINUE_LINKING);
+
+      FOR_EACH_VEC_ELT (b->m_stmts, j, stmt)
+	tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
+    }
 }
 
 void
@@ -2943,12 +3020,11 @@  postprocess ()
 }
 
 void
-playback::function::
+playback::block::
 add_eval (location *loc,
 	  rvalue *rvalue)
 {
   gcc_assert (rvalue);
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
   if (loc)
     set_tree_location (rvalue->as_tree (), loc);
@@ -2957,14 +3033,13 @@  add_eval (location *loc,
 }
 
 void
-playback::function::
+playback::block::
 add_assignment (location *loc,
 		lvalue *lvalue,
 		rvalue *rvalue)
 {
   gcc_assert (lvalue);
   gcc_assert (rvalue);
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
   tree t_lvalue = lvalue->as_tree ();
   tree t_rvalue = rvalue->as_tree ();
@@ -2986,12 +3061,10 @@  add_assignment (location *loc,
 }
 
 void
-playback::function::
+playback::block::
 add_comment (location *loc,
 	     const char *text)
 {
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
-
   /* Wrap the text in C-style comment delimiters.  */
   size_t sz =
     (3 /* opening delim */
@@ -3003,20 +3076,27 @@  add_comment (location *loc,
 
   /* For now we simply implement this by adding a dummy label with a name
      containing the given text.  */
-  add_label (loc, wrapped);
+  tree identifier = get_identifier (wrapped);
+  tree label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL,
+				identifier, void_type_node);
+  DECL_CONTEXT (label_decl) = m_func->as_fndecl ();
+
+  tree label_expr = build1 (LABEL_EXPR, void_type_node, label_decl);
+  if (loc)
+    set_tree_location (label_expr, loc);
+  add_stmt (label_expr);
 }
 
 void
-playback::function::
+playback::block::
 add_conditional (location *loc,
 		 rvalue *boolval,
-		 label *on_true,
-		 label *on_false)
+		 block *on_true,
+		 block *on_false)
 {
   gcc_assert (boolval);
   gcc_assert (on_true);
-  /* on_false can be NULL */
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
+  gcc_assert (on_false);
 
   /* COND_EXPR wants statement lists for the true/false operands, but we
      want labels.
@@ -3025,16 +3105,11 @@  add_conditional (location *loc,
 			   on_true->as_label_decl ());
   if (loc)
     set_tree_location (true_jump, loc);
-  tree false_jump;
-  if (on_false)
-    {
-      false_jump = build1 (GOTO_EXPR, void_type_node,
-			   on_false->as_label_decl ());
-      if (loc)
-        set_tree_location (false_jump, loc);
-    }
-  else
-    false_jump = NULL;
+
+  tree false_jump = build1 (GOTO_EXPR, void_type_node,
+			    on_false->as_label_decl ());
+  if (loc)
+    set_tree_location (false_jump, loc);
 
   tree stmt =
     build3 (COND_EXPR, void_type_node, boolval->as_tree (),
@@ -3044,41 +3119,12 @@  add_conditional (location *loc,
   add_stmt (stmt);
 }
 
-playback::label *
-playback::function::
-add_label (location *loc,
-	   const char *name)
-{
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
-
-  label *lab = new label (this, name);
-  place_forward_label (loc, lab);
-  return lab;
-}
-
 void
-playback::function::
-place_forward_label (location *loc, label *lab)
-{
-  gcc_assert (lab);
-  gcc_assert (NULL == lab->m_label_expr); // must not have already been placed
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
-
-  lab->m_label_expr = build1 (LABEL_EXPR,
-			     void_type_node,
-			     lab->as_label_decl ());
-  if (loc)
-    set_tree_location (lab->m_label_expr, loc);
-  add_stmt (lab->m_label_expr);
-}
-
-void
-playback::function::
+playback::block::
 add_jump (location *loc,
-	  label *target)
+	  block *target)
 {
   gcc_assert (target);
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
 
   // see c_finish_loop
   //tree top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
@@ -3111,17 +3157,15 @@  c_finish_goto_label (location_t loc, tree label)
 }
 
 void
-playback::function::
+playback::block::
 add_return (location *loc,
 	    rvalue *rvalue)
 {
-  gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED);
-
   tree modify_retval = NULL;
-  tree return_type = TREE_TYPE (TREE_TYPE (m_inner_fndecl));
+  tree return_type = m_func->get_return_type_as_tree ();
   if (rvalue)
     {
-      tree t_lvalue = DECL_RESULT (m_inner_fndecl);
+      tree t_lvalue = DECL_RESULT (m_func->as_fndecl ());
       tree t_rvalue = rvalue->as_tree ();
       if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue))
 	t_rvalue = build1 (CONVERT_EXPR,
@@ -3140,17 +3184,11 @@  add_return (location *loc,
   add_stmt (return_stmt);
 }
 
-playback::loop *
-playback::function::
-new_loop (location *loc,
-	  rvalue *boolval)
-{
-  return new loop (this, loc, boolval);
-}
-
-playback::label::
-label (function *func,
+playback::block::
+block (function *func,
        const char *name)
+: m_func (func),
+  m_stmts ()
 {
   tree identifier;
 
@@ -3166,26 +3204,6 @@  label (function *func,
   m_label_expr = NULL;
 }
 
-
-playback::loop::
-loop (function *func, location *loc, rvalue *boolval) :
-  m_func(func)
-{
-  m_label_cond = func->add_label (loc, "loop_cond");
-  m_label_body = func->new_forward_label ("loop_body");
-  m_label_end = func->new_forward_label ("loop_end");
-  func->add_conditional (loc, boolval, m_label_body, m_label_end);
-  func->place_forward_label (loc, m_label_body);
-}
-
-void
-playback::loop::
-end (location *loc)
-{
-  m_func->add_jump (loc, m_label_cond);
-  m_func->place_forward_label (loc, m_label_end);
-}
-
 result *
 playback::context::
 compile ()
@@ -3396,6 +3414,14 @@  replay ()
       /* No GC can happen yet; process the cached source locations.  */
       handle_locations ();
 
+      /* We've now created tree nodes for the stmts in the various blocks
+	 in each function, but we haven't built each function's single stmt
+	 list yet.  Do so now.  */
+      FOR_EACH_VEC_ELT (m_functions, i, func)
+	func->build_stmt_list ();
+
+      /* No GC can have happened yet.  */
+
       /* Postprocess the functions.  This could trigger GC.  */
       FOR_EACH_VEC_ELT (m_functions, i, func)
 	{
diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h
index 0017f6c..a37cafe 100644
--- a/gcc/jit/internal-api.h
+++ b/gcc/jit/internal-api.h
@@ -88,12 +88,12 @@  namespace recording {
   class struct_;
   class fields;
   class function;
-  class label;
+  class block;
   class rvalue;
   class lvalue;
   class local;
   class param;
-  class loop;
+  class statement;
 }
 
 namespace playback {
@@ -103,11 +103,10 @@  namespace playback {
   class field;
   class struct_;
   class function;
-  class label;
+  class block;
   class rvalue;
   class lvalue;
   class param;
-  class loop;
   class source_file;
   class source_line;
 }
@@ -151,8 +150,8 @@  playback_location (replayer *r, location *loc);
 const char *
 playback_string (string *str);
 
-playback::label *
-playback_label (label *lab);
+playback::block *
+playback_block (block *b);
 
 /* A JIT-compilation context.  */
 class context
@@ -312,6 +311,9 @@  public:
   void dump_to_file (const char *path, bool update_locations);
 
 private:
+  void validate ();
+
+private:
   context *m_parent_ctxt;
 
   int m_error_count;
@@ -845,53 +847,8 @@  public:
 	     type *type,
 	     const char *name);
 
-  label*
-  new_forward_label (const char *name);
-
-  void
-  add_eval (location *loc,
-	    rvalue *rvalue);
-
-  void
-  add_assignment (location *loc,
-		  lvalue *lvalue,
-		  rvalue *rvalue);
-
-  void
-  add_assignment_op (location *loc,
-		     lvalue *lvalue,
-		     enum gcc_jit_binary_op op,
-		     rvalue *rvalue);
-
-  void
-  add_comment (location *loc,
-	       const char *text);
-
-  void
-  add_conditional (location *loc,
-		   rvalue *boolval,
-		   label *on_true,
-		   label *on_false);
-
-  label *
-  add_label (location *loc,
-	     const char *name);
-
-  void
-  place_forward_label (location *loc, label *lab);
-
-  void
-  add_jump (location *loc,
-	    label *target);
-
-  void
-  add_return (location *loc,
-	      rvalue *rvalue);
-
-  loop *
-  new_loop (location *loc,
-	    rvalue *boolval,
-	    lvalue *iteration_var, rvalue *step);
+  block*
+  new_block (const char *name);
 
   type *get_return_type () const { return m_return_type; }
   string * get_name () const { return m_name; }
@@ -901,6 +858,8 @@  public:
 
   void write_to_dump (dump &d);
 
+  void validate ();
+
 private:
   string * make_debug_string ();
 
@@ -912,41 +871,88 @@  private:
   vec<param *> m_params;
   int m_is_variadic;
   enum built_in_function m_builtin_id;
-  /* Additional vectors to help when dumping.  */
   vec<local *> m_locals;
-  vec<memento *> m_activity; // statements and labels
+  vec<block *> m_blocks;
 };
 
-class label : public memento
+class block : public memento
 {
 public:
-  label (function *func, string *name)
+  block (function *func, string *name)
   : memento (func->m_ctxt),
     m_func (func),
     m_name (name),
-    m_has_been_placed (false)
+    m_statements (),
+    m_has_been_terminated (false),
+    m_is_reachable (false)
   {
   }
 
-  void replay_into (replayer *r);
+  function *get_function () { return m_func; }
+
+  bool has_been_terminated () { return m_has_been_terminated; }
+  bool is_reachable () { return m_is_reachable; }
+
+  void
+  add_eval (location *loc,
+	    rvalue *rvalue);
+
+  void
+  add_assignment (location *loc,
+		  lvalue *lvalue,
+		  rvalue *rvalue);
+
+  void
+  add_assignment_op (location *loc,
+		     lvalue *lvalue,
+		     enum gcc_jit_binary_op op,
+		     rvalue *rvalue);
+
+  void
+  add_comment (location *loc,
+	       const char *text);
+
+  void
+  end_with_conditional (location *loc,
+			rvalue *boolval,
+			block *on_true,
+			block *on_false);
+
+  void
+  end_with_jump (location *loc,
+		 block *target);
 
-  playback::label *
-  playback_label () const
+  void
+  end_with_return (location *loc,
+		   rvalue *rvalue);
+
+  playback::block *
+  playback_block () const
   {
-    return static_cast <playback::label *> (m_playback_obj);
+    return static_cast <playback::block *> (m_playback_obj);
   }
 
-  bool has_been_placed () { return m_has_been_placed; }
+  void write_to_dump (dump &d);
+
+  bool validate ();
+
+  statement *get_last_statement () const;
+
+  int get_successor_blocks (block **next1, block **next2) const;
 
 private:
   string * make_debug_string ();
 
+  void replay_into (replayer *r);
+
 private:
   function *m_func;
   string *m_name;
-  bool m_has_been_placed;
+  vec<statement *> m_statements;
+  bool m_has_been_terminated;
+  bool m_is_reachable;
 
-  friend class place_label;
+  friend class function;
 };
 
 class global : public lvalue
@@ -1283,17 +1289,19 @@  private:
 
 class statement : public memento
 {
+public:
+  virtual int get_successor_blocks (block **out_next1,
+				    block **out_next2) const;
+
+  void write_to_dump (dump &d);
+
 protected:
-  statement (function *func, location *loc)
-  : memento (func->m_ctxt),
-    m_func (func),
+  statement (block *b, location *loc)
+  : memento (b->m_ctxt),
+    m_block (b),
     m_loc (loc) {}
 
-  playback::function *
-  playback_function () const
-  {
-    return m_func->playback_function ();
-  }
+  block *get_block () const { return m_block; }
 
   playback::location *
   playback_location (replayer *r) const
@@ -1302,20 +1310,17 @@  protected:
   }
 
 private:
-  void write_to_dump (dump &d);
-
-private:
-  function *m_func;
+  block *m_block;
   location *m_loc;
 };
 
 class eval : public statement
 {
 public:
-  eval (function *func,
+  eval (block *b,
 	location *loc,
 	rvalue *rvalue)
-  : statement (func, loc),
+  : statement (b, loc),
     m_rvalue (rvalue) {}
 
   void replay_into (replayer *r);
@@ -1330,11 +1335,11 @@  private:
 class assignment : public statement
 {
 public:
-  assignment (function *func,
+  assignment (block *b,
 	      location *loc,
 	      lvalue *lvalue,
 	      rvalue *rvalue)
-  : statement (func, loc),
+  : statement (b, loc),
     m_lvalue (lvalue),
     m_rvalue (rvalue) {}
 
@@ -1351,12 +1356,12 @@  private:
 class assignment_op : public statement
 {
 public:
-  assignment_op (function *func,
+  assignment_op (block *b,
 		 location *loc,
 		 lvalue *lvalue,
 		 enum gcc_jit_binary_op op,
 		 rvalue *rvalue)
-  : statement (func, loc),
+  : statement (b, loc),
     m_lvalue (lvalue),
     m_op (op),
     m_rvalue (rvalue) {}
@@ -1375,10 +1380,10 @@  private:
 class comment : public statement
 {
 public:
-  comment (function *func,
+  comment (block *b,
 	   location *loc,
 	   string *text)
-  : statement (func, loc),
+  : statement (b, loc),
     m_text (text) {}
 
   void replay_into (replayer *r);
@@ -1393,145 +1398,70 @@  private:
 class conditional : public statement
 {
 public:
-  conditional (function *func,
+  conditional (block *b,
 	       location *loc,
 	       rvalue *boolval,
-	       label *on_true,
-	       label *on_false)
-  : statement (func, loc),
+	       block *on_true,
+	       block *on_false)
+  : statement (b, loc),
     m_boolval (boolval),
     m_on_true (on_true),
     m_on_false (on_false) {}
 
   void replay_into (replayer *r);
 
-private:
-  string * make_debug_string ();
-
-private:
-  rvalue *m_boolval;
-  label *m_on_true;
-  label *m_on_false;
-};
-
-class place_label : public statement
-{
-public:
-  place_label (function *func,
-	       location *loc,
-	       label *lab);
-
-  void replay_into (replayer *r);
+  int get_successor_blocks (block **out_next1,
+			    block **out_next2) const;
 
 private:
   string * make_debug_string ();
 
-  void write_to_dump (dump &d);
-
 private:
-  label *m_label;
+  rvalue *m_boolval;
+  block *m_on_true;
+  block *m_on_false;
 };
 
 class jump : public statement
 {
 public:
-  jump (function *func,
+  jump (block *b,
 	location *loc,
-	label *target)
-  : statement (func, loc),
+	block *target)
+  : statement (b, loc),
     m_target (target) {}
 
   void replay_into (replayer *r);
 
+  int get_successor_blocks (block **out_next1,
+			    block **out_next2) const;
+
 private:
   string * make_debug_string ();
 
 private:
-  label *m_target;
+  block *m_target;
 };
 
 class return_ : public statement
 {
 public:
-  return_ (function *func,
+  return_ (block *b,
 	   location *loc,
 	   rvalue *rvalue)
-  : statement (func, loc),
+  : statement (b, loc),
     m_rvalue (rvalue) {}
 
   void replay_into (replayer *r);
 
-private:
-  string * make_debug_string ();
-
-private:
-  rvalue *m_rvalue;
-};
-
-class loop : public memento
-{
-public:
-  loop (function *func,
-	location *loc,
-	rvalue *boolval,
-	lvalue *iteration_var, rvalue *step)
-  : memento (func->m_ctxt),
-    m_func (func),
-    m_loc (loc),
-    m_boolval (boolval),
-    m_iteration_var (iteration_var),
-    m_step (step)
-  {}
-
-  void replay_into (replayer *r);
-
-  void end (location *loc);
-
-  playback::loop *
-  playback_loop ()
-  {
-    return static_cast <playback::loop *> (m_playback_obj);
-  }
+  int get_successor_blocks (block **out_next1,
+			    block **out_next2) const;
 
 private:
   string * make_debug_string ();
 
 private:
-  function *m_func;
-  location *m_loc;
-  rvalue *m_boolval;
-
-  /* The following fields are for handling
-     gcc_jit_function_new_loop_over_range and are NULL for other loops.
-
-     We preserve these from "_over_range" calls so that we can inject a
-     += assignment in the correct place immediately before the loop ends.
-     This is done within the recording::loop::end () method.
-     The corresponding playback::loop class has no special handling for
-     this, with the += operation having become a regular assignment by
-     that time.  */
-  lvalue *m_iteration_var;
-  rvalue *m_step;
-};
-
-class loop_end : public memento
-{
-public:
-  loop_end (loop *loop,
-	    location *loc)
-  : memento (loop->m_ctxt),
-    m_loop (loop),
-    m_loc (loc)
-  {}
-
-  void replay_into (replayer *r);
-
-private:
-  string * make_debug_string ();
-
-private:
-  loop *m_loop;
-  location *m_loc;
+  rvalue *m_rvalue;
 };
 
 } // namespace gcc::jit::recording
@@ -1826,8 +1756,42 @@  public:
 	     type *type,
 	     const char *name);
 
-  label*
-  new_forward_label (const char *name);
+  block*
+  new_block (const char *name);
+
+  void
+  build_stmt_list ();
+
+  void
+  postprocess ();
+
+public:
+  context *m_ctxt;
+
+public:
+  void
+  set_tree_location (tree t, location *loc)
+  {
+    m_ctxt->set_tree_location (t, loc);
+  }
+
+private:
+  tree m_inner_fndecl;
+  tree m_inner_block;
+  tree m_inner_bind_expr;
+  enum gcc_jit_function_kind m_kind;
+  tree m_stmt_list;
+  tree_stmt_iterator m_stmt_iter;
+  vec<block *> m_blocks;
+};
+
+class block : public wrapper
+{
+public:
+  block (function *func,
+	 const char *name);
+
+  tree as_label_decl () const { return m_label_decl; }
 
   void
   add_eval (location *loc,
@@ -1845,68 +1809,43 @@  public:
   void
   add_conditional (location *loc,
 		   rvalue *boolval,
-		   label *on_true,
-		   label *on_false);
+		   block *on_true,
+		   block *on_false);
 
-  label *
-  add_label (location *loc,
+  block *
+  add_block (location *loc,
 	     const char *name);
 
   void
-  place_forward_label (location *loc, label *lab);
-
-  void
   add_jump (location *loc,
-	    label *target);
+	    block *target);
 
   void
   add_return (location *loc,
 	      rvalue *rvalue);
 
-  loop *
-  new_loop (location *loc,
-	    rvalue *boolval);
-
-  void
-  postprocess ();
-
-public:
-  context *m_ctxt;
-
 private:
-  void add_stmt (tree stmt)
-  {
-    tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING);
-  }
-
   void
   set_tree_location (tree t, location *loc)
   {
-    m_ctxt->set_tree_location (t, loc);
+    m_func->set_tree_location (t, loc);
   }
 
-private:
-  tree m_inner_fndecl;
-  tree m_inner_block;
-  tree m_inner_bind_expr;
-  enum gcc_jit_function_kind m_kind;
-  tree m_stmt_list;
-  tree_stmt_iterator m_stmt_iter;
-};
-
-class label : public wrapper
-{
-public:
-  label (function *func,
-	 const char *name);
-
-  tree as_label_decl () const { return m_label_decl; }
+  void add_stmt (tree stmt)
+  {
+    /* TODO: use one stmt_list per block.  */
+    m_stmts.safe_push (stmt);
+  }
 
 private:
+  function *m_func;
   tree m_label_decl;
+  vec<tree> m_stmts;
 
 public: // for now
   tree m_label_expr;
+
+  friend class function;
 };
 
 class rvalue : public wrapper
@@ -1970,20 +1909,6 @@  public:
   {}
 };
 
-class loop : public wrapper
-{
-public:
-  loop (function *func, location *loc, rvalue *boolval);
-
-  void end (location *loc);
-
-private:
-  function *m_func;
-  label *m_label_cond;
-  label *m_label_body;
-  label *m_label_end;
-};
-
 /* Dealing with the linemap API.
 
    It appears that libcpp requires locations to be created as if by
diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h
index 95a8c71..a8801a3 100644
--- a/gcc/jit/libgccjit++.h
+++ b/gcc/jit/libgccjit++.h
@@ -22,7 +22,7 @@  namespace gccjit
   class struct_;
   class param;
   class function;
-  class label;
+  class block;
   class rvalue;
   class lvalue;
 
@@ -290,13 +290,32 @@  namespace gccjit
 
     param get_param (int index);
 
-    label new_forward_label ();
-    label new_forward_label (const std::string &name);
+    block new_block ();
+    block new_block (const std::string &name);
 
     lvalue new_local (type type_,
 		      const std::string &name,
 		      location loc = location ());
 
+    /* A series of overloaded operator () with various numbers of arguments
+       for a very terse way of creating a call to this function.  The call
+       is created within the same context as the function itself, which may
+       not be what you want.  */
+    rvalue operator() (location loc = location ());
+    rvalue operator() (rvalue arg0,
+		       location loc = location ());
+    rvalue operator() (rvalue arg0, rvalue arg1,
+		       location loc = location ());
+  };
+
+  class block : public object
+  {
+  public:
+    block ();
+    block (gcc_jit_block *inner);
+
+    gcc_jit_block *get_inner_block () const;
+
     void add_eval (rvalue rvalue,
 		   location loc = location ());
 
@@ -309,28 +328,6 @@  namespace gccjit
 			    rvalue rvalue,
 			    location loc = location ());
 
-    void add_comment (const std::string &text,
-		      location loc = location ());
-
-    void add_conditional (rvalue boolval,
-			  label on_true,
-			  label on_false,
-			  location loc = location ());
-
-    label add_label (const std::string &name,
-		     location loc = location ());
-    label add_label (location loc = location ());
-
-    void place_forward_label (label lab,
-			      location loc = location ());
-
-    void add_jump (label target,
-		   location loc = location ());
-
-    void add_return (rvalue rvalue,
-		     location loc = location ());
-    void add_return (location loc = location ());
-
     /* A way to add a function call to the body of a function being
        defined, with various numbers of args.  */
     rvalue add_call (function other,
@@ -345,24 +342,21 @@  namespace gccjit
 		     rvalue arg0, rvalue arg1, rvalue arg2,
 		     location loc = location ());
 
-    /* A series of overloaded operator () with various numbers of arguments
-       for a very terse way of creating a call to this function.  The call
-       is created within the same context as the function itself, which may
-       not be what you want.  */
-    rvalue operator() (location loc = location ());
-    rvalue operator() (rvalue arg0,
-		       location loc = location ());
-    rvalue operator() (rvalue arg0, rvalue arg1,
-		       location loc = location ());
-  };
+    void add_comment (const std::string &text,
+		      location loc = location ());
 
-  class label : public object
-  {
-  public:
-    label ();
-    label (gcc_jit_label *inner);
+    void end_with_conditional (rvalue boolval,
+			       block on_true,
+			       block on_false,
+			       location loc = location ());
+
+    void end_with_jump (block target,
+			location loc = location ());
+
+    void end_with_return (rvalue rvalue,
+			  location loc = location ());
+    void end_with_return (location loc = location ());
 
-    gcc_jit_label *get_inner_label () const;
   };
 
   class rvalue : public object
@@ -1090,18 +1084,18 @@  function::get_param (int index)
 					    index));
 }
 
-inline label
-function::new_forward_label ()
+inline block
+function::new_block ()
 {
-  return label (gcc_jit_function_new_forward_label (get_inner_function (),
-						    NULL));
+  return block (gcc_jit_function_new_block (get_inner_function (),
+					    NULL));
 }
 
-inline label
-function::new_forward_label (const std::string &name)
+inline block
+function::new_block (const std::string &name)
 {
-  return label (gcc_jit_function_new_forward_label (get_inner_function (),
-						    name.c_str ()));
+  return block (gcc_jit_function_new_block (get_inner_function (),
+					    name.c_str ()));
 }
 
 inline lvalue
@@ -1116,141 +1110,115 @@  function::new_local (type type_,
 }
 
 inline void
-function::add_eval (rvalue rvalue,
-		    location loc)
-{
-  gcc_jit_function_add_eval (get_inner_function (),
-			     loc.get_inner_location (),
-			     rvalue.get_inner_rvalue ());
-}
-
-inline void
-function::add_assignment (lvalue lvalue,
-			  rvalue rvalue,
-			  location loc)
-{
-  gcc_jit_function_add_assignment (get_inner_function (),
-				   loc.get_inner_location (),
-				   lvalue.get_inner_lvalue (),
-				   rvalue.get_inner_rvalue ());
-}
-
-inline void
-function::add_assignment_op (lvalue lvalue,
-			     enum gcc_jit_binary_op op,
-			     rvalue rvalue,
-			     location loc)
+block::add_eval (rvalue rvalue,
+		 location loc)
 {
-  gcc_jit_function_add_assignment_op (get_inner_function (),
-				      loc.get_inner_location (),
-				      lvalue.get_inner_lvalue (),
-				      op,
-				      rvalue.get_inner_rvalue ());
+  gcc_jit_block_add_eval (get_inner_block (),
+			  loc.get_inner_location (),
+			  rvalue.get_inner_rvalue ());
 }
 
 inline void
-function::add_comment (const std::string &text,
+block::add_assignment (lvalue lvalue,
+		       rvalue rvalue,
 		       location loc)
 {
-  gcc_jit_function_add_comment (get_inner_function (),
+  gcc_jit_block_add_assignment (get_inner_block (),
 				loc.get_inner_location (),
-				text.c_str ());
+				lvalue.get_inner_lvalue (),
+				rvalue.get_inner_rvalue ());
 }
 
 inline void
-function::add_conditional (rvalue boolval,
-			   label on_true,
-			   label on_false,
-			   location loc)
-{
-  gcc_jit_function_add_conditional (get_inner_function (),
-				    loc.get_inner_location (),
-				    boolval.get_inner_rvalue (),
-				    on_true.get_inner_label (),
-				    on_false.get_inner_label ());
-}
-
-inline label
-function::add_label (const std::string &name,
-		     location loc)
+block::add_assignment_op (lvalue lvalue,
+			  enum gcc_jit_binary_op op,
+			  rvalue rvalue,
+			  location loc)
 {
-  return label (gcc_jit_function_add_label (get_inner_function (),
-					    loc.get_inner_location (),
-					    name.c_str ()));
+  gcc_jit_block_add_assignment_op (get_inner_block (),
+				   loc.get_inner_location (),
+				   lvalue.get_inner_lvalue (),
+				   op,
+				   rvalue.get_inner_rvalue ());
 }
 
-inline label
-function::add_label (location loc)
+inline void
+block::add_comment (const std::string &text,
+		    location loc)
 {
-  return label (gcc_jit_function_add_label (get_inner_function (),
-					    loc.get_inner_location (),
-					    NULL));
+  gcc_jit_block_add_comment (get_inner_block (),
+			     loc.get_inner_location (),
+			     text.c_str ());
 }
 
 inline void
-function::place_forward_label (label lab,
-			       location loc)
+block::end_with_conditional (rvalue boolval,
+			     block on_true,
+			     block on_false,
+			     location loc)
 {
-  gcc_jit_function_place_forward_label (get_inner_function (),
-					loc.get_inner_location (),
-					lab.get_inner_label ());
+  gcc_jit_block_end_with_conditional (get_inner_block (),
+				      loc.get_inner_location (),
+				      boolval.get_inner_rvalue (),
+				      on_true.get_inner_block (),
+				      on_false.get_inner_block ());
 }
 
 inline void
-function::add_jump (label target,
-		    location loc)
+block::end_with_jump (block target,
+		      location loc)
 {
-  gcc_jit_function_add_jump (get_inner_function (),
-			     loc.get_inner_location (),
-			     target.get_inner_label ());
+  gcc_jit_block_end_with_jump (get_inner_block (),
+			       loc.get_inner_location (),
+			       target.get_inner_block ());
 }
 
 inline void
-function::add_return (rvalue rvalue,
-		      location loc)
+block::end_with_return (rvalue rvalue,
+			location loc)
 {
-  gcc_jit_function_add_return (get_inner_function (),
-			       loc.get_inner_location (),
-			       rvalue.get_inner_rvalue ());
+  gcc_jit_block_end_with_return (get_inner_block (),
+				 loc.get_inner_location (),
+				 rvalue.get_inner_rvalue ());
 }
 
 inline void
-function::add_return (location loc)
+block::end_with_return (location loc)
 {
-  gcc_jit_function_add_void_return (get_inner_function (),
-				    loc.get_inner_location ());
+  gcc_jit_block_end_with_void_return (get_inner_block (),
+				      loc.get_inner_location ());
 }
 
 inline rvalue
-function::add_call (function other,
-		    location loc)
+block::add_call (function other,
+		 location loc)
 {
   rvalue c = get_context ().new_call (other, loc);
   add_eval (c);
   return c;
 }
 inline rvalue
-function::add_call (function other,
-		    rvalue arg0,
-		    location loc)
+block::add_call (function other,
+		 rvalue arg0,
+		 location loc)
 {
   rvalue c = get_context ().new_call (other, arg0, loc);
   add_eval (c);
   return c;
 }
 inline rvalue
-function::add_call (function other,
-		    rvalue arg0, rvalue arg1,
-		    location loc)
+block::add_call (function other,
+		 rvalue arg0, rvalue arg1,
+		 location loc)
 {
   rvalue c = get_context ().new_call (other, arg0, arg1, loc);
   add_eval (c);
   return c;
 }
 inline rvalue
-function::add_call (function other,
-		    rvalue arg0, rvalue arg1, rvalue arg2,
-		    location loc)
+block::add_call (function other,
+		 rvalue arg0, rvalue arg1, rvalue arg2,
+		 location loc)
 {
   rvalue c = get_context ().new_call (other, arg0, arg1, arg2, loc);
   add_eval (c);
@@ -1279,17 +1247,17 @@  function::operator() (rvalue arg0, rvalue arg1,
 				  loc);
 }
 
-// class label
-inline label::label () : object (NULL) {}
-inline label::label (gcc_jit_label *inner)
-  : object (gcc_jit_label_as_object (inner))
+// class block
+inline block::block () : object (NULL) {}
+inline block::block (gcc_jit_block *inner)
+  : object (gcc_jit_block_as_object (inner))
 {}
 
-inline gcc_jit_label *
-label::get_inner_label () const
+inline gcc_jit_block *
+block::get_inner_block () const
 {
   /* Manual downcast: */
-  return reinterpret_cast<gcc_jit_label *> (get_inner_object ());
+  return reinterpret_cast<gcc_jit_block *> (get_inner_object ());
 }
 
 //  class rvalue
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index 3493518..1146261 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -56,7 +56,7 @@  struct gcc_jit_function : public gcc::jit::recording::function
 {
 };
 
-struct gcc_jit_label : public gcc::jit::recording::label
+struct gcc_jit_block : public gcc::jit::recording::block
 {
 };
 
@@ -72,10 +72,6 @@  struct gcc_jit_param : public gcc::jit::recording::param
 {
 };
 
-struct gcc_jit_loop : public gcc::jit::recording::loop
-{
-};
-
 /**********************************************************************
  Error-handling.
 
@@ -204,22 +200,28 @@  struct gcc_jit_loop : public gcc::jit::recording::loop
       }								\
   JIT_END_STMT
 
-/* Check that FUNC is non-NULL, and that it's OK to add statements to
+/* Check that BLOCK is non-NULL, and that it's OK to add statements to
    it.  */
-#define RETURN_IF_NOT_FUNC_DEFINITION(FUNC) \
+#define RETURN_IF_NOT_VALID_BLOCK(BLOCK) \
   JIT_BEGIN_STMT							\
-    RETURN_IF_FAIL ((FUNC), NULL, "NULL function");			\
-    RETURN_IF_FAIL ((FUNC)->get_kind () != GCC_JIT_FUNCTION_IMPORTED,	\
-		    NULL,						\
-		    "Cannot add code to an imported function");	\
+    RETURN_IF_FAIL ((BLOCK), NULL, "NULL block");			\
+    RETURN_IF_FAIL_PRINTF2 (						\
+      !(BLOCK)->has_been_terminated (),				\
+      (BLOCK)->get_context (),						\
+      "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_FUNC_DEFINITION(FUNC) \
+#define RETURN_NULL_IF_NOT_VALID_BLOCK(BLOCK) \
   JIT_BEGIN_STMT							\
-    RETURN_NULL_IF_FAIL ((FUNC), NULL, "NULL function");		\
-    RETURN_NULL_IF_FAIL ((FUNC)->get_kind () != GCC_JIT_FUNCTION_IMPORTED,\
-			 NULL,						\
-			 "Cannot add code to an imported function");	\
+    RETURN_NULL_IF_FAIL ((BLOCK), NULL, "NULL block");			\
+    RETURN_NULL_IF_FAIL_PRINTF2 (					\
+      !(BLOCK)->has_been_terminated (),				\
+      (BLOCK)->get_context (),						\
+      "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
@@ -568,22 +570,25 @@  gcc_jit_function_get_param (gcc_jit_function *func, int index)
   return static_cast <gcc_jit_param *> (func->get_param (index));
 }
 
-gcc_jit_label*
-gcc_jit_function_new_forward_label (gcc_jit_function *func,
-				    const char *name)
+gcc_jit_block*
+gcc_jit_function_new_block (gcc_jit_function *func,
+			    const char *name)
 {
   RETURN_NULL_IF_FAIL (func, NULL, "NULL function");
+  RETURN_NULL_IF_FAIL (func->get_kind () != GCC_JIT_FUNCTION_IMPORTED,
+		       func->get_context (),
+		       "cannot add block to an imported function");
   /* name can be NULL.  */
 
-  return (gcc_jit_label *)func->new_forward_label (name);
+  return (gcc_jit_block *)func->new_block (name);
 }
 
 gcc_jit_object *
-gcc_jit_label_as_object (gcc_jit_label *label)
+gcc_jit_block_as_object (gcc_jit_block *block)
 {
-  RETURN_NULL_IF_FAIL (label, NULL, "NULL label");
+  RETURN_NULL_IF_FAIL (block, NULL, "NULL block");
 
-  return static_cast <gcc_jit_object *> (label->as_object ());
+  return static_cast <gcc_jit_object *> (block->as_object ());
 }
 
 gcc_jit_lvalue *
@@ -895,6 +900,9 @@  gcc_jit_rvalue_dereference_field (gcc_jit_rvalue *ptr,
   RETURN_NULL_IF_FAIL (field, NULL, "NULL field");
   gcc::jit::recording::type *underlying_type =
     ptr->get_type ()->dereference ();
+  RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt,
+			       "field %s has not been placed in a struct",
+			       field->get_debug_string ());
   RETURN_NULL_IF_FAIL_PRINTF3 (
     underlying_type, ptr->m_ctxt,
     "dereference of non-pointer %s (type: %s) when accessing ->%s",
@@ -956,49 +964,26 @@  gcc_jit_function_new_local (gcc_jit_function *func,
   return (gcc_jit_lvalue *)func->new_local (loc, type, name);
 }
 
-gcc_jit_label *
-gcc_jit_function_add_label (gcc_jit_function *func,
-			    gcc_jit_location *loc,
-			    const char *name)
-{
-  RETURN_NULL_IF_NOT_FUNC_DEFINITION (func);
-  /* loc and name can be NULL.  */
-
-  return (gcc_jit_label *)func->add_label (loc, name);
-}
-
 void
-gcc_jit_function_place_forward_label (gcc_jit_function *func,
-				      gcc_jit_location *loc,
-				      gcc_jit_label *lab)
-{
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
-  RETURN_IF_FAIL (lab, ctxt, "NULL label");
-
-  func->place_forward_label (loc, lab);
-}
-
-void
-gcc_jit_function_add_eval (gcc_jit_function *func,
-			   gcc_jit_location *loc,
-			   gcc_jit_rvalue *rvalue)
+gcc_jit_block_add_eval (gcc_jit_block *block,
+			gcc_jit_location *loc,
+			gcc_jit_rvalue *rvalue)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (rvalue, ctxt, "NULL rvalue");
 
-  return func->add_eval (loc, rvalue);
+  return block->add_eval (loc, rvalue);
 }
 
 void
-gcc_jit_function_add_assignment (gcc_jit_function *func,
-				 gcc_jit_location *loc,
-				 gcc_jit_lvalue *lvalue,
-				 gcc_jit_rvalue *rvalue)
+gcc_jit_block_add_assignment (gcc_jit_block *block,
+			      gcc_jit_location *loc,
+			      gcc_jit_lvalue *lvalue,
+			      gcc_jit_rvalue *rvalue)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (lvalue, ctxt, "NULL lvalue");
   RETURN_IF_FAIL (rvalue, ctxt, "NULL rvalue");
   RETURN_IF_FAIL_PRINTF4 (
@@ -1012,23 +997,23 @@  gcc_jit_function_add_assignment (gcc_jit_function *func,
     rvalue->get_debug_string (),
     rvalue->get_type ()->get_debug_string ());
 
-  return func->add_assignment (loc, lvalue, rvalue);
+  return block->add_assignment (loc, lvalue, rvalue);
 }
 
 void
-gcc_jit_function_add_assignment_op (gcc_jit_function *func,
-				    gcc_jit_location *loc,
-				    gcc_jit_lvalue *lvalue,
-				    enum gcc_jit_binary_op op,
-				    gcc_jit_rvalue *rvalue)
+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_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (lvalue, ctxt, "NULL lvalue");
   /* FIXME: op is checked by new_binary_op */
   RETURN_IF_FAIL (rvalue, ctxt, "NULL rvalue");
 
-  return func->add_assignment_op (loc, lvalue, op, rvalue);
+  return block->add_assignment_op (loc, lvalue, op, rvalue);
 }
 
 static bool
@@ -1041,14 +1026,14 @@  is_bool (gcc_jit_rvalue *boolval)
 }
 
 void
-gcc_jit_function_add_conditional (gcc_jit_function *func,
-				  gcc_jit_location *loc,
-				  gcc_jit_rvalue *boolval,
-				  gcc_jit_label *on_true,
-				  gcc_jit_label *on_false)
+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_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (boolval, ctxt, "NULL boolval");
   RETURN_IF_FAIL_PRINTF2 (
    is_bool (boolval), ctxt,
@@ -1056,42 +1041,73 @@  gcc_jit_function_add_conditional (gcc_jit_function *func,
    boolval->get_debug_string (),
    boolval->get_type ()->get_debug_string ());
   RETURN_IF_FAIL (on_true, ctxt, "NULL on_true");
-  /* on_false can be NULL */
+  RETURN_IF_FAIL (on_true, ctxt, "NULL on_false");
+  RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == on_true->get_function (),
+    ctxt,
+    "\"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,
+    "\"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 func->add_conditional (loc, boolval, on_true, on_false);
+  return block->end_with_conditional (loc, boolval, on_true, on_false);
 }
 
 void
-gcc_jit_function_add_comment (gcc_jit_function *func,
-			      gcc_jit_location *loc,
-			      const char *text)
+gcc_jit_block_add_comment (gcc_jit_block *block,
+			   gcc_jit_location *loc,
+			   const char *text)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (text, ctxt, "NULL text");
 
-  func->add_comment (loc, text);
+  block->add_comment (loc, text);
 }
 
 void
-gcc_jit_function_add_jump (gcc_jit_function *func,
-			gcc_jit_location *loc,
-			gcc_jit_label *target)
+gcc_jit_block_end_with_jump (gcc_jit_block *block,
+			     gcc_jit_location *loc,
+			     gcc_jit_block *target)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
   RETURN_IF_FAIL (target, ctxt, "NULL target");
+  RETURN_IF_FAIL_PRINTF4 (
+    block->get_function () == target->get_function (),
+    ctxt,
+    "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 ());
 
-  func->add_jump (loc, target);
+  block->end_with_jump (loc, target);
 }
 
 void
-gcc_jit_function_add_return (gcc_jit_function *func,
-			     gcc_jit_location *loc,
-			     gcc_jit_rvalue *rvalue)
+gcc_jit_block_end_with_return (gcc_jit_block *block,
+			       gcc_jit_location *loc,
+			       gcc_jit_rvalue *rvalue)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  gcc::jit::recording::context *ctxt = block->get_context ();
+  gcc::jit::recording::function *func = block->get_function ();
   RETURN_IF_FAIL (rvalue, ctxt, "NULL rvalue");
   RETURN_IF_FAIL_PRINTF4 (
     compatible_types (
@@ -1105,15 +1121,16 @@  gcc_jit_function_add_return (gcc_jit_function *func,
     func->get_debug_string (),
     func->get_return_type ()->get_debug_string ());
 
-  return func->add_return (loc, rvalue);
+  return block->end_with_return (loc, rvalue);
 }
 
 void
-gcc_jit_function_add_void_return (gcc_jit_function *func,
-				  gcc_jit_location *loc)
+gcc_jit_block_end_with_void_return (gcc_jit_block *block,
+				    gcc_jit_location *loc)
 {
-  RETURN_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
+  RETURN_IF_NOT_VALID_BLOCK (block);
+  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,
@@ -1122,81 +1139,7 @@  gcc_jit_function_add_void_return (gcc_jit_function *func,
     func->get_debug_string (),
     func->get_return_type ()->get_debug_string ());
 
-  return func->add_return (loc, NULL);
-}
-
-gcc_jit_loop *
-gcc_jit_function_new_loop (gcc_jit_function *func,
-			   gcc_jit_location *loc,
-			   gcc_jit_rvalue *boolval)
-{
-  RETURN_NULL_IF_NOT_FUNC_DEFINITION (func);
-  gcc::jit::recording::context *ctxt = func->m_ctxt;
-  RETURN_NULL_IF_FAIL (boolval, ctxt, "NULL boolval");
-  RETURN_NULL_IF_FAIL_PRINTF2 (
-   is_bool (boolval), ctxt,
-   "%s (type: %s) is not of boolean type ",
-   boolval->get_debug_string (),
-   boolval->get_type ()->get_debug_string ());
-
-  return (gcc_jit_loop *)func->new_loop (loc, boolval, NULL, NULL);
-}
-
-gcc_jit_loop *
-gcc_jit_function_new_loop_over_range (gcc_jit_function *func,
-				      gcc_jit_location *loc,
-				      gcc_jit_lvalue *iteration_var,
-				      gcc_jit_rvalue *start_of_range,
-				      gcc_jit_rvalue *upper_bound_of_range,
-				      gcc_jit_rvalue *step)
-{
-  RETURN_NULL_IF_NOT_FUNC_DEFINITION (func);
-  gcc_jit_context *ctxt = static_cast <gcc_jit_context *> (func->m_ctxt);
-  RETURN_NULL_IF_FAIL (iteration_var, ctxt, "NULL iteration_var");
-  RETURN_NULL_IF_FAIL (upper_bound_of_range, ctxt,
-		       "NULL upper_bound_of_range");
-  if (!start_of_range)
-    start_of_range =
-      gcc_jit_context_zero (ctxt,
-			    static_cast <gcc_jit_type *> (iteration_var->get_type ()));
-
-  if (!step)
-    step = gcc_jit_context_one (ctxt,
-				static_cast <gcc_jit_type *> (iteration_var->get_type ()));
-
-  /* "iteration_var = start_of_range;" */
-  gcc_jit_function_add_assignment (func,
-				   loc,
-				   iteration_var,
-				   start_of_range);
-
-  /* "(iteration_var < upper_bound_of_range)" */
-  gcc_jit_rvalue *boolval =
-    gcc_jit_context_new_comparison (ctxt,
-				    loc,
-				    GCC_JIT_COMPARISON_LT,
-				    gcc_jit_lvalue_as_rvalue (iteration_var),
-				    upper_bound_of_range);
-
-  /* the += is added when loop_end is called.  */
-  return (gcc_jit_loop *)func->new_loop (loc, boolval, iteration_var, step);
-}
-
-gcc_jit_object *
-gcc_jit_loop_as_object (gcc_jit_loop *loop)
-{
-  RETURN_NULL_IF_FAIL (loop, NULL, "NULL loop");
-
-  return static_cast <gcc_jit_object *> (loop->as_object ());
-}
-
-void
-gcc_jit_loop_end (gcc_jit_loop *loop,
-		  gcc_jit_location *loc)
-{
-  RETURN_IF_FAIL (loop, NULL, "NULL loop");
-
-  loop->end (loc);
+  return block->end_with_return (loc, NULL);
 }
 
 /**********************************************************************
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 9fe9b61..c24fddd 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -39,11 +39,10 @@  typedef struct gcc_jit_result gcc_jit_result;
 	    +- gcc_jit_struct
          +- gcc_jit_field
          +- gcc_jit_function
-         +- gcc_jit_label
+         +- gcc_jit_block
          +- gcc_jit_rvalue
              +- gcc_jit_lvalue
                  +- gcc_jit_param
-         +- gcc_jit_loop
 */
 typedef struct gcc_jit_object gcc_jit_object;
 
@@ -77,8 +76,23 @@  typedef struct gcc_jit_struct gcc_jit_struct;
    linking to within the rest of the process.  */
 typedef struct gcc_jit_function gcc_jit_function;
 
-/* A gcc_jit_label is a jump target within a function, for control flow.  */
-typedef struct gcc_jit_label gcc_jit_label;
+/* A gcc_jit_block encapsulates a "basic block" of statements within a
+   function (i.e. with one entry point and one exit point).
+
+   Every block within a function must be terminated with a conditional,
+   a branch, or a return.
+
+   The blocks within a function form a directed graph.
+
+   The entrypoint to the function is the first block created within
+   it.
+
+   All of the blocks in a function must be reachable via some path from
+   the first block.
+
+   It's OK to have more than one "return" from a function (i.e. multiple
+   blocks that terminate by returning).  */
+typedef struct gcc_jit_block gcc_jit_block;
 
 /* A gcc_jit_rvalue is an expression within your code, with some type.  */
 typedef struct gcc_jit_rvalue gcc_jit_rvalue;
@@ -93,10 +107,6 @@  typedef struct gcc_jit_lvalue gcc_jit_lvalue;
    rvalue); use gcc_jit_param_as_lvalue to convert.  */
 typedef struct gcc_jit_param gcc_jit_param;
 
-/* A gcc_jit_loop is a pre-canned way of creating loops without needing
-   to manually manage gcc_jit_label instances.  */
-typedef struct gcc_jit_loop gcc_jit_loop;
-
 /*
    Acquire a JIT-compilation context.
 
@@ -269,11 +279,7 @@  gcc_jit_result_release (gcc_jit_result *result);
  copied, so you don't need to keep them around.  Note that this *isn't*
  the case for other parts of the API.
 
- You create code by adding a sequence of statements to a function.
- Control flow is expressed using labels, rather than by explicitly
- managing blocks.   If you have blocks, you can simply create labels
- for the start of each block.  You can even create labels for every
- instruction in your language; unused labels will be optimized away.
+ You create code by adding a sequence of statements to blocks.
 **********************************************************************/
 
 /**********************************************************************
@@ -484,17 +490,18 @@  gcc_jit_function_as_object (gcc_jit_function *func);
 extern gcc_jit_param *
 gcc_jit_function_get_param (gcc_jit_function *func, int index);
 
-/* Create a label, to be placed later.
+/* Create a block.
 
    The name can be NULL, or you can give it a meaningful name, which
-   may show up in dumps of the internal representation.  */
-extern gcc_jit_label *
-gcc_jit_function_new_forward_label (gcc_jit_function *func,
-				    const char *name);
+   may show up in dumps of the internal representation, and in error
+   messages.  */
+extern gcc_jit_block *
+gcc_jit_function_new_block (gcc_jit_function *func,
+			    const char *name);
 
-/* Upcasting from label to object.  */
+/* Upcasting from block to object.  */
 extern gcc_jit_object *
-gcc_jit_label_as_object (gcc_jit_label *label);
+gcc_jit_block_as_object (gcc_jit_block *block);
 
 /**********************************************************************
  lvalues, rvalues and expressions.
@@ -744,9 +751,9 @@  gcc_jit_function_new_local (gcc_jit_function *func,
      (void)expression;
 */
 extern void
-gcc_jit_function_add_eval (gcc_jit_function *func,
-			   gcc_jit_location *loc,
-			   gcc_jit_rvalue *rvalue);
+gcc_jit_block_add_eval (gcc_jit_block *block,
+			gcc_jit_location *loc,
+			gcc_jit_rvalue *rvalue);
 
 /* Add evaluation of an rvalue, assigning the result to the given
    lvalue.
@@ -756,10 +763,10 @@  gcc_jit_function_add_eval (gcc_jit_function *func,
      lvalue = rvalue;
 */
 extern void
-gcc_jit_function_add_assignment (gcc_jit_function *func,
-				 gcc_jit_location *loc,
-				 gcc_jit_lvalue *lvalue,
-				 gcc_jit_rvalue *rvalue);
+gcc_jit_block_add_assignment (gcc_jit_block *block,
+			      gcc_jit_location *loc,
+			      gcc_jit_lvalue *lvalue,
+			      gcc_jit_rvalue *rvalue);
 
 /* Add evaluation of an rvalue, using the result to modify an
    lvalue.
@@ -771,11 +778,11 @@  gcc_jit_function_add_assignment (gcc_jit_function *func,
      lvalue /= rvalue;
    etc  */
 extern void
-gcc_jit_function_add_assignment_op (gcc_jit_function *func,
-				    gcc_jit_location *loc,
-				    gcc_jit_lvalue *lvalue,
-				    enum gcc_jit_binary_op op,
-				    gcc_jit_rvalue *rvalue);
+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);
 
 /* Add a no-op textual comment to the internal representation of the
    code.  It will be optimized away, but will be visible in the dumps
@@ -786,12 +793,12 @@  gcc_jit_function_add_assignment_op (gcc_jit_function *func,
    and thus may be of use when debugging how your project's internal
    representation gets converted to the libgccjit IR.  */
 extern void
-gcc_jit_function_add_comment (gcc_jit_function *func,
-			      gcc_jit_location *loc,
-			      const char *text);
+gcc_jit_block_add_comment (gcc_jit_block *block,
+			   gcc_jit_location *loc,
+			   const char *text);
 
-/* Add evaluation of an rvalue, branching on the result to the
-   appropriate label.
+/* Terminate a block by adding evaluation of an rvalue, branching on the
+   result to the appropriate successor block.
 
    This is roughly equivalent to this C code:
 
@@ -800,119 +807,46 @@  gcc_jit_function_add_comment (gcc_jit_function *func,
      else
        goto on_false;
 
-   on_true must be non-NULL.
-
-   on_false may be NULL, in which case this is roughly equivalent to:
-
-     if (boolval)
-       goto on_true;
-*/
-extern void
-gcc_jit_function_add_conditional (gcc_jit_function *func,
-				  gcc_jit_location *loc,
-				  gcc_jit_rvalue *boolval,
-				  gcc_jit_label *on_true,
-				  gcc_jit_label *on_false);
-
-/* Add a label at the current location in the function.
-
-   This is roughly equivalent to this C code:
-
-      name:
-
-   The name can be NULL, or you can give it a meaningful name, which
-   may show up in dumps of the internal representation.  */
-extern gcc_jit_label *
-gcc_jit_function_add_label (gcc_jit_function *func,
-			    gcc_jit_location *loc,
-			    const char *name);
-
-/* Place a pre-existing label at the current location in the function.
-
-   This is roughly equivalent to this C code:
-
-      name:
-
-   The label must not have already been placed.  */
+   block, boolval, on_true, and on_false must be non-NULL.  */
 extern void
-gcc_jit_function_place_forward_label (gcc_jit_function *func,
-				      gcc_jit_location *loc,
-				      gcc_jit_label *lab);
+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);
 
-/* Add a jump from the current location in the function to the given
-   label.
+/* Terminate a block by adding a jump to the given target block.
 
    This is roughly equivalent to this C code:
 
       goto target;
 */
 extern void
-gcc_jit_function_add_jump (gcc_jit_function *func,
-			   gcc_jit_location *loc,
-			   gcc_jit_label *target);
+gcc_jit_block_end_with_jump (gcc_jit_block *block,
+			     gcc_jit_location *loc,
+			     gcc_jit_block *target);
 
-/* Add evaluation of an rvalue, returning the value.
+/* Terminate a block by adding evaluation of an rvalue, returning the value.
 
    This is roughly equivalent to this C code:
 
       return expression;
 */
 extern void
-gcc_jit_function_add_return (gcc_jit_function *func,
-			     gcc_jit_location *loc,
-			     gcc_jit_rvalue *rvalue);
+gcc_jit_block_end_with_return (gcc_jit_block *block,
+			       gcc_jit_location *loc,
+			       gcc_jit_rvalue *rvalue);
 
-/* Add a valueless return, for use within a function with
-   "void" return type.
+/* Terminate a block by adding a valueless return, for use within a function
+   with "void" return type.
 
    This is equivalent to this C code:
 
       return;
 */
 extern void
-gcc_jit_function_add_void_return (gcc_jit_function *func,
-				  gcc_jit_location *loc);
-
-/* Helper function for creating a loop:
-      while (boolval)
-	{
-	   BODY;
-	}
-
-  Statements will be added to the body of the loop until
-  gcc_jit_loop_end is called.  */
-extern gcc_jit_loop *
-gcc_jit_function_new_loop (gcc_jit_function *func,
-			   gcc_jit_location *loc,
-			   gcc_jit_rvalue *boolval);
-
-/* Helper function for creating a loop of this form:
-      for (i = start_of_range; i < upper_bound_of_range; i += step)
-	{
-	   BODY;
-	}
-
-  Statements will be added to the body of the loop until
-  gcc_jit_loop_end is called.
-
-  "start_of_range" can be NULL, in which case 0 is used.
-  "upper_bound_of_range" must be non-NULL.
-  "step" can be NULL, in which case 1 is used.  */
-extern gcc_jit_loop *
-gcc_jit_function_new_loop_over_range (gcc_jit_function *func,
-				      gcc_jit_location *loc,
-				      gcc_jit_lvalue *iteration_var,
-				      gcc_jit_rvalue *start_of_range,
-				      gcc_jit_rvalue *upper_bound_of_range,
-				      gcc_jit_rvalue *step);
-
-/* Upcasting from loop to object.  */
-extern gcc_jit_object *
-gcc_jit_loop_as_object (gcc_jit_loop *loop);
-
-extern void
-gcc_jit_loop_end (gcc_jit_loop *loop,
-		  gcc_jit_location *loc);
+gcc_jit_block_end_with_void_return (gcc_jit_block *block,
+				    gcc_jit_location *loc);
 
 /**********************************************************************
  Nested contexts.
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 13d9619..48fd9d2 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -2,6 +2,15 @@ 
 {
   global:
     # Keep this list sorted alphabetically:
+    gcc_jit_block_add_assignment;
+    gcc_jit_block_add_assignment_op;
+    gcc_jit_block_add_comment;
+    gcc_jit_block_add_eval;
+    gcc_jit_block_as_object;
+    gcc_jit_block_end_with_conditional;
+    gcc_jit_block_end_with_jump;
+    gcc_jit_block_end_with_return;
+    gcc_jit_block_end_with_void_return;
     gcc_jit_context_acquire;
     gcc_jit_context_compile;
     gcc_jit_context_dump_to_file;
@@ -35,26 +44,11 @@ 
     gcc_jit_context_set_str_option;
     gcc_jit_context_zero;
     gcc_jit_field_as_object;
-    gcc_jit_function_add_assignment;
-    gcc_jit_function_add_assignment_op;
-    gcc_jit_function_add_comment;
-    gcc_jit_function_add_conditional;
-    gcc_jit_function_add_eval;
-    gcc_jit_function_add_jump;
-    gcc_jit_function_add_label;
-    gcc_jit_function_add_return;
-    gcc_jit_function_add_void_return;
     gcc_jit_function_as_object;
     gcc_jit_function_get_param;
-    gcc_jit_function_new_forward_label;
+    gcc_jit_function_new_block;
     gcc_jit_function_new_local;
-    gcc_jit_function_new_loop;
-    gcc_jit_function_new_loop_over_range;
-    gcc_jit_function_place_forward_label;
-    gcc_jit_label_as_object;
     gcc_jit_location_as_object;
-    gcc_jit_loop_as_object;
-    gcc_jit_loop_end;
     gcc_jit_lvalue_as_object;
     gcc_jit_lvalue_as_rvalue;
     gcc_jit_lvalue_access_field;
diff --git a/gcc/testsuite/ChangeLog.jit b/gcc/testsuite/ChangeLog.jit
index b8619e6..ea8566c 100644
--- a/gcc/testsuite/ChangeLog.jit
+++ b/gcc/testsuite/ChangeLog.jit
@@ -1,3 +1,50 @@ 
+2014-02-27  David Malcolm  <dmalcolm@redhat.com>
+
+	* jit.dg/test-accessing-struct.c (create_code): Port to
+	block-based API.
+	* jit.dg/test-calling-external-function.c (create_code): Likewise.
+	* jit.dg/test-error-accessing-field-in-other-struct.c (create_code):
+	Likewise.
+	* jit.dg/test-error-call-with-mismatching-args.c (create_code):
+	Likewise.
+	* jit.dg/test-error-call-with-not-enough-args.c (create_code):
+	Likewise.
+	* jit.dg/test-error-call-with-too-many-args.c (create_code):
+	Likewise.
+	* jit.dg/test-error-dereference-field-of-non-pointer.c (create_code):
+	Likewise.
+	* jit.dg/test-error-dereference-read: Likewise.
+	* jit.dg/test-error-mismatching-types-in-assignment.c: Likewise.
+	* jit.dg/test-error-return-within-void-function.c: Likewise.
+	* jit.dg/test-expressions.c: Likewise.
+	* jit.dg/test-factorial.c: Likewise.
+	* jit.dg/test-functions.c: Likewise.
+	* jit.dg/test-fuzzer.c: Likewise.
+	* jit.dg/test-hello-world.c (create_code): Likewise.
+	* jit.dg/test-nested-contexts.c: Likewise.
+	* jit.dg/test-operator-overloading.cc: Likewise.
+	* jit.dg/test-quadratic.c: Likewise.
+	* jit.dg/test-quadratic.cc: Likewise.
+	* jit.dg/test-reading-struct.c (create_code): Likewise.
+	* jit.dg/test-string-literal.c (create_code): Likewise.
+	* jit.dg/test-sum-of-squares.c (create_code): Likewise.
+	* jit.dg/test-types.c (create_code): Likewise.
+	* jit.dg/test-using-global.c (create_code): Likewise.
+
+	* jit.dg/test-arrays.c (create_code): Likewise, eliminating use of
+	loop API.
+	* jit.dg/test-dot-product.c (create_code): Likewise.
+	* jit.dg/test-linked-list.c (create_code): Likewise.
+
+	* jit.dg/test-error-adding-to-terminated-block.c: New testcase.
+	* jit.dg/test-error-block-in-wrong-function.c: Likewise.
+	* jit.dg/test-error-missing-return.c: Likewise.
+	* jit.dg/test-error-unreachable-block.c: Likewise.
+	* jit.dg/test-error-unterminated-block.c: Likewise.
+
+	* jit.dg/test-error-label-already-placed.c: Delete obsolete testcase.
+	* jit.dg/test-error-unplaced-label.c: Likewise.
+
 2014-02-25  David Malcolm  <dmalcolm@redhat.com>
 
 	* jit.dg/test-functions.c (create_use_of_void_return): New, to add
diff --git a/gcc/testsuite/jit.dg/test-accessing-struct.c b/gcc/testsuite/jit.dg/test-accessing-struct.c
index eb4ecca..109a0d7 100644
--- a/gcc/testsuite/jit.dg/test-accessing-struct.c
+++ b/gcc/testsuite/jit.dg/test-accessing-struct.c
@@ -76,14 +76,16 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	y)));
 
   /* f->z = ... */
-  gcc_jit_function_add_assignment (
-    test_fn,
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_assignment (
+    block,
     NULL,
     gcc_jit_rvalue_dereference_field (
       gcc_jit_param_as_rvalue (param_f),
       NULL,
       z),
     sum);
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-arrays.c b/gcc/testsuite/jit.dg/test-arrays.c
index 12ea77e..378a2a3 100644
--- a/gcc/testsuite/jit.dg/test-arrays.c
+++ b/gcc/testsuite/jit.dg/test-arrays.c
@@ -74,27 +74,40 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				  1, &param_ah,
 				  0);
 
+  gcc_jit_block *initial = gcc_jit_function_new_block (func, "initial");
+  gcc_jit_block *loop_test = gcc_jit_function_new_block (func, "loop_test");
+  gcc_jit_block *loop_body = gcc_jit_function_new_block (func, "loop_body");
+  gcc_jit_block *final = gcc_jit_function_new_block (func, "final");
+
   /* "ah->m_before = 4.0f;" */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_rvalue_dereference_field (
       gcc_jit_param_as_rvalue (param_ah), NULL, field_m_before),
     gcc_jit_context_new_rvalue_from_int (ctxt, float_type, 4));
 
-  gcc_jit_function_add_comment (func, NULL,
-				"for i in 0 to (ARRAY_SIZE - 1):");
+  gcc_jit_block_add_comment (initial, NULL,
+			     "for i in 0 to (ARRAY_SIZE - 1):");
   gcc_jit_lvalue *i =
     gcc_jit_function_new_local (func, NULL, int_type, "i");
-  gcc_jit_loop *loop =
-    gcc_jit_function_new_loop_over_range (
-      func, NULL, i,
-      NULL,
-      gcc_jit_context_new_rvalue_from_int (ctxt, int_type, ARRAY_SIZE),
-      NULL);
+  gcc_jit_block_add_assignment (initial, NULL,
+      i,
+      gcc_jit_context_zero (ctxt, int_type));
 
-  gcc_jit_function_add_comment (func, NULL, "ah->m_ints[i] = (i * i);");
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_end_with_jump (initial, NULL, loop_test);
+
+  gcc_jit_block_end_with_conditional (loop_test, NULL,
+    gcc_jit_context_new_comparison (
+      ctxt, NULL,
+      GCC_JIT_COMPARISON_LT,
+      gcc_jit_lvalue_as_rvalue (i),
+      gcc_jit_context_new_rvalue_from_int (ctxt, int_type, ARRAY_SIZE)),
+    loop_body,
+    final);
+
+  gcc_jit_block_add_comment (loop_body, NULL, "ah->m_ints[i] = (i * i);");
+  gcc_jit_block_add_assignment (
+    loop_body, NULL,
     gcc_jit_context_new_array_access (
       ctxt, NULL,
       gcc_jit_lvalue_as_rvalue (gcc_jit_rvalue_dereference_field (
@@ -109,14 +122,22 @@  create_code (gcc_jit_context *ctxt, void *user_data)
       gcc_jit_lvalue_as_rvalue (i),
       gcc_jit_lvalue_as_rvalue (i)));
 
-  gcc_jit_loop_end (loop, NULL);
+  /* "i++" */
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
+    i,
+    GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_one (ctxt, int_type));
+
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_test);
 
  /* ah->m_after = 2.0f; */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    final, NULL,
     gcc_jit_rvalue_dereference_field (
       gcc_jit_param_as_rvalue (param_ah), NULL, field_m_after),
     gcc_jit_context_new_rvalue_from_int (ctxt, float_type, 2));
+  gcc_jit_block_end_with_void_return (final, NULL);
 
 }
 
diff --git a/gcc/testsuite/jit.dg/test-calling-external-function.c b/gcc/testsuite/jit.dg/test-calling-external-function.c
index 9bf2e7e..177f329 100644
--- a/gcc/testsuite/jit.dg/test-calling-external-function.c
+++ b/gcc/testsuite/jit.dg/test-calling-external-function.c
@@ -73,13 +73,14 @@  create_code (gcc_jit_context *ctxt, void *user_data)
           ctxt,
           int_type,
           (i + 3) ));
-
-  gcc_jit_function_add_eval (
-    test_fn, NULL,
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
     gcc_jit_context_new_call (ctxt,
                               NULL,
                               called_fn,
                               3, args));
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 static int called_with[3];
diff --git a/gcc/testsuite/jit.dg/test-dot-product.c b/gcc/testsuite/jit.dg/test-dot-product.c
index e78546c..2cde66d 100644
--- a/gcc/testsuite/jit.dg/test-dot-product.c
+++ b/gcc/testsuite/jit.dg/test-dot-product.c
@@ -42,30 +42,42 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				  "my_dot_product",
 				  3, params, 0);
 
+  gcc_jit_block *initial = gcc_jit_function_new_block (func, "initial");
+  gcc_jit_block *loop_test = gcc_jit_function_new_block (func, "loop_test");
+  gcc_jit_block *loop_body = gcc_jit_function_new_block (func, "loop_body");
+  gcc_jit_block *final = gcc_jit_function_new_block (func, "final");
+
   /* Build: "double result = 0.;" */
   gcc_jit_lvalue *result =
     gcc_jit_function_new_local (func, NULL, val_type, "result");
 
-  gcc_jit_function_add_assignment (func, NULL,
-    result,
-    gcc_jit_context_zero (ctxt, val_type));
+  gcc_jit_block_add_assignment (initial, NULL,
+    result, gcc_jit_context_zero (ctxt, val_type));
 
   /* Build: "for (int i = 0; i < n; i++)" */
   gcc_jit_lvalue *i =
     gcc_jit_function_new_local (func, NULL, int_type, "i");
-  gcc_jit_function_add_assignment (func, NULL,
+  gcc_jit_block_add_assignment (initial, NULL,
     i, gcc_jit_context_zero (ctxt, int_type));
-  gcc_jit_loop *loop = gcc_jit_function_new_loop (func, NULL,
+
+  gcc_jit_block_end_with_jump (initial, NULL, loop_test);
+
+  gcc_jit_block_end_with_conditional (
+    loop_test, NULL,
+
     /* (i < n) */
     gcc_jit_context_new_comparison (
       ctxt, NULL,
       GCC_JIT_COMPARISON_LT,
       gcc_jit_lvalue_as_rvalue (i),
-      gcc_jit_param_as_rvalue (param_n)));
+      gcc_jit_param_as_rvalue (param_n)),
+
+    loop_body,
+    final);
 
   /* Build: "result += a[i] * b[i];" */
-  gcc_jit_function_add_assignment_op (
-    func, NULL,
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
     result,
     GCC_JIT_BINARY_OP_PLUS,
     gcc_jit_context_new_binary_op (
@@ -84,17 +96,17 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	  gcc_jit_lvalue_as_rvalue (i)))));
 
   /* Build: "i++" */
-  gcc_jit_function_add_assignment_op (
-    func, NULL,
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
     i,
     GCC_JIT_BINARY_OP_PLUS,
     gcc_jit_context_one (ctxt, int_type));
 
-  gcc_jit_loop_end (loop, NULL);
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_test);
 
   /* Build: "return result;" */
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    final,
     NULL,
     gcc_jit_lvalue_as_rvalue (result));
 }
diff --git a/gcc/testsuite/jit.dg/test-error-accessing-field-in-other-struct.c b/gcc/testsuite/jit.dg/test-error-accessing-field-in-other-struct.c
index 1cc2cb6..f10954b 100644
--- a/gcc/testsuite/jit.dg/test-error-accessing-field-in-other-struct.c
+++ b/gcc/testsuite/jit.dg/test-error-accessing-field-in-other-struct.c
@@ -60,11 +60,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                int_type,
                                "q");
   /* We don't actually need a gcc_jit_type for "struct bar" for the test.  */
-#if 0
   gcc_jit_field *bar_fields[] = {p, q};
-  gcc_jit_struct *struct_bar =
-    gcc_jit_context_new_struct_type (ctxt, NULL, "foo", 2, bar_fields);
-#endif
+  (void)gcc_jit_context_new_struct_type (ctxt, NULL, "foo", 2, bar_fields);
 
   gcc_jit_type *foo_ptr =
     gcc_jit_type_get_pointer (gcc_jit_struct_as_type (struct_foo));
@@ -95,10 +92,13 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	NULL,
 	q));
 
-  gcc_jit_function_add_assignment (
-    test_fn,
+  gcc_jit_block *block =
+    gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_assignment (
+    block,
     NULL,
     lvalue, rvalue);
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-error-adding-to-terminated-block.c b/gcc/testsuite/jit.dg/test-error-adding-to-terminated-block.c
new file mode 100644
index 0000000..9c39dd6
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-adding-to-terminated-block.c
@@ -0,0 +1,48 @@ 
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+       void
+       test_fn ()
+       {
+	 return;
+	 return;
+       }
+  */
+  gcc_jit_type *void_t =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Build the test_fn.  */
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_t,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "initial");
+
+  gcc_jit_block_end_with_void_return (initial, NULL);
+  /* Error: "initial" has already been terminated.  */
+  gcc_jit_block_end_with_void_return (initial, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      "gcc_jit_block_end_with_void_return:"
+		      " adding to terminated block:"
+		      " initial (already terminated by: return;)");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c b/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
new file mode 100644
index 0000000..b7342a3
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
@@ -0,0 +1,65 @@ 
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+       void
+       test_fn ()
+       {
+	 goto label;
+       }
+
+       void
+       other_fn ()
+       {
+         label:
+       };
+     where the destination block is in another function.
+  */
+  gcc_jit_type *void_t =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Build the test_fn.  */
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_t,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+  /* Build the other_fn.  */
+  gcc_jit_function *other_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_t,
+                                  "other_fn",
+                                  0, NULL,
+                                  0);
+
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block *block_within_other_fn =
+    gcc_jit_function_new_block (other_fn, "block_within_other_fn");
+
+  gcc_jit_block_end_with_jump (initial, NULL, block_within_other_fn);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      "gcc_jit_block_end_with_jump:"
+		      " target block is not in same function:"
+		      " source block initial is in function test_fn"
+		      " whereas target block block_within_other_fn"
+		      " is in function other_fn");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-call-with-mismatching-args.c b/gcc/testsuite/jit.dg/test-error-call-with-mismatching-args.c
index 4f0b105..7306ffd 100644
--- a/gcc/testsuite/jit.dg/test-error-call-with-mismatching-args.c
+++ b/gcc/testsuite/jit.dg/test-error-call-with-mismatching-args.c
@@ -60,13 +60,15 @@  create_code (gcc_jit_context *ctxt, void *user_data)
   gcc_jit_rvalue *arg =
     gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 42);
 
-  gcc_jit_function_add_eval (
-    test_fn, NULL,
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  gcc_jit_block_add_eval (
+    block, NULL,
     gcc_jit_context_new_call (ctxt,
                               NULL,
                               called_fn,
                               1, &arg));
   /* the above has the wrong type for argument 1.  */
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-error-call-with-not-enough-args.c b/gcc/testsuite/jit.dg/test-error-call-with-not-enough-args.c
index 4cfa09e..1e8dbbc 100644
--- a/gcc/testsuite/jit.dg/test-error-call-with-not-enough-args.c
+++ b/gcc/testsuite/jit.dg/test-error-call-with-not-enough-args.c
@@ -54,15 +54,16 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   "test_caller",
                                   0, NULL,
                                   0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
   /* called_function ();  */
-  gcc_jit_function_add_eval (
-    test_fn, NULL,
+  gcc_jit_block_add_eval (
+    block, NULL,
     gcc_jit_context_new_call (ctxt,
                               NULL,
                               called_fn,
                               0, NULL));
   /* the above has the wrong arg count.  */
-
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 extern void
diff --git a/gcc/testsuite/jit.dg/test-error-call-with-too-many-args.c b/gcc/testsuite/jit.dg/test-error-call-with-too-many-args.c
index bbfb99f..a50fc97 100644
--- a/gcc/testsuite/jit.dg/test-error-call-with-too-many-args.c
+++ b/gcc/testsuite/jit.dg/test-error-call-with-too-many-args.c
@@ -55,16 +55,17 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   "test_caller",
                                   1, &param_a,
                                   0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
   /* called_function (a);  */
   gcc_jit_rvalue *arg = gcc_jit_param_as_rvalue (param_a);
-  gcc_jit_function_add_eval (
-    test_fn, NULL,
+  gcc_jit_block_add_eval (
+    block, NULL,
     gcc_jit_context_new_call (ctxt,
                               NULL,
                               called_fn,
                               1, &arg));
   /* the above has the wrong arg count.  */
-
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 extern void
diff --git a/gcc/testsuite/jit.dg/test-error-dereference-field-of-non-pointer.c b/gcc/testsuite/jit.dg/test-error-dereference-field-of-non-pointer.c
index 287498d..75171d7 100644
--- a/gcc/testsuite/jit.dg/test-error-dereference-field-of-non-pointer.c
+++ b/gcc/testsuite/jit.dg/test-error-dereference-field-of-non-pointer.c
@@ -56,6 +56,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				gcc_jit_struct_as_type (struct_foo),
 				"tmp");
 
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+
   /* Erroneous: tmp->x = ... */
   gcc_jit_lvalue *lvalue =
     gcc_jit_rvalue_dereference_field (
@@ -71,10 +73,12 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	NULL,
 	y));
 
-  gcc_jit_function_add_assignment (
-    test_fn,
+  gcc_jit_block_add_assignment (
+    block,
     NULL,
     lvalue, rvalue);
+
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-error-dereference-read-of-non-pointer.c b/gcc/testsuite/jit.dg/test-error-dereference-read-of-non-pointer.c
index c9ae40d..7b97ab1 100644
--- a/gcc/testsuite/jit.dg/test-error-dereference-read-of-non-pointer.c
+++ b/gcc/testsuite/jit.dg/test-error-dereference-read-of-non-pointer.c
@@ -31,9 +31,10 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   "test_bogus_dereference_read",
                                   1, &param_i,
                                   0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
   /* Erroneous: "return *i;" */
-  gcc_jit_function_add_return (
-    test_fn,
+  gcc_jit_block_end_with_return (
+    block,
     NULL,
     gcc_jit_lvalue_as_rvalue (
       gcc_jit_rvalue_dereference (
diff --git a/gcc/testsuite/jit.dg/test-error-label-already-placed.c b/gcc/testsuite/jit.dg/test-error-label-already-placed.c
deleted file mode 100644
index a20134b..0000000
--- a/gcc/testsuite/jit.dg/test-error-label-already-placed.c
+++ /dev/null
@@ -1,59 +0,0 @@ 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "libgccjit.h"
-
-#include "harness.h"
-
-void
-create_code (gcc_jit_context *ctxt, void *user_data)
-{
-  /* Let's try to inject the equivalent of:
-
-      int
-      test_fn (void)
-      {
-	foo:
-	   return 0;
-
-	foo:
-	   return 1;
-
-      }
-
-     and verify that the 2nd attempt to place the label fails.
-   */
-  gcc_jit_type *int_type =
-    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
-
-  gcc_jit_function *func =
-    gcc_jit_context_new_function (ctxt, NULL,
-				  GCC_JIT_FUNCTION_EXPORTED,
-				  int_type,
-				  "test_fn",
-				  0, NULL, 0);
-
-  /* Create forward label: */
-  gcc_jit_label *label =
-    gcc_jit_function_new_forward_label (func, "foo");
-
-  gcc_jit_function_place_forward_label (func, NULL, label);
-  gcc_jit_function_add_return (func, NULL,
-			       gcc_jit_context_zero (ctxt, int_type));
-
-  /* Erroneous 2nd placement of label: */
-  gcc_jit_function_place_forward_label (func, NULL, label);
-  gcc_jit_function_add_return (func, NULL,
-			       gcc_jit_context_one (ctxt, int_type));
-}
-
-void
-verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
-{
-  CHECK_VALUE (result, NULL);
-
-  /* Verify that the correct error message was emitted.  */
-  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
-		      "label foo has already been placed");
-}
diff --git a/gcc/testsuite/jit.dg/test-error-mismatching-types-in-assignment.c b/gcc/testsuite/jit.dg/test-error-mismatching-types-in-assignment.c
index 58b1650..83d212d 100644
--- a/gcc/testsuite/jit.dg/test-error-mismatching-types-in-assignment.c
+++ b/gcc/testsuite/jit.dg/test-error-mismatching-types-in-assignment.c
@@ -36,11 +36,14 @@  create_code (gcc_jit_context *ctxt, void *user_data)
     gcc_jit_function_new_local (
       test_fn, NULL, int_type, "i");
 
-  gcc_jit_function_add_assignment (
-    test_fn, NULL,
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+
+  gcc_jit_block_add_assignment (
+    block, NULL,
     i, /* of type int */
     gcc_jit_context_new_string_literal (
       ctxt, "this is not an int"));
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
@@ -50,7 +53,7 @@  verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
 
   /* Verify that the correct error message was emitted.  */
   CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
-		      "gcc_jit_function_add_assignment:"
+		      "gcc_jit_block_add_assignment:"
 		      " mismatching types:"
 		      " assignment to i (type: int)"
 		      " from \"this is not an int\" (type: const char *)");
diff --git a/gcc/testsuite/jit.dg/test-error-missing-return.c b/gcc/testsuite/jit.dg/test-error-missing-return.c
new file mode 100644
index 0000000..5e24346
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-missing-return.c
@@ -0,0 +1,40 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+       int
+       test_fn ()
+       {
+       }
+     and verify that the API complains about the lack of
+     a returned value.
+  */
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  (void)gcc_jit_context_new_function (ctxt, NULL,
+				      GCC_JIT_FUNCTION_EXPORTED,
+				      int_type,
+				      "test_fn",
+				      0, NULL,
+				      0);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      "function test_fn returns non-void (type: int)"
+		      " but has no blocks");
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-return-within-void-function.c b/gcc/testsuite/jit.dg/test-error-return-within-void-function.c
index 3abc188..1b0e9c8 100644
--- a/gcc/testsuite/jit.dg/test-error-return-within-void-function.c
+++ b/gcc/testsuite/jit.dg/test-error-return-within-void-function.c
@@ -30,9 +30,11 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   "test_fn",
                                   0, NULL,
                                   0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+
   /* "return 42;"  (i.e. non-void) */
-  gcc_jit_function_add_return (
-    test_fn, NULL,
+  gcc_jit_block_end_with_return (
+    block, NULL,
     gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 42));
 }
 
@@ -45,7 +47,7 @@  verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
 
   /* Verify that the correct error message was emitted.  */
   CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
-		      "gcc_jit_function_add_return:"
+		      "gcc_jit_block_end_with_return:"
 		      " mismatching types: return of (int)42 (type: int)"
 		      " in function test_fn (return type: void)");
 }
diff --git a/gcc/testsuite/jit.dg/test-error-unplaced-label.c b/gcc/testsuite/jit.dg/test-error-unplaced-label.c
deleted file mode 100644
index 464fdfc..0000000
--- a/gcc/testsuite/jit.dg/test-error-unplaced-label.c
+++ /dev/null
@@ -1,50 +0,0 @@ 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "libgccjit.h"
-
-#include "harness.h"
-
-void
-create_code (gcc_jit_context *ctxt, void *user_data)
-{
-  /* Let's try to inject the equivalent of:
-
-      void
-      test_fn (void)
-      {
-        goto foo;
-      }
-
-     and verify that an error is issued due to label "foo" not being
-     placed.
-   */
-  gcc_jit_type *void_type =
-    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
-
-  gcc_jit_function *func =
-    gcc_jit_context_new_function (ctxt, NULL,
-				  GCC_JIT_FUNCTION_EXPORTED,
-				  void_type,
-				  "test_fn",
-				  0, NULL, 0);
-
-  /* Create forward label: */
-  gcc_jit_label *unplaced_label =
-    gcc_jit_function_new_forward_label (func, "foo");
-
-  /* Use it (but never place it): */
-  gcc_jit_function_add_jump (func, NULL, unplaced_label);
-}
-
-void
-verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
-{
-  CHECK_VALUE (result, NULL);
-
-  /* Verify that the correct error message was emitted.  */
-  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
-		      "unplaced label within test_fn: foo");
-}
-
diff --git a/gcc/testsuite/jit.dg/test-error-unreachable-block.c b/gcc/testsuite/jit.dg/test-error-unreachable-block.c
new file mode 100644
index 0000000..10806bd
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-unreachable-block.c
@@ -0,0 +1,50 @@ 
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+       void
+       test_fn ()
+       {
+	 return;
+
+	 return;
+       }
+     where the second block is unreachable.
+  */
+  gcc_jit_type *void_t =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Build the test_fn.  */
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_t,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "a");
+  gcc_jit_block *unreachable =
+    gcc_jit_function_new_block (test_fn, "b");
+
+  gcc_jit_block_end_with_void_return (initial, NULL);
+
+  gcc_jit_block_end_with_void_return (unreachable, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      "unreachable block: b");
+}
diff --git a/gcc/testsuite/jit.dg/test-error-unterminated-block.c b/gcc/testsuite/jit.dg/test-error-unterminated-block.c
new file mode 100644
index 0000000..491f09f
--- /dev/null
+++ b/gcc/testsuite/jit.dg/test-error-unterminated-block.c
@@ -0,0 +1,42 @@ 
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+       void
+       test_fn ()
+       {
+         initial:
+       }
+     with an unterminated block.
+  */
+  gcc_jit_type *void_t =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+
+  /* Build the test_fn.  */
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  void_t,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+  (void)gcc_jit_function_new_block (test_fn, "initial");
+  /* Error: the above block isn't terminated.  */
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+		      "unterminated block in test_fn: initial");
+}
diff --git a/gcc/testsuite/jit.dg/test-expressions.c b/gcc/testsuite/jit.dg/test-expressions.c
index d2fd950..e339d8a 100644
--- a/gcc/testsuite/jit.dg/test-expressions.c
+++ b/gcc/testsuite/jit.dg/test-expressions.c
@@ -41,7 +41,8 @@  make_test_of_unary_op (gcc_jit_context *ctxt,
     type,
     gcc_jit_param_as_rvalue (param_a));
 
-  gcc_jit_function_add_return (test_fn, NULL, unary_op);
+  gcc_jit_block *initial = gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block_end_with_return (initial, NULL, unary_op);
 
   return gcc_jit_object_get_debug_string (
     gcc_jit_rvalue_as_object (unary_op));
@@ -142,7 +143,8 @@  make_test_of_binary_op (gcc_jit_context *ctxt,
       gcc_jit_param_as_rvalue (param_a),
       gcc_jit_param_as_rvalue (param_b));
 
-  gcc_jit_function_add_return (test_fn, NULL, binary_op);
+  gcc_jit_block *initial = gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block_end_with_return (initial, NULL, binary_op);
 
   return gcc_jit_object_get_debug_string (
     gcc_jit_rvalue_as_object (binary_op));
@@ -347,7 +349,8 @@  make_test_of_comparison (gcc_jit_context *ctxt,
       gcc_jit_param_as_rvalue (param_a),
       gcc_jit_param_as_rvalue (param_b));
 
-  gcc_jit_function_add_return (test_fn, NULL, comparison);
+  gcc_jit_block *initial = gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block_end_with_return (initial, NULL, comparison);
 
   return gcc_jit_object_get_debug_string (
     gcc_jit_rvalue_as_object (comparison));
@@ -483,8 +486,10 @@  make_tests_of_dereferences (gcc_jit_context *ctxt)
 				    "test_dereference_read",
 				    1, &param_ptr,
 				    0);
-    gcc_jit_function_add_return (
-      test_dereference_read,
+    gcc_jit_block *initial =
+      gcc_jit_function_new_block (test_dereference_read, "initial");
+    gcc_jit_block_end_with_return (
+      initial,
       NULL,
       gcc_jit_lvalue_as_rvalue (
 	gcc_jit_rvalue_dereference (
@@ -505,13 +510,16 @@  make_tests_of_dereferences (gcc_jit_context *ctxt)
 				    "test_dereference_write",
 				    2, params,
 				    0);
-    gcc_jit_function_add_assignment (
-      test_dereference_write,
+    gcc_jit_block *initial =
+      gcc_jit_function_new_block (test_dereference_write, "initial");
+    gcc_jit_block_add_assignment (
+      initial,
       NULL,
       gcc_jit_rvalue_dereference (
 	gcc_jit_param_as_rvalue (param_ptr),
 	NULL),
       gcc_jit_param_as_rvalue (param_i));
+    gcc_jit_block_end_with_void_return (initial, NULL);
   }
 }
 
@@ -578,8 +586,10 @@  make_test_of_get_address (gcc_jit_context *ctxt)
 				  "test_get_address",
 				  0, NULL,
 				  0);
-  gcc_jit_function_add_return (
-    test_get_address,
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_get_address, "initial");
+  gcc_jit_block_end_with_return (
+    initial,
     NULL,
     gcc_jit_lvalue_get_address (
       test_global,
diff --git a/gcc/testsuite/jit.dg/test-factorial.c b/gcc/testsuite/jit.dg/test-factorial.c
index d71db9f..eecd831 100644
--- a/gcc/testsuite/jit.dg/test-factorial.c
+++ b/gcc/testsuite/jit.dg/test-factorial.c
@@ -36,16 +36,16 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				  "my_factorial",
 				  1, params, 0);
 
- /* Create forward labels: */
-  gcc_jit_label *label_true =
-    gcc_jit_function_new_forward_label (func, NULL);
-
-  gcc_jit_label *label_false =
-    gcc_jit_function_new_forward_label (func, NULL);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (func, "initial");
+  gcc_jit_block *on_true =
+    gcc_jit_function_new_block (func, "on_true");
+  gcc_jit_block *on_false =
+    gcc_jit_function_new_block (func, "on_false");
 
  /* if (x < 2) */
-  gcc_jit_function_add_conditional (
-    func, NULL,
+  gcc_jit_block_end_with_conditional (
+    initial, NULL,
     gcc_jit_context_new_comparison (
       ctxt, NULL,
       GCC_JIT_COMPARISON_LT,
@@ -54,19 +54,17 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	ctxt,
 	the_type,
 	2)),
-    label_true,
-    label_false);
+    on_true,
+    on_false);
 
   /* true branch: */
-  gcc_jit_function_place_forward_label (func, NULL, label_true);
   /* return x */
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    on_true,
     NULL,
     gcc_jit_param_as_rvalue (x));
 
   /* false branch: */
-  gcc_jit_function_place_forward_label (func, NULL, label_false);
   gcc_jit_rvalue *x_minus_1 =
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
@@ -76,8 +74,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	ctxt,
 	the_type,
 	1));
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    on_false,
     NULL,
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
diff --git a/gcc/testsuite/jit.dg/test-fibonacci.c b/gcc/testsuite/jit.dg/test-fibonacci.c
index 63e9bbb..03c13c6 100644
--- a/gcc/testsuite/jit.dg/test-fibonacci.c
+++ b/gcc/testsuite/jit.dg/test-fibonacci.c
@@ -50,16 +50,16 @@  FIRST_LINE + 7: }
 				  "my_fibonacci",
 				  1, params, 0);
 
- /* Create forward labels: */
-  gcc_jit_label *label_true =
-    gcc_jit_function_new_forward_label (func, NULL);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (func, "initial");
+  gcc_jit_block *on_true =
+    gcc_jit_function_new_block (func, "on_true");
+  gcc_jit_block *on_false =
+    gcc_jit_function_new_block (func, "on_false");
 
-  gcc_jit_label *label_false =
-    gcc_jit_function_new_forward_label (func, NULL);
-
- /* if (x < 2) */
-  gcc_jit_function_add_conditional (
-    func,
+  /* if (x < 2) */
+  gcc_jit_block_end_with_conditional (
+    initial,
     gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 3, 19),
     gcc_jit_context_new_comparison (
       ctxt,
@@ -70,25 +70,17 @@  FIRST_LINE + 7: }
 	ctxt,
 	the_type,
 	2)),
-    label_true,
-    label_false);
+    on_true,
+    on_false);
 
   /* true branch: */
-  gcc_jit_function_place_forward_label (
-    func,
-    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 4, 21),
-    label_true);
   /* return x */
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    on_true,
     gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 4, 21),
     gcc_jit_param_as_rvalue (x));
 
   /* false branch: */
-  gcc_jit_function_place_forward_label (
-    func,
-    gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 21),
-    label_false);
   gcc_jit_rvalue *x_minus_1 =
     gcc_jit_context_new_binary_op (
       ctxt,
@@ -109,8 +101,8 @@  FIRST_LINE + 7: }
 	ctxt,
 	the_type,
 	2));
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    on_false,
     gcc_jit_context_new_location (ctxt, __FILE__, FIRST_LINE + 6, 21),
     gcc_jit_context_new_binary_op (
       ctxt,
diff --git a/gcc/testsuite/jit.dg/test-functions.c b/gcc/testsuite/jit.dg/test-functions.c
index d82995c..a42f304 100644
--- a/gcc/testsuite/jit.dg/test-functions.c
+++ b/gcc/testsuite/jit.dg/test-functions.c
@@ -53,7 +53,9 @@  create_test_of_builtin_strcmp (gcc_jit_context *ctxt)
     gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (call)),
     "strcmp (a, b)");
 
-  gcc_jit_function_add_return (test_fn, NULL, call);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block_end_with_return (initial, NULL, call);
 }
 
 static void
@@ -109,7 +111,9 @@  create_test_of_builtin_trig (gcc_jit_context *ctxt)
     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);
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "initial");
+  gcc_jit_block_end_with_return (initial, NULL, ret);
 }
 
 static void
@@ -128,9 +132,7 @@  create_use_of_void_return (gcc_jit_context *ctxt)
        {
          *out = 1;
 	 return;
-	 *out = 2;
        }
-     where the second assignment is unreachable.
   */
   gcc_jit_type *void_t =
     gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
@@ -149,19 +151,16 @@  create_use_of_void_return (gcc_jit_context *ctxt)
                                   "test_of_void_return",
                                   1, &param_out,
                                   0);
-  gcc_jit_function_add_assignment (
-    test_fn, NULL,
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_fn, "initial");
+
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     /* "*out = ..." */
     gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (param_out),
 				NULL),
     gcc_jit_context_one (ctxt, int_t));
-  gcc_jit_function_add_void_return (test_fn, NULL);
-  gcc_jit_function_add_assignment (
-    test_fn, NULL,
-    /* "*out = ..." */
-    gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (param_out),
-				NULL),
-    gcc_jit_context_new_rvalue_from_int (ctxt, int_t, 2));
+  gcc_jit_block_end_with_void_return (initial, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-fuzzer.c b/gcc/testsuite/jit.dg/test-fuzzer.c
index 11c1db5..8cd0d75 100644
--- a/gcc/testsuite/jit.dg/test-fuzzer.c
+++ b/gcc/testsuite/jit.dg/test-fuzzer.c
@@ -61,6 +61,8 @@  typedef struct function_fuzzer
   int num_locals;
   gcc_jit_lvalue **locals;
 
+  gcc_jit_block *block;
+
 } function_fuzzer;
 
 static void
@@ -274,6 +276,7 @@  make_random_function (fuzzer *f)
       ff->num_params,
       ff->params,
       fuzzer_randrange (f, 0, 1));
+  ff->block = gcc_jit_function_new_block (ff->fn, NULL);
 
   /* Create locals.  */
   if (kind != GCC_JIT_FUNCTION_IMPORTED)
@@ -301,6 +304,9 @@  make_random_function (fuzzer *f)
 	function_fuzzer_add_stmt (ff);
     }
 
+  gcc_jit_block_end_with_return (ff->block, NULL, get_random_rvalue (ff, 3));
+
+
   gcc_jit_function *result = ff->fn;
 
   free (ff->params);
@@ -313,13 +319,13 @@  make_random_function (fuzzer *f)
 
 static void function_fuzzer_add_stmt (function_fuzzer *ff)
 {
-  gcc_jit_function_add_eval (ff->fn,
-			     get_random_location (ff->f),
-			     get_random_rvalue (ff, 4));
-  gcc_jit_function_add_assignment (ff->fn,
-				   get_random_location (ff->f),
-				   get_random_lvalue (ff, 4),
-				   get_random_rvalue (ff, 4));
+  gcc_jit_block_add_eval (ff->block,
+			  get_random_location (ff->f),
+			  get_random_rvalue (ff, 4));
+  gcc_jit_block_add_assignment (ff->block,
+				get_random_location (ff->f),
+				get_random_lvalue (ff, 4),
+				get_random_rvalue (ff, 4));
   /* TODO: place more kinds of statement */
   /* TODO: labels  */
 }
diff --git a/gcc/testsuite/jit.dg/test-hello-world.c b/gcc/testsuite/jit.dg/test-hello-world.c
index 5778f78..93f737b 100644
--- a/gcc/testsuite/jit.dg/test-hello-world.c
+++ b/gcc/testsuite/jit.dg/test-hello-world.c
@@ -44,16 +44,19 @@  create_code (gcc_jit_context *ctxt, void *user_data)
   args[0] = gcc_jit_context_new_string_literal (ctxt, "hello %s\n");
   args[1] = gcc_jit_param_as_rvalue (param_name);
 
-  gcc_jit_function_add_comment (
-    func, NULL,
+  gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
+
+  gcc_jit_block_add_comment (
+    block, NULL,
     "a test comment");
 
-  gcc_jit_function_add_eval (
-    func, NULL,
+  gcc_jit_block_add_eval (
+    block, NULL,
     gcc_jit_context_new_call (ctxt,
                               NULL,
                               printf_func,
                               2, args));
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 extern void
diff --git a/gcc/testsuite/jit.dg/test-linked-list.c b/gcc/testsuite/jit.dg/test-linked-list.c
index b925b9e..28871d9 100644
--- a/gcc/testsuite/jit.dg/test-linked-list.c
+++ b/gcc/testsuite/jit.dg/test-linked-list.c
@@ -60,24 +60,31 @@  create_code (gcc_jit_context *ctxt, void *user_data)
   gcc_jit_lvalue *total =
     gcc_jit_function_new_local (fn, NULL, t_int, "total");
 
+  gcc_jit_block *initial = gcc_jit_function_new_block (fn, "initial");
+  gcc_jit_block *loop_test = gcc_jit_function_new_block (fn, "loop_test");
+  gcc_jit_block *loop_body = gcc_jit_function_new_block (fn, "loop_body");
+  gcc_jit_block *final = gcc_jit_function_new_block (fn, "final");
+
   /* total = 0; */
-  gcc_jit_function_add_assignment (
-    fn, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     total,
     gcc_jit_context_zero (ctxt, t_int));
+  gcc_jit_block_end_with_jump (initial, NULL, loop_test);
 
   /* while (n) */
-  gcc_jit_loop *loop =
-    gcc_jit_function_new_loop (
-      fn, NULL,
-      gcc_jit_context_new_comparison (ctxt, NULL,
-				      GCC_JIT_COMPARISON_NE,
-				      gcc_jit_param_as_rvalue (param_n),
-				      gcc_jit_context_null (ctxt, t_node_ptr)));
+  gcc_jit_block_end_with_conditional (
+    loop_test, NULL,
+    gcc_jit_context_new_comparison (ctxt, NULL,
+				    GCC_JIT_COMPARISON_NE,
+				    gcc_jit_param_as_rvalue (param_n),
+				    gcc_jit_context_null (ctxt, t_node_ptr)),
+    loop_body,
+    final);
 
   /* total += n->value; */
-  gcc_jit_function_add_assignment_op (
-    fn, NULL,
+  gcc_jit_block_add_assignment_op (
+    loop_body, NULL,
     total,
     GCC_JIT_BINARY_OP_PLUS,
     gcc_jit_lvalue_as_rvalue (
@@ -87,8 +94,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	f_value)));
 
   /* n = n->next; */
-  gcc_jit_function_add_assignment (
-    fn, NULL,
+  gcc_jit_block_add_assignment (
+    loop_body, NULL,
     gcc_jit_param_as_lvalue (param_n),
     gcc_jit_lvalue_as_rvalue (
       gcc_jit_rvalue_dereference_field (
@@ -96,10 +103,11 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	NULL,
 	f_next)));
 
-  gcc_jit_loop_end (loop, NULL);
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_test);
 
   /* return total; */
-  gcc_jit_function_add_return (fn, NULL, gcc_jit_lvalue_as_rvalue (total));
+  gcc_jit_block_end_with_return (
+    final, NULL, gcc_jit_lvalue_as_rvalue (total));
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-nested-contexts.c b/gcc/testsuite/jit.dg/test-nested-contexts.c
index d24a859..e34d2dd 100644
--- a/gcc/testsuite/jit.dg/test-nested-contexts.c
+++ b/gcc/testsuite/jit.dg/test-nested-contexts.c
@@ -73,6 +73,7 @@  struct top_level
   gcc_jit_rvalue *zero;
 
   gcc_jit_type *int_type;
+  gcc_jit_type *void_type;
 
   /* "struct quadratic" */
   gcc_jit_type *struct_quadratic;
@@ -110,6 +111,8 @@  make_types (struct top_level *top_level)
 
   top_level->int_type =
     gcc_jit_context_get_type (top_level->ctxt, GCC_JIT_TYPE_INT);
+  top_level->void_type =
+    gcc_jit_context_get_type (top_level->ctxt, GCC_JIT_TYPE_VOID);
 
   top_level->a =
     gcc_jit_context_new_field (top_level->ctxt,
@@ -169,12 +172,14 @@  make_calc_discriminant (struct top_level *top_level,
   middle_level->calc_discriminant =
     gcc_jit_context_new_function (middle_level->ctxt, NULL,
 				  GCC_JIT_FUNCTION_EXPORTED,
-				  top_level->numeric_type,
+				  top_level->void_type,
 				  "calc_discriminant",
 				  1, &param_q,
 				  0);
-  gcc_jit_function_add_comment (
-    middle_level->calc_discriminant, NULL,
+  gcc_jit_block *blk =
+    gcc_jit_function_new_block (middle_level->calc_discriminant, NULL);
+  gcc_jit_block_add_comment (
+    blk, NULL,
     "(b^2 - 4ac)");
 
   gcc_jit_rvalue *q_a =
@@ -193,8 +198,8 @@  make_calc_discriminant (struct top_level *top_level,
 	  gcc_jit_param_as_rvalue (param_q),
 	  NULL, top_level->c));
 
-  gcc_jit_function_add_assignment (
-    middle_level->calc_discriminant, NULL,
+  gcc_jit_block_add_assignment (
+    blk, NULL,
 
     /* q->discriminant =...  */
     gcc_jit_rvalue_dereference_field (
@@ -231,6 +236,8 @@  make_calc_discriminant (struct top_level *top_level,
 	  GCC_JIT_BINARY_OP_MULT,
 	  top_level->numeric_type,
 	  q_a, q_c)))); /* end of gcc_jit_function_add_assignment call.  */
+
+  gcc_jit_block_end_with_void_return (blk, NULL);
 }
 
 static void
@@ -268,51 +275,56 @@  make_test_quadratic (struct top_level *top_level,
       test_quadratic, NULL,
       top_level->struct_quadratic,
       "q");
+
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_quadratic,
+				"initial");
+  gcc_jit_block *on_positive_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "positive_discriminant");
+
+  gcc_jit_block *on_nonpositive_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "nonpositive_discriminant");
+
+  gcc_jit_block *on_zero_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "zero_discriminant");
+
+  gcc_jit_block *on_negative_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "negative_discriminant");
+
+  /* Initial block.  */
   /* q.a = a; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, top_level->a),
     gcc_jit_param_as_rvalue (a));
   /* q.b = b; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, top_level->b),
     gcc_jit_param_as_rvalue (b));
   /* q.c = c; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, top_level->c),
     gcc_jit_param_as_rvalue (c));
   /* calc_discriminant (&q); */
   gcc_jit_rvalue *address_of_q = gcc_jit_lvalue_get_address (q, NULL);
-  gcc_jit_function_add_eval (
-    test_quadratic, NULL,
+  gcc_jit_block_add_eval (
+    initial, NULL,
     gcc_jit_context_new_call (
       bottom_level->ctxt, NULL,
       middle_level->calc_discriminant,
       1, &address_of_q));
 
-  gcc_jit_label *on_positive_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "positive_discriminant");
-
-  gcc_jit_label *on_nonpositive_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "nonpositive_discriminant");
-
-  gcc_jit_label *on_zero_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "zero_discriminant");
-
-  gcc_jit_label *on_negative_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "negative_discriminant");
-
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    initial, NULL,
     "if (q.discriminant > 0)");
-  gcc_jit_function_add_conditional (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_conditional (
+    initial, NULL,
     gcc_jit_context_new_comparison (
       bottom_level->ctxt, NULL,
       GCC_JIT_COMPARISON_GT,
@@ -324,9 +336,7 @@  make_test_quadratic (struct top_level *top_level,
     on_positive_discriminant,
     on_nonpositive_discriminant);
 
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_positive_discriminant);
+  /* Block: "on_positive_discriminant" */
   /* double s = sqrt (q.discriminant); */
   gcc_jit_lvalue *s = gcc_jit_function_new_local (
     test_quadratic, NULL,
@@ -336,8 +346,8 @@  make_test_quadratic (struct top_level *top_level,
     gcc_jit_rvalue_access_field (gcc_jit_lvalue_as_rvalue (q),
 				 NULL,
 				 top_level->discriminant);
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
     s,
     gcc_jit_context_new_call (
       bottom_level->ctxt, NULL,
@@ -361,11 +371,11 @@  make_test_quadratic (struct top_level *top_level,
 	2),
       gcc_jit_param_as_rvalue (a));
 
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    on_positive_discriminant, NULL,
     "*r1 = (-b + s) / (2 * a);");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
 
     /* "*r1 = ..." */
     gcc_jit_rvalue_dereference (
@@ -384,11 +394,11 @@  make_test_quadratic (struct top_level *top_level,
 	gcc_jit_lvalue_as_rvalue (s)),
       two_a));
 
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    on_positive_discriminant, NULL,
     "*r2 = (-b - s) / (2 * a)");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
 
     /* "*r2 = ..." */
     gcc_jit_rvalue_dereference (
@@ -408,22 +418,19 @@  make_test_quadratic (struct top_level *top_level,
       two_a));
 
   /* "return 2;" */
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_return (
+    on_positive_discriminant, NULL,
     gcc_jit_context_new_rvalue_from_int (
       bottom_level->ctxt,
       top_level->int_type,
       2));
 
-  /* "else if (q.discriminant == 0)" */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_nonpositive_discriminant);
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  /* Block: "on_nonpositive_discriminant" */
+  gcc_jit_block_add_comment (
+    on_nonpositive_discriminant, NULL,
     "else if (q.discriminant == 0)");
-  gcc_jit_function_add_conditional (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_conditional (
+    on_nonpositive_discriminant, NULL,
     gcc_jit_context_new_comparison (
       bottom_level->ctxt, NULL,
       GCC_JIT_COMPARISON_EQ,
@@ -435,16 +442,12 @@  make_test_quadratic (struct top_level *top_level,
     on_zero_discriminant,
     on_negative_discriminant);
 
-  /* if (q.discriminant == 0) */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_zero_discriminant);
-
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  /* Block: "on_zero_discriminant" */
+  gcc_jit_block_add_comment (
+    on_zero_discriminant, NULL,
     "*r1 = -b / (2 * a);");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_zero_discriminant, NULL,
 
     /* "*r1 = ..." */
     gcc_jit_rvalue_dereference (
@@ -459,16 +462,14 @@  make_test_quadratic (struct top_level *top_level,
       two_a));
 
   /* "return 1;" */
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_return (
+    on_zero_discriminant, NULL,
       gcc_jit_context_one (bottom_level->ctxt, top_level->int_type));
 
-  /* else return 0; */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_negative_discriminant);
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  /* Block: "on_negative_discriminant" */
+  gcc_jit_block_end_with_return (
+    /* else return 0; */
+    on_negative_discriminant, NULL,
     gcc_jit_context_zero (bottom_level->ctxt, top_level->int_type));
 }
 
diff --git a/gcc/testsuite/jit.dg/test-operator-overloading.cc b/gcc/testsuite/jit.dg/test-operator-overloading.cc
index 226cb22..cbb1e98 100644
--- a/gcc/testsuite/jit.dg/test-operator-overloading.cc
+++ b/gcc/testsuite/jit.dg/test-operator-overloading.cc
@@ -70,6 +70,7 @@  struct quadratic_test
   gccjit::rvalue zero;
 
   gccjit::type int_type;
+  gccjit::type void_type;
 
   /* "struct quadratic" */
   gccjit::type quadratic;
@@ -95,6 +96,7 @@  make_types (quadratic_test &testcase)
   testcase.zero = testcase.ctxt.zero (testcase.numeric_type);
 
   testcase.int_type = testcase.ctxt.get_int_type <int> ();
+  testcase.void_type = testcase.ctxt.get_type (GCC_JIT_TYPE_VOID);
 
   testcase.a = testcase.ctxt.new_field (testcase.numeric_type, "a");
   testcase.b = testcase.ctxt.new_field (testcase.numeric_type, "b");
@@ -139,11 +141,12 @@  make_calc_discriminant (quadratic_test &testcase)
   params[0] = param_q;
   testcase.calc_discriminant =
     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
-				testcase.numeric_type,
+				testcase.void_type,
 				"calc_discriminant",
 				params,
 				0);
-  testcase.calc_discriminant.add_comment ("(b^2 - 4ac)");
+  gccjit::block block = testcase.calc_discriminant.new_block ();
+  block.add_comment ("(b^2 - 4ac)");
 
   gccjit::rvalue q_a = param_q.dereference_field (testcase.a);
   gccjit::rvalue q_b = param_q.dereference_field (testcase.b);
@@ -152,11 +155,12 @@  make_calc_discriminant (quadratic_test &testcase)
   gccjit::rvalue four =
     testcase.ctxt.new_rvalue (testcase.numeric_type, 4);
 
-  testcase.calc_discriminant.add_assignment (
+  block.add_assignment (
     /* q->discriminant =...  */
     param_q.dereference_field (testcase.discriminant),
     /* (q->b * q->b) - (4 * q->a * q->c) */
     (q_b * q_b) - (four * q_a * q_c));
+  block.end_with_return ();
 }
 
 static void
@@ -186,41 +190,41 @@  make_test_quadratic (quadratic_test &testcase)
 
   /* struct quadratic q; */
   gccjit::lvalue q = test_quadratic.new_local (testcase.quadratic, "q");
+
+  gccjit::block initial = test_quadratic.new_block ("initial");
+  gccjit::block on_positive_discriminant
+    = test_quadratic.new_block ("positive_discriminant");
+  gccjit::block on_nonpositive_discriminant
+    = test_quadratic.new_block ("nonpositive_discriminant");
+  gccjit::block on_zero_discriminant
+    = test_quadratic.new_block ("zero_discriminant");
+  gccjit::block on_negative_discriminant
+    = test_quadratic.new_block ("negative_discriminant");
+
+  CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
+                      "zero_discriminant");
+
   /* q.a = a; */
-  test_quadratic.add_assignment (q.access_field (testcase.a), a);
+  initial.add_assignment (q.access_field (testcase.a), a);
   /* q.b = b; */
-  test_quadratic.add_assignment (q.access_field (testcase.b), b);
+  initial.add_assignment (q.access_field (testcase.b), b);
   /* q.c = c; */
-  test_quadratic.add_assignment (q.access_field (testcase.c), c);
+  initial.add_assignment (q.access_field (testcase.c), c);
   /* calc_discriminant (&q); */
   gccjit::rvalue address_of_q = q.get_address ();
-  test_quadratic.add_eval (testcase.calc_discriminant (address_of_q));
-
-  gccjit::label on_positive_discriminant
-    = test_quadratic.new_forward_label ("positive_discriminant");
+  initial.add_eval (testcase.calc_discriminant (address_of_q));
 
-  gccjit::label on_nonpositive_discriminant
-    = test_quadratic.new_forward_label ("nonpositive_discriminant");
-
-  gccjit::label on_zero_discriminant
-    = test_quadratic.new_forward_label ("zero_discriminant");
-
-  gccjit::label on_negative_discriminant
-    = test_quadratic.new_forward_label ("negative_discriminant");
-  CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
-                      "zero_discriminant");
-
-  test_quadratic.add_comment ("if (q.discriminant > 0)");
-  test_quadratic.add_conditional (
+  initial.add_comment ("if (q.discriminant > 0)");
+  initial.end_with_conditional (
     q.access_field (testcase.discriminant) > testcase.zero,
     on_positive_discriminant,
     on_nonpositive_discriminant);
 
-  test_quadratic.place_forward_label (on_positive_discriminant);
+  /* Block: "on_positive_discriminant" */
   /* double s = sqrt (q.discriminant); */
   gccjit::lvalue s = test_quadratic.new_local (testcase.numeric_type, "s");
   gccjit::rvalue discriminant_of_q = q.access_field (testcase.discriminant);
-  test_quadratic.add_assignment (s, testcase.sqrt (discriminant_of_q));
+  on_positive_discriminant.add_assignment (s, testcase.sqrt (discriminant_of_q));
 
   gccjit::rvalue minus_b = -b;
   gccjit::rvalue two =
@@ -229,35 +233,35 @@  make_test_quadratic (quadratic_test &testcase)
   CHECK_STRING_VALUE (two_a.get_debug_string ().c_str (),
                       "(double)2 * a");
 
-  test_quadratic.add_comment ("*r1 = (-b + s) / (2 * a);");
-  test_quadratic.add_assignment (*r1, (minus_b + s) / two_a);
+  on_positive_discriminant.add_comment ("*r1 = (-b + s) / (2 * a);");
+  on_positive_discriminant.add_assignment (*r1, (minus_b + s) / two_a);
 
-  test_quadratic.add_comment ("*r2 = (-b - s) / (2 * a)");
-  test_quadratic.add_assignment (*r2, (minus_b - s) / two_a);
+  on_positive_discriminant.add_comment ("*r2 = (-b - s) / (2 * a)");
+  on_positive_discriminant.add_assignment (*r2, (minus_b - s) / two_a);
 
   /* "return 2;" */
-  test_quadratic.add_return (testcase.ctxt.new_rvalue (testcase.int_type, 2));
+  on_positive_discriminant.end_with_return (
+    testcase.ctxt.new_rvalue (testcase.int_type, 2));
 
+  /* Block: "on_nonpositive_discriminant" */
   /* "else if (q.discriminant == 0)" */
-  test_quadratic.place_forward_label (on_nonpositive_discriminant);
-  test_quadratic.add_comment ("else if (q.discriminant == 0)");
-  test_quadratic.add_conditional (
+  on_nonpositive_discriminant.add_comment ("else if (q.discriminant == 0)");
+  on_nonpositive_discriminant.end_with_conditional (
     q.access_field (testcase.discriminant) == testcase.zero,
     on_zero_discriminant,
     on_negative_discriminant);
 
+  /* Block: "on_zero_discriminant" */
   /* if (q.discriminant == 0) */
-  test_quadratic.place_forward_label (on_zero_discriminant);
-
-  test_quadratic.add_comment ("*r1 = -b / (2 * a);");
-  test_quadratic.add_assignment (*r1, minus_b / two_a);
+  on_zero_discriminant.add_comment ("*r1 = -b / (2 * a);");
+  on_zero_discriminant.add_assignment (*r1, minus_b / two_a);
 
   /* "return 1;" */
-  test_quadratic.add_return (testcase.int_type.one ());
+  on_zero_discriminant.end_with_return (testcase.int_type.one ());
 
+  /* Block: "on_negative_discriminant" */
   /* else return 0; */
-  test_quadratic.place_forward_label (on_negative_discriminant);
-  test_quadratic.add_return (testcase.int_type.zero ());
+  on_negative_discriminant.end_with_return (testcase.int_type.zero ());
 
   /* Verify that output stream operator << works.  */
   std::ostringstream os;
diff --git a/gcc/testsuite/jit.dg/test-quadratic.c b/gcc/testsuite/jit.dg/test-quadratic.c
index 337bea5..b984445 100644
--- a/gcc/testsuite/jit.dg/test-quadratic.c
+++ b/gcc/testsuite/jit.dg/test-quadratic.c
@@ -60,6 +60,7 @@  struct quadratic_test
   gcc_jit_rvalue *zero;
 
   gcc_jit_type *int_type;
+  gcc_jit_type *void_type;
 
   /* "struct quadratic" */
   gcc_jit_type *quadratic;
@@ -90,6 +91,9 @@  make_types (struct quadratic_test *testcase)
   testcase->int_type =
     gcc_jit_context_get_type (testcase->ctxt, GCC_JIT_TYPE_INT);
 
+  testcase->void_type =
+    gcc_jit_context_get_type (testcase->ctxt, GCC_JIT_TYPE_VOID);
+
   testcase->a =
     gcc_jit_context_new_field (testcase->ctxt,
 			       NULL,
@@ -146,12 +150,14 @@  make_calc_discriminant (struct quadratic_test *testcase)
   testcase->calc_discriminant =
     gcc_jit_context_new_function (testcase->ctxt, NULL,
 				  GCC_JIT_FUNCTION_EXPORTED,
-				  testcase->numeric_type,
+				  testcase->void_type,
 				  "calc_discriminant",
 				  1, &param_q,
 				  0);
-  gcc_jit_function_add_comment (
-    testcase->calc_discriminant, NULL,
+  gcc_jit_block *blk =
+    gcc_jit_function_new_block (testcase->calc_discriminant, NULL);
+  gcc_jit_block_add_comment (
+    blk, NULL,
     "(b^2 - 4ac)");
 
   gcc_jit_rvalue *q_a =
@@ -170,8 +176,8 @@  make_calc_discriminant (struct quadratic_test *testcase)
 	  gcc_jit_param_as_rvalue (param_q),
 	  NULL, testcase->c));
 
-  gcc_jit_function_add_assignment (
-    testcase->calc_discriminant, NULL,
+  gcc_jit_block_add_assignment (
+    blk, NULL,
 
     /* q->discriminant =...  */
     gcc_jit_rvalue_dereference_field (
@@ -208,6 +214,8 @@  make_calc_discriminant (struct quadratic_test *testcase)
 	  GCC_JIT_BINARY_OP_MULT,
 	  testcase->numeric_type,
 	  q_a, q_c)))); /* end of gcc_jit_function_add_assignment call.  */
+
+  gcc_jit_block_end_with_void_return (blk, NULL);
 }
 
 static void
@@ -236,58 +244,62 @@  make_test_quadratic (struct quadratic_test *testcase)
 				  "test_quadratic",
 				  5, params,
 				  0);
-
   /* struct quadratic q; */
   gcc_jit_lvalue *q =
     gcc_jit_function_new_local (
       test_quadratic, NULL,
       testcase->quadratic,
       "q");
+
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (test_quadratic,
+				"initial");
+  gcc_jit_block *on_positive_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "positive_discriminant");
+
+  gcc_jit_block *on_nonpositive_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "nonpositive_discriminant");
+
+  gcc_jit_block *on_zero_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "zero_discriminant");
+
+  gcc_jit_block *on_negative_discriminant
+    = gcc_jit_function_new_block (test_quadratic,
+				  "negative_discriminant");
+
+  /* Initial block.  */
   /* q.a = a; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, testcase->a),
     gcc_jit_param_as_rvalue (a));
   /* q.b = b; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, testcase->b),
     gcc_jit_param_as_rvalue (b));
   /* q.c = c; */
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     gcc_jit_lvalue_access_field (q, NULL, testcase->c),
     gcc_jit_param_as_rvalue (c));
   /* calc_discriminant (&q); */
   gcc_jit_rvalue *address_of_q = gcc_jit_lvalue_get_address (q, NULL);
-  gcc_jit_function_add_eval (
-    test_quadratic, NULL,
+  gcc_jit_block_add_eval (
+    initial, NULL,
     gcc_jit_context_new_call (
       testcase->ctxt, NULL,
       testcase->calc_discriminant,
       1, &address_of_q));
 
-  gcc_jit_label *on_positive_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "positive_discriminant");
-
-  gcc_jit_label *on_nonpositive_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "nonpositive_discriminant");
-
-  gcc_jit_label *on_zero_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "zero_discriminant");
-
-  gcc_jit_label *on_negative_discriminant
-    = gcc_jit_function_new_forward_label (test_quadratic,
-					  "negative_discriminant");
-
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    initial, NULL,
     "if (q.discriminant > 0)");
-  gcc_jit_function_add_conditional (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_conditional (
+    initial, NULL,
     gcc_jit_context_new_comparison (
       testcase->ctxt, NULL,
       GCC_JIT_COMPARISON_GT,
@@ -299,9 +311,7 @@  make_test_quadratic (struct quadratic_test *testcase)
     on_positive_discriminant,
     on_nonpositive_discriminant);
 
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_positive_discriminant);
+  /* Block: "on_positive_discriminant" */
   /* double s = sqrt (q.discriminant); */
   gcc_jit_lvalue *s = gcc_jit_function_new_local (
     test_quadratic, NULL,
@@ -311,8 +321,8 @@  make_test_quadratic (struct quadratic_test *testcase)
     gcc_jit_rvalue_access_field (gcc_jit_lvalue_as_rvalue (q),
 				 NULL,
 				 testcase->discriminant);
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
     s,
     gcc_jit_context_new_call (
       testcase->ctxt, NULL,
@@ -336,11 +346,11 @@  make_test_quadratic (struct quadratic_test *testcase)
 	2),
       gcc_jit_param_as_rvalue (a));
 
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    on_positive_discriminant, NULL,
     "*r1 = (-b + s) / (2 * a);");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
 
     /* "*r1 = ..." */
     gcc_jit_rvalue_dereference (
@@ -359,11 +369,11 @@  make_test_quadratic (struct quadratic_test *testcase)
 	gcc_jit_lvalue_as_rvalue (s)),
       two_a));
 
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_comment (
+    on_positive_discriminant, NULL,
     "*r2 = (-b - s) / (2 * a)");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_positive_discriminant, NULL,
 
     /* "*r2 = ..." */
     gcc_jit_rvalue_dereference (
@@ -383,22 +393,19 @@  make_test_quadratic (struct quadratic_test *testcase)
       two_a));
 
   /* "return 2;" */
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_return (
+    on_positive_discriminant, NULL,
     gcc_jit_context_new_rvalue_from_int (
       testcase->ctxt,
       testcase->int_type,
       2));
 
-  /* "else if (q.discriminant == 0)" */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_nonpositive_discriminant);
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  /* Block: "on_nonpositive_discriminant" */
+  gcc_jit_block_add_comment (
+    on_nonpositive_discriminant, NULL,
     "else if (q.discriminant == 0)");
-  gcc_jit_function_add_conditional (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_conditional (
+    on_nonpositive_discriminant, NULL,
     gcc_jit_context_new_comparison (
       testcase->ctxt, NULL,
       GCC_JIT_COMPARISON_EQ,
@@ -410,16 +417,12 @@  make_test_quadratic (struct quadratic_test *testcase)
     on_zero_discriminant,
     on_negative_discriminant);
 
-  /* if (q.discriminant == 0) */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_zero_discriminant);
-
-  gcc_jit_function_add_comment (
-    test_quadratic, NULL,
+  /* Block: "on_zero_discriminant" */
+  gcc_jit_block_add_comment (
+    on_zero_discriminant, NULL,
     "*r1 = -b / (2 * a);");
-  gcc_jit_function_add_assignment (
-    test_quadratic, NULL,
+  gcc_jit_block_add_assignment (
+    on_zero_discriminant, NULL,
 
     /* "*r1 = ..." */
     gcc_jit_rvalue_dereference (
@@ -432,18 +435,15 @@  make_test_quadratic (struct quadratic_test *testcase)
       testcase->numeric_type,
       minus_b,
       two_a));
-
-  /* "return 1;" */
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  gcc_jit_block_end_with_return (
+    /* "return 1;" */
+    on_zero_discriminant, NULL,
       gcc_jit_context_one (testcase->ctxt, testcase->int_type));
 
-  /* else return 0; */
-  gcc_jit_function_place_forward_label (
-    test_quadratic, NULL,
-    on_negative_discriminant);
-  gcc_jit_function_add_return (
-    test_quadratic, NULL,
+  /* Block: "on_negative_discriminant" */
+  gcc_jit_block_end_with_return (
+    /* "else return 0;" */
+    on_negative_discriminant, NULL,
     gcc_jit_context_zero (testcase->ctxt, testcase->int_type));
 }
 
diff --git a/gcc/testsuite/jit.dg/test-quadratic.cc b/gcc/testsuite/jit.dg/test-quadratic.cc
index f7333e5..f347669 100644
--- a/gcc/testsuite/jit.dg/test-quadratic.cc
+++ b/gcc/testsuite/jit.dg/test-quadratic.cc
@@ -69,6 +69,7 @@  struct quadratic_test
   gccjit::rvalue zero;
 
   gccjit::type int_type;
+  gccjit::type void_type;
 
   /* "struct quadratic" */
   gccjit::type quadratic;
@@ -94,6 +95,7 @@  make_types (quadratic_test &testcase)
   testcase.zero = testcase.ctxt.zero (testcase.numeric_type);
 
   testcase.int_type = testcase.ctxt.get_int_type <int> ();
+  testcase.void_type = testcase.ctxt.get_type (GCC_JIT_TYPE_VOID);
 
   testcase.a = testcase.ctxt.new_field (testcase.numeric_type, "a");
   testcase.b = testcase.ctxt.new_field (testcase.numeric_type, "b");
@@ -138,17 +140,18 @@  make_calc_discriminant (quadratic_test &testcase)
   params[0] = param_q;
   testcase.calc_discriminant =
     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
-				testcase.numeric_type,
+				testcase.void_type,
 				"calc_discriminant",
 				params,
 				0);
-  testcase.calc_discriminant.add_comment ("(b^2 - 4ac)");
+  gccjit::block block = testcase.calc_discriminant.new_block ();
+  block.add_comment ("(b^2 - 4ac)");
 
   gccjit::rvalue q_a = param_q.dereference_field (testcase.a);
   gccjit::rvalue q_b = param_q.dereference_field (testcase.b);
   gccjit::rvalue q_c = param_q.dereference_field (testcase.c);
 
-  testcase.calc_discriminant.add_assignment (
+  block.add_assignment (
     /* q->discriminant =...  */
     param_q.dereference_field (testcase.discriminant),
     /* (q->b * q->b) - (4 * q->a * q->c) */
@@ -171,6 +174,7 @@  make_calc_discriminant (quadratic_test &testcase)
 	testcase.ctxt.new_mult (
 	  testcase.numeric_type,
 	  q_a, q_c)))); /* end of add_assignment call.	*/
+  block.end_with_return ();
 }
 
 static void
@@ -200,43 +204,43 @@  make_test_quadratic (quadratic_test &testcase)
 
   /* struct quadratic q; */
   gccjit::lvalue q = test_quadratic.new_local (testcase.quadratic, "q");
+
+  gccjit::block initial = test_quadratic.new_block ("initial");
+  gccjit::block on_positive_discriminant
+    = test_quadratic.new_block ("positive_discriminant");
+  gccjit::block on_nonpositive_discriminant
+    = test_quadratic.new_block ("nonpositive_discriminant");
+  gccjit::block on_zero_discriminant
+    = test_quadratic.new_block ("zero_discriminant");
+  gccjit::block on_negative_discriminant
+    = test_quadratic.new_block ("negative_discriminant");
+
+  CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
+                      "zero_discriminant");
+
   /* q.a = a; */
-  test_quadratic.add_assignment (q.access_field (testcase.a), a);
+  initial.add_assignment (q.access_field (testcase.a), a);
   /* q.b = b; */
-  test_quadratic.add_assignment (q.access_field (testcase.b), b);
+  initial.add_assignment (q.access_field (testcase.b), b);
   /* q.c = c; */
-  test_quadratic.add_assignment (q.access_field (testcase.c), c);
+  initial.add_assignment (q.access_field (testcase.c), c);
   /* calc_discriminant (&q); */
   gccjit::rvalue address_of_q = q.get_address ();
-  test_quadratic.add_call (testcase.calc_discriminant, address_of_q);
-
-  gccjit::label on_positive_discriminant
-    = test_quadratic.new_forward_label ("positive_discriminant");
+  initial.add_call (testcase.calc_discriminant, address_of_q);
 
-  gccjit::label on_nonpositive_discriminant
-    = test_quadratic.new_forward_label ("nonpositive_discriminant");
-
-  gccjit::label on_zero_discriminant
-    = test_quadratic.new_forward_label ("zero_discriminant");
-
-  gccjit::label on_negative_discriminant
-    = test_quadratic.new_forward_label ("negative_discriminant");
-  CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
-                      "zero_discriminant");
-
-  test_quadratic.add_comment ("if (q.discriminant > 0)");
-  test_quadratic.add_conditional (
+  initial.add_comment ("if (q.discriminant > 0)");
+  initial.end_with_conditional (
     testcase.ctxt.new_gt (
       q.access_field (testcase.discriminant),
       testcase.zero),
     on_positive_discriminant,
     on_nonpositive_discriminant);
 
-  test_quadratic.place_forward_label (on_positive_discriminant);
+  /* Block: "on_positive_discriminant" */
   /* double s = sqrt (q.discriminant); */
   gccjit::lvalue s = test_quadratic.new_local (testcase.numeric_type, "s");
   gccjit::rvalue discriminant_of_q = q.access_field (testcase.discriminant);
-  test_quadratic.add_assignment (
+  on_positive_discriminant.add_assignment (
     s,
     testcase.ctxt.new_call (testcase.sqrt, discriminant_of_q));
 
@@ -252,8 +256,8 @@  make_test_quadratic (quadratic_test &testcase)
   CHECK_STRING_VALUE (two_a.get_debug_string ().c_str (),
                       "(double)2 * a");
 
-  test_quadratic.add_comment ("*r1 = (-b + s) / (2 * a);");
-  test_quadratic.add_assignment (
+  on_positive_discriminant.add_comment ("*r1 = (-b + s) / (2 * a);");
+  on_positive_discriminant.add_assignment (
     /* "*r1 = ..." */
     r1.dereference (),
 
@@ -266,8 +270,8 @@  make_test_quadratic (quadratic_test &testcase)
 	s),
       two_a));
 
-  test_quadratic.add_comment ("*r2 = (-b - s) / (2 * a)");
-  test_quadratic.add_assignment (
+  on_positive_discriminant.add_comment ("*r2 = (-b - s) / (2 * a)");
+  on_positive_discriminant.add_assignment (
     /* "*r2 = ..." */
     r2.dereference (),
 
@@ -281,23 +285,22 @@  make_test_quadratic (quadratic_test &testcase)
       two_a));
 
   /* "return 2;" */
-  test_quadratic.add_return (testcase.ctxt.new_rvalue (testcase.int_type, 2));
+  on_positive_discriminant.end_with_return (
+    testcase.ctxt.new_rvalue (testcase.int_type, 2));
 
-  /* "else if (q.discriminant == 0)" */
-  test_quadratic.place_forward_label (on_nonpositive_discriminant);
-  test_quadratic.add_comment ("else if (q.discriminant == 0)");
-  test_quadratic.add_conditional (
+  /* Block: "on_nonpositive_discriminant" */
+  on_nonpositive_discriminant.add_comment ("else if (q.discriminant == 0)");
+  on_nonpositive_discriminant.end_with_conditional (
     testcase.ctxt.new_eq (
       q.access_field (testcase.discriminant),
       testcase.zero),
     on_zero_discriminant,
     on_negative_discriminant);
 
+  /* Block: "on_zero_discriminant" */
   /* if (q.discriminant == 0) */
-  test_quadratic.place_forward_label (on_zero_discriminant);
-
-  test_quadratic.add_comment ("*r1 = -b / (2 * a);");
-  test_quadratic.add_assignment (
+  on_zero_discriminant.add_comment ("*r1 = -b / (2 * a);");
+  on_zero_discriminant.add_assignment (
     /* "*r1 = ..." */
     r1.dereference (),
 
@@ -308,11 +311,13 @@  make_test_quadratic (quadratic_test &testcase)
       two_a));
 
   /* "return 1;" */
-  test_quadratic.add_return (testcase.ctxt.one (testcase.int_type));
+  on_zero_discriminant.end_with_return (
+    testcase.ctxt.one (testcase.int_type));
 
+  /* Block: "on_negative_discriminant" */
   /* else return 0; */
-  test_quadratic.place_forward_label (on_negative_discriminant);
-  test_quadratic.add_return (testcase.ctxt.zero (testcase.int_type));
+  on_negative_discriminant.end_with_return (
+    testcase.ctxt.zero (testcase.int_type));
 
   /* Verify that output stream operator << works.  */
   std::ostringstream os;
diff --git a/gcc/testsuite/jit.dg/test-reading-struct.c b/gcc/testsuite/jit.dg/test-reading-struct.c
index 4214320..a090ba9 100644
--- a/gcc/testsuite/jit.dg/test-reading-struct.c
+++ b/gcc/testsuite/jit.dg/test-reading-struct.c
@@ -62,8 +62,9 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   0);
 
   /* return f->x * f->y; */
-  gcc_jit_function_add_return (
-    fn_test_reading,
+  gcc_jit_block *reading_block = gcc_jit_function_new_block (fn_test_reading, NULL);
+  gcc_jit_block_end_with_return (
+    reading_block,
     NULL,
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
@@ -95,21 +96,22 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				struct_type,
 				"tmp");
   /* tmp.x = 5; */
-  gcc_jit_function_add_assignment (
-    fn_test_writing, NULL,
+  gcc_jit_block *writing_block = gcc_jit_function_new_block (fn_test_writing, NULL);
+  gcc_jit_block_add_assignment (
+    writing_block, NULL,
     gcc_jit_lvalue_access_field (local_tmp, NULL, x),
     gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 5));
 
   /* tmp.y = 7; */
-  gcc_jit_function_add_assignment (
-    fn_test_writing, NULL,
+  gcc_jit_block_add_assignment (
+    writing_block, NULL,
     gcc_jit_lvalue_access_field (local_tmp, NULL, y),
     gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 7));
 
   /* return test_reading (&tmp); */
   gcc_jit_rvalue *arg = gcc_jit_lvalue_get_address (local_tmp, NULL);
-  gcc_jit_function_add_return (
-    fn_test_writing,
+  gcc_jit_block_end_with_return (
+    writing_block,
     NULL,
     gcc_jit_context_new_call (
       ctxt, NULL,
diff --git a/gcc/testsuite/jit.dg/test-string-literal.c b/gcc/testsuite/jit.dg/test-string-literal.c
index 5d1b15f..097830d 100644
--- a/gcc/testsuite/jit.dg/test-string-literal.c
+++ b/gcc/testsuite/jit.dg/test-string-literal.c
@@ -28,9 +28,10 @@  create_code (gcc_jit_context *ctxt, void *user_data)
                                   "test_string_literal",
                                   0, NULL,
                                   0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
 
-  gcc_jit_function_add_return (
-    test_fn, NULL,
+  gcc_jit_block_end_with_return (
+    block, NULL,
     gcc_jit_context_new_string_literal (ctxt, "hello world"));
 }
 
diff --git a/gcc/testsuite/jit.dg/test-sum-of-squares.c b/gcc/testsuite/jit.dg/test-sum-of-squares.c
index 10b1644..ae411e0 100644
--- a/gcc/testsuite/jit.dg/test-sum-of-squares.c
+++ b/gcc/testsuite/jit.dg/test-sum-of-squares.c
@@ -42,41 +42,43 @@  create_code (gcc_jit_context *ctxt, void *user_data)
   gcc_jit_lvalue *sum =
     gcc_jit_function_new_local (func, NULL, the_type, "sum");
 
-  /* Create forward label: */
-  gcc_jit_label *label_after_loop =
-    gcc_jit_function_new_forward_label (func, "after_loop");
+  gcc_jit_block *initial =
+    gcc_jit_function_new_block (func, "initial");
+  gcc_jit_block *loop_cond =
+    gcc_jit_function_new_block (func, "loop_cond");
+  gcc_jit_block *loop_body =
+    gcc_jit_function_new_block (func, "loop_body");
+  gcc_jit_block *after_loop =
+    gcc_jit_function_new_block (func, "after_loop");
 
   /* sum = 0; */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     sum,
     gcc_jit_context_new_rvalue_from_int (ctxt, the_type, 0));
 
   /* i = 0; */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    initial, NULL,
     i,
     gcc_jit_context_new_rvalue_from_int (ctxt, the_type, 0));
 
-
-  /* label "cond:" */
-  gcc_jit_label *label_cond =
-    gcc_jit_function_add_label (func, NULL, "cond");
+  gcc_jit_block_end_with_jump (initial, NULL, loop_cond);
 
   /* if (i >= n) */
-  gcc_jit_function_add_conditional (
-    func, NULL,
+  gcc_jit_block_end_with_conditional (
+    loop_cond, NULL,
     gcc_jit_context_new_comparison (
        ctxt, NULL,
        GCC_JIT_COMPARISON_GE,
        gcc_jit_lvalue_as_rvalue (i),
        gcc_jit_param_as_rvalue (n)),
-    label_after_loop,
-    NULL);
+    after_loop,
+    loop_body);
 
   /* sum += i * i */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    loop_body, NULL,
     sum,
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
@@ -89,8 +91,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	 gcc_jit_lvalue_as_rvalue (i))));
 
   /* i++ */
-  gcc_jit_function_add_assignment (
-    func, NULL,
+  gcc_jit_block_add_assignment (
+    loop_body, NULL,
     i,
     gcc_jit_context_new_binary_op (
       ctxt, NULL,
@@ -101,17 +103,11 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 	the_type,
 	1)));
 
-  /* goto label_cond; */
-  gcc_jit_function_add_jump (
-    func, NULL,
-    label_cond);
-
-  /* label "after_loop:" */
-  gcc_jit_function_place_forward_label (func, NULL, label_after_loop);
+  gcc_jit_block_end_with_jump (loop_body, NULL, loop_cond);
 
   /* return sum */
-  gcc_jit_function_add_return (
-    func,
+  gcc_jit_block_end_with_return (
+    after_loop,
     NULL,
     gcc_jit_lvalue_as_rvalue (sum));
 }
diff --git a/gcc/testsuite/jit.dg/test-types.c b/gcc/testsuite/jit.dg/test-types.c
index 575176f..8debcd7 100644
--- a/gcc/testsuite/jit.dg/test-types.c
+++ b/gcc/testsuite/jit.dg/test-types.c
@@ -182,11 +182,12 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				  "test_types",
 				  1, &param_z,
 				  0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
 
   /* Write to the various fields of param "z".	*/
 #define ASSIGN(FIELD, EXPR) \
-  gcc_jit_function_add_assignment (		\
-    test_fn, NULL,				\
+  gcc_jit_block_add_assignment (		\
+    block, NULL,				\
     gcc_jit_rvalue_dereference_field (		\
       gcc_jit_param_as_rvalue (param_z),	\
       NULL,					\
@@ -305,6 +306,8 @@  create_code (gcc_jit_context *ctxt, void *user_data)
       stderr))
 
 #undef ASSIGN
+
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 void
diff --git a/gcc/testsuite/jit.dg/test-using-global.c b/gcc/testsuite/jit.dg/test-using-global.c
index 801bcce..3ec949f 100644
--- a/gcc/testsuite/jit.dg/test-using-global.c
+++ b/gcc/testsuite/jit.dg/test-using-global.c
@@ -40,12 +40,14 @@  create_code (gcc_jit_context *ctxt, void *user_data)
 				  "test_using_global",
 				  0, NULL,
 				  0);
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
 
-  gcc_jit_function_add_assignment_op (
-    test_fn, NULL,
+  gcc_jit_block_add_assignment_op (
+    block, NULL,
     gcc_jit_context_new_global (ctxt, NULL, int_type, "the_global"),
     GCC_JIT_BINARY_OP_PLUS,
     gcc_jit_context_one (ctxt, int_type));
+  gcc_jit_block_end_with_void_return (block, NULL);
 }
 
 int the_global;