Fix timeline tracking for parallel blocks #344

Closed
derppening wants to merge 5 commits from issue-298 into master
6 changed files with 146 additions and 75 deletions

33
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "ahash"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
dependencies = [
"getrandom",
"once_cell",
@ -15,14 +15,15 @@ dependencies = [
[[package]]
name = "ahash"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
@ -387,7 +388,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash 0.7.6",
"ahash 0.7.7",
]
[[package]]
@ -674,7 +675,7 @@ dependencies = [
name = "nac3parser"
version = "0.1.2"
dependencies = [
"ahash 0.8.3",
"ahash 0.8.5",
"insta",
"lalrpop",
"lalrpop-util",
@ -1547,3 +1548,23 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zerocopy"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c19fae0c8a9efc6a8281f2e623db8af1db9e57852e04cde3e754dd2dc29340f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc56589e9ddd1f1c28d4b4b5c773ce232910a6bb67a70133d61c9e347585efe9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]

View File

@ -1,7 +1,7 @@
use nac3core::{
codegen::{
expr::gen_call,
stmt::{gen_block, gen_with},
stmt::gen_with,
CodeGenContext, CodeGenerator,
},
symbol_resolver::ValueEnum,
@ -28,9 +28,17 @@ use std::{
pub struct ArtiqCodeGenerator<'a> {
name: String,
/// The size of a `size_t` variable in bits.
size_t: u32,
/// Monotonic counter for naming `start`/`stop` variables used by `with parallel` blocks.
name_counter: u32,
/// Variable for tracking the start of a `with parallel` block.
start: Option<Expr<Option<Type>>>,
/// Variable for tracking the end of a `with parallel` block.
end: Option<Expr<Option<Type>>>,
timeline: &'a (dyn TimeFns + Sync),
}
@ -67,7 +75,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?;
if let Some(end) = self.end.clone() {
if let (
Some(start),
Some(end)
) = (self.start.clone(), self.end.clone()) {
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
@ -86,11 +98,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
.unwrap();
let end_store = self.gen_store_target(ctx, &end, Some("end_store.addr"))?;
ctx.builder.build_store(end_store, max);
}
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
self.timeline.emit_at_mu(ctx, start_val);
}
Ok(result)
}
@ -102,6 +114,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if let StmtKind::With { items, body, .. } = &stmt.node {
if items.len() == 1 && items[0].optional_vars.is_none() {
let item = &items[0];
// Behavior of parallel and sequential:
// Each function call (indirectly, can be inside a sequential block) within a parallel
// block will update the end variable to the maximum now_mu in the block.
@ -119,11 +132,15 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if id == &"parallel".into() {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
self.gen_expr(ctx, old_start)?
.unwrap()
.to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
@ -157,8 +174,11 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
ctx.builder.build_store(end, now);
self.end = Some(end_expr);
self.name_counter += 1;
gen_block(self, ctx, body.iter())?;
self.gen_block(ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
@ -172,6 +192,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self
@ -183,6 +204,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
if let Some(old_end) = &old_end {
let outer_end_val = self
@ -207,25 +229,23 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
let outer_end = self.gen_store_target(ctx, old_end, Some("outer_end.addr"))?;
ctx.builder.build_store(outer_end, max);
}
self.start = old_start;
self.end = old_end;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
let start = self.start.take();
for stmt in body.iter() {
self.gen_stmt(ctx, stmt)?;
if ctx.is_terminated() {
break;
}
}
self.start = start;
self.gen_block(ctx, body.iter())?;
return Ok(());
}
}
}
// not parallel/sequential
gen_with(self, ctx, stmt)
} else {

View File

@ -1,9 +1,16 @@
use inkwell::{values::BasicValueEnum, AddressSpace, AtomicOrdering};
use nac3core::codegen::CodeGenContext;
/// Functions for manipulating the timeline.
pub trait TimeFns {
/// Emits LLVM IR for `now_mu`.
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>;
/// Emits LLVM IR for `at_mu`.
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>);
/// Emits LLVM IR for `delay_mu`.
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>);
}
@ -20,23 +27,25 @@ impl TimeFns for NowPinningTimeFns64 {
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now_hiptr");
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
};
if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
ctx.builder.build_load(now_hiptr, "now_hi"),
ctx.builder.build_load(now_loptr, "now_lo"),
ctx.builder.build_load(now_hiptr, "now.hi"),
ctx.builder.build_load(now_loptr, "now.lo"),
) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
let shifted_hi = ctx.builder.build_left_shift(
zext_hi,
i64_type.const_int(32, false),
"now_shifted_zext_hi",
"",
);
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
ctx.builder.build_or(shifted_hi, zext_lo, "now_or").into()
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
ctx.builder.build_or(shifted_hi, zext_lo, "now_mu").into()
} else {
unreachable!();
}
@ -48,14 +57,15 @@ impl TimeFns for NowPinningTimeFns64 {
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
i32_type,
"now_trunc",
"",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
let now = ctx
.module
.get_global("now")
@ -63,11 +73,12 @@ impl TimeFns for NowPinningTimeFns64 {
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::default()),
"now_bitcast",
"now.hi.addr",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
};
ctx.builder
.build_store(now_hiptr, time_hi)
@ -97,41 +108,43 @@ impl TimeFns for NowPinningTimeFns64 {
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now_hiptr");
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_loptr")
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
};
if let (
BasicValueEnum::IntValue(now_hi),
BasicValueEnum::IntValue(now_lo),
BasicValueEnum::IntValue(dt),
) = (
ctx.builder.build_load(now_hiptr, "now_hi"),
ctx.builder.build_load(now_loptr, "now_lo"),
ctx.builder.build_load(now_hiptr, "now.hi"),
ctx.builder.build_load(now_loptr, "now.lo"),
dt,
) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "");
let shifted_hi = ctx.builder.build_left_shift(
zext_hi,
i64_type.const_int(32, false),
"now_shifted_zext_hi",
"",
);
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now_or");
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "");
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now");
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
let time = ctx.builder.build_int_add(now_val, dt, "time");
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(
time,
i64_type.const_int(32, false),
false,
"now_lshr",
"",
),
i32_type,
"now_trunc",
"time.hi",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
ctx.builder
.build_store(now_hiptr, time_hi)
@ -162,11 +175,12 @@ impl TimeFns for NowPinningTimeFns {
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
if let BasicValueEnum::IntValue(now_raw) = now_raw {
let i64_32 = i64_type.const_int(32, false);
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
ctx.builder.build_or(now_lo, now_hi, "now_or").into()
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
ctx.builder.build_or(now_lo, now_hi, "now_mu").into()
} else {
unreachable!();
}
@ -176,11 +190,12 @@ impl TimeFns for NowPinningTimeFns {
let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
ctx.builder.build_right_shift(time, i64_32, false, ""),
i32_type,
"now_trunc",
"time.hi",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let now = ctx
@ -190,11 +205,12 @@ impl TimeFns for NowPinningTimeFns {
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::default()),
"now_bitcast",
"now.hi.addr",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
};
ctx.builder
.build_store(now_hiptr, time_hi)
@ -224,26 +240,28 @@ impl TimeFns for NowPinningTimeFns {
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "");
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) {
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_or");
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi");
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_val");
let time = ctx.builder.build_int_add(now_val, dt, "time");
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
ctx.builder.build_right_shift(time, i64_32, false, "time.hi"),
i32_type,
"now_trunc",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo");
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::default()),
"now_bitcast",
"now.hi.addr",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
};
ctx.builder
.build_store(now_hiptr, time_hi)

View File

@ -169,6 +169,7 @@ pub trait CodeGenerator {
}
/// Generate code for a statement
///
/// Return true if the statement must early return
fn gen_stmt<'ctx, 'a>(
&mut self,
@ -181,6 +182,18 @@ pub trait CodeGenerator {
gen_stmt(self, ctx, stmt)
}
/// Generates code for a block statement.
fn gen_block<'ctx, 'a, 'b, I: Iterator<Item = &'b Stmt<Option<Type>>>>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmts: I,
) -> Result<(), String>
where
Self: Sized,
{
gen_block(self, ctx, stmts)
}
/// See [bool_to_i1].
fn bool_to_i1<'ctx, 'a>(
&self,

View File

@ -1,5 +1,4 @@
use crate::{
codegen::stmt::gen_block,
symbol_resolver::{StaticValue, SymbolResolver},
toplevel::{TopLevelContext, TopLevelDef},
typecheck::{
@ -859,7 +858,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let body = task.body.clone();
gen_func_impl(context, generator, registry, builder, module, task, |generator, ctx| {
gen_block(generator, ctx, body.iter())
generator.gen_block(ctx, body.iter())
})
}

View File

@ -303,7 +303,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
ctx.builder.position_at_end(body_bb);
ctx.builder.build_store(target_i, ctx.builder.build_load(i, "").into_int_value());
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
// Test if next element is still in range
let next_i = ctx.builder.build_int_add(
@ -345,7 +345,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
.into_pointer_value();
let val = ctx.build_gep_and_load(arr_ptr, &[index], Some("val"));
generator.gen_assign(ctx, target, val.into())?;
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
let index = ctx.builder.build_load(index_addr, "for.index").into_int_value();
let inc = ctx.builder.build_int_add(index, size_t.const_int(1, true), "");
@ -365,7 +365,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
gen_block(generator, ctx, orelse.iter())?;
generator.gen_block(ctx, orelse.iter())?;
if !ctx.is_terminated() {
ctx.builder.build_unconditional_branch(cont_bb);
}
@ -419,7 +419,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
ctx.builder.position_at_end(body_bb);
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
for (k, (_, _, counter)) in var_assignment.iter() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
if counter != counter2 {
@ -431,7 +431,7 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
}
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
gen_block(generator, ctx, orelse.iter())?;
generator.gen_block(ctx, orelse.iter())?;
if !ctx.is_terminated() {
ctx.builder.build_unconditional_branch(cont_bb);
}
@ -483,7 +483,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
unreachable!()
};
ctx.builder.position_at_end(body_bb);
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
for (k, (_, _, counter)) in var_assignment.iter() {
let (_, static_val, counter2) = ctx.var_assignment.get_mut(k).unwrap();
if counter != counter2 {
@ -499,7 +499,7 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
}
if !orelse.is_empty() {
ctx.builder.position_at_end(orelse_bb);
gen_block(generator, ctx, orelse.iter())?;
generator.gen_block(ctx, orelse.iter())?;
if !ctx.is_terminated() {
if cont_bb.is_none() {
cont_bb = Some(ctx.ctx.append_basic_block(current, "cont"));
@ -788,9 +788,9 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
}
let old_clauses = ctx.outer_catch_clauses.replace((all_clauses, dispatcher, exn));
let old_unwind = ctx.unwind_target.replace(landingpad);
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
if ctx.builder.get_insert_block().unwrap().get_terminator().is_none() {
gen_block(generator, ctx, orelse.iter())?;
generator.gen_block(ctx, orelse.iter())?;
}
let body = ctx.builder.get_insert_block().unwrap();
// reset old_clauses and old_unwind
@ -892,7 +892,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
ctx.var_assignment.insert(*name, (exn_store, None, 0));
ctx.builder.build_store(exn_store, exn.as_basic_value());
}
gen_block(generator, ctx, body.iter())?;
generator.gen_block(ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// only need to call end catch if not terminated
// otherwise, we already handled in return/break/continue/raise
@ -975,7 +975,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
// exception path
let cleanup = cleanup.unwrap();
ctx.builder.position_at_end(cleanup);
gen_block(generator, ctx, finalbody.iter())?;
generator.gen_block(ctx, finalbody.iter())?;
if !ctx.is_terminated() {
ctx.build_call_or_invoke(resume, &[], "resume");
ctx.builder.build_unreachable();
@ -987,7 +987,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
final_targets.push(tail);
let finalizer = ctx.ctx.append_basic_block(current_fun, "try.finally");
ctx.builder.position_at_end(finalizer);
gen_block(generator, ctx, finalbody.iter())?;
generator.gen_block(ctx, finalbody.iter())?;
if !ctx.is_terminated() {
let dest = ctx.builder.build_load(final_state, "final_dest");
ctx.builder.build_indirect_branch(dest, &final_targets);