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;
