forked from M-Labs/nac3
core/stmt: Remove gen_if_chained*
Turns out it is really difficult to get lifetimes and closures right, so let's just provide the most rudimentary if-else codegen and we can nest them if necessary.
This commit is contained in:
parent
e0f440040c
commit
025b3cd02f
@ -29,7 +29,6 @@ use nac3parser::ast::{
|
|||||||
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
||||||
};
|
};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
/// See [`CodeGenerator::gen_var_alloc`].
|
/// See [`CodeGenerator::gen_var_alloc`].
|
||||||
pub fn gen_var<'ctx>(
|
pub fn gen_var<'ctx>(
|
||||||
@ -695,173 +694,6 @@ pub fn gen_while<G: CodeGenerator>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
|
||||||
///
|
|
||||||
/// ```c
|
|
||||||
/// T val;
|
|
||||||
/// if (ifts[0].cond()) {
|
|
||||||
/// val = ifts[0].then();
|
|
||||||
/// } else if (ifts[1].cond()) {
|
|
||||||
/// val = ifts[1].then();
|
|
||||||
/// } else if /* ... */
|
|
||||||
/// else {
|
|
||||||
/// if (else_fn) {
|
|
||||||
/// val = else_fn();
|
|
||||||
/// } else {
|
|
||||||
/// __builtin_unreachable();
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// - `ifts` - A slice of tuples containing the condition and body of a branch respectively. The
|
|
||||||
/// branches will be generated in the order as appears in the slice.
|
|
||||||
/// - `else_fn` - The body to generate if no other branches evaluates to `true`. If [`None`], a call
|
|
||||||
/// to `__builtin_unreachable` will be generated instead.
|
|
||||||
pub fn gen_chained_if_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
ifts: &[(CondFn, ThenFn)],
|
|
||||||
else_fn: Option<ElseFn>,
|
|
||||||
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
|
||||||
ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
|
||||||
ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
|
||||||
R: BasicValue<'ctx>,
|
|
||||||
{
|
|
||||||
assert!(!ifts.is_empty());
|
|
||||||
|
|
||||||
let current_bb = ctx.builder.get_insert_block().unwrap();
|
|
||||||
let current_fn = current_bb.get_parent().unwrap();
|
|
||||||
|
|
||||||
let end_bb = ctx.ctx.append_basic_block(current_fn, "if.end");
|
|
||||||
|
|
||||||
let vals = {
|
|
||||||
let mut vals = ifts.iter()
|
|
||||||
.map(|(cond, then)| -> Result<_, String> {
|
|
||||||
let then_bb = ctx.ctx.insert_basic_block_after(current_bb, "if.then");
|
|
||||||
let else_bb = ctx.ctx.insert_basic_block_after(current_bb, "if.else");
|
|
||||||
|
|
||||||
let cond = cond(generator, ctx)?;
|
|
||||||
assert_eq!(cond.get_type().get_bit_width(), ctx.ctx.bool_type().get_bit_width());
|
|
||||||
ctx.builder.build_conditional_branch(cond, then_bb, else_bb).unwrap();
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(then_bb);
|
|
||||||
let val = then(generator, ctx)?;
|
|
||||||
|
|
||||||
if !ctx.is_terminated() {
|
|
||||||
ctx.builder.build_unconditional_branch(end_bb).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(else_bb);
|
|
||||||
|
|
||||||
Ok((val, then_bb))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
if let Some(else_fn) = else_fn {
|
|
||||||
let else_bb = ctx.builder.get_insert_block().unwrap();
|
|
||||||
let else_val = else_fn(generator, ctx)?;
|
|
||||||
vals.push((else_val, else_bb));
|
|
||||||
|
|
||||||
if !ctx.is_terminated() {
|
|
||||||
ctx.builder.build_unconditional_branch(end_bb).unwrap();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.builder.build_unreachable().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
vals
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.builder.position_at_end(end_bb);
|
|
||||||
let phi = if vals.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let llvm_val_ty = vals.iter()
|
|
||||||
.filter_map(|(val, _)| val.as_ref().map(|v| v.as_basic_value_enum().get_type()))
|
|
||||||
.reduce(|acc, ty| {
|
|
||||||
assert_eq!(acc, ty);
|
|
||||||
acc
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let phi = ctx.builder.build_phi(llvm_val_ty, "").unwrap();
|
|
||||||
vals.into_iter()
|
|
||||||
.filter_map(|(val, bb)| val.map(|v| (v, bb)))
|
|
||||||
.for_each(|(val, bb)| phi.add_incoming(&[(&val.as_basic_value_enum(), bb)]));
|
|
||||||
|
|
||||||
Some(phi.as_basic_value())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(phi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
|
||||||
///
|
|
||||||
/// ```c
|
|
||||||
/// if (ifts[0].cond()) {
|
|
||||||
/// ifts[0].then();
|
|
||||||
/// } else if (ifts[1].cond()) {
|
|
||||||
/// ifts[1].then();
|
|
||||||
/// } else if /* ... */
|
|
||||||
/// else {
|
|
||||||
/// if (else_fn) {
|
|
||||||
/// else_fn();
|
|
||||||
/// } else {
|
|
||||||
/// __builtin_unreachable();
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// This function mainly serves as an abstraction over [`gen_chained_if_expr_callback`] when a value
|
|
||||||
/// does not need to be returned from the `if` construct.
|
|
||||||
///
|
|
||||||
/// - `ifts` - A slice of tuples containing the condition and body of a branch respectively. The
|
|
||||||
/// branches will be generated in the order as appears in the slice.
|
|
||||||
/// - `else_fn` - The body to generate if no other branches evaluates to `true`. If [`None`], a call
|
|
||||||
/// to `__builtin_unreachable` will be generated instead.
|
|
||||||
pub fn gen_chained_if_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn>(
|
|
||||||
generator: &mut G,
|
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
|
||||||
ifts: &[(CondFn, ThenFn)],
|
|
||||||
else_fn: &Option<ElseFn>,
|
|
||||||
) -> Result<(), String>
|
|
||||||
where
|
|
||||||
G: CodeGenerator + ?Sized,
|
|
||||||
CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
|
||||||
ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
|
||||||
ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
|
||||||
{
|
|
||||||
let res = gen_chained_if_expr_callback(
|
|
||||||
generator,
|
|
||||||
ctx,
|
|
||||||
ifts.iter()
|
|
||||||
.map(|(cond, then)| {
|
|
||||||
(
|
|
||||||
cond,
|
|
||||||
|generator: &mut G, ctx: &mut CodeGenContext<'ctx, 'a>| {
|
|
||||||
then(generator, ctx)?;
|
|
||||||
Ok(None::<BasicValueEnum<'ctx>>)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect_vec()
|
|
||||||
.as_slice(),
|
|
||||||
else_fn
|
|
||||||
.as_ref()
|
|
||||||
.map(|else_fn| |generator: &mut G, ctx: &mut CodeGenContext<'ctx, 'a>| {
|
|
||||||
else_fn(generator, ctx)?;
|
|
||||||
Ok(None)
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
assert!(res.is_none());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
||||||
///
|
///
|
||||||
/// ```c
|
/// ```c
|
||||||
@ -872,9 +704,6 @@ pub fn gen_chained_if_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn>(
|
|||||||
/// val = else_fn();
|
/// val = else_fn();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// This function mainly serves as an abstraction over [`gen_chained_if_expr_callback`] for a basic
|
|
||||||
/// `if`-`else` construct that returns a value.
|
|
||||||
pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>(
|
pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
@ -884,17 +713,51 @@ pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>(
|
|||||||
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
) -> Result<Option<BasicValueEnum<'ctx>>, String>
|
||||||
where
|
where
|
||||||
G: CodeGenerator + ?Sized,
|
G: CodeGenerator + ?Sized,
|
||||||
CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
CondFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
||||||
ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
ThenFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
||||||
ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
ElseFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<Option<R>, String>,
|
||||||
R: BasicValue<'ctx>,
|
R: BasicValue<'ctx>,
|
||||||
{
|
{
|
||||||
gen_chained_if_expr_callback(
|
let current_bb = ctx.builder.get_insert_block().unwrap();
|
||||||
generator,
|
let current_fn = current_bb.get_parent().unwrap();
|
||||||
ctx,
|
|
||||||
&[(cond_fn, then_fn)],
|
let end_bb = ctx.ctx.append_basic_block(current_fn, "if.end");
|
||||||
Some(else_fn),
|
|
||||||
)
|
let then_bb = ctx.ctx.insert_basic_block_after(current_bb, "if.then");
|
||||||
|
let else_bb = ctx.ctx.insert_basic_block_after(current_bb, "if.else");
|
||||||
|
|
||||||
|
let cond = cond_fn(generator, ctx)?;
|
||||||
|
assert_eq!(cond.get_type().get_bit_width(), ctx.ctx.bool_type().get_bit_width());
|
||||||
|
ctx.builder.build_conditional_branch(cond, then_bb, else_bb).unwrap();
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(then_bb);
|
||||||
|
let then_val = then_fn(generator, ctx)?;
|
||||||
|
if !ctx.is_terminated() {
|
||||||
|
ctx.builder.build_unconditional_branch(end_bb).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.builder.position_at_end(else_bb);
|
||||||
|
let else_val = else_fn(generator, ctx)?;
|
||||||
|
if !ctx.is_terminated() {
|
||||||
|
ctx.builder.build_unconditional_branch(end_bb).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let phi = match (then_val, else_val) {
|
||||||
|
(Some(tv), Some(ev)) => {
|
||||||
|
let tv_ty = tv.as_basic_value_enum().get_type();
|
||||||
|
assert_eq!(tv_ty, ev.as_basic_value_enum().get_type());
|
||||||
|
|
||||||
|
let phi = ctx.builder.build_phi(tv_ty, "").unwrap();
|
||||||
|
phi.add_incoming(&[(&tv, then_bb), (&ev, else_bb)]);
|
||||||
|
|
||||||
|
Some(phi.as_basic_value())
|
||||||
|
},
|
||||||
|
(Some(tv), None) => Some(tv.as_basic_value_enum()),
|
||||||
|
(None, Some(ev)) => Some(ev.as_basic_value_enum()),
|
||||||
|
(None, None) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(phi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
/// Generates a C-style chained-`if` construct using lambdas, similar to the following C code:
|
||||||
@ -903,33 +766,37 @@ pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>(
|
|||||||
/// if (cond_fn()) {
|
/// if (cond_fn()) {
|
||||||
/// then_fn();
|
/// then_fn();
|
||||||
/// } else {
|
/// } else {
|
||||||
/// if (else_fn) {
|
/// else_fn();
|
||||||
/// else_fn();
|
|
||||||
/// }
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// This function mainly serves as an abstraction over [`gen_chained_if_expr_callback`] for a basic
|
|
||||||
/// `if`-`else` construct that does not return a value.
|
|
||||||
pub fn gen_if_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn>(
|
pub fn gen_if_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn>(
|
||||||
generator: &mut G,
|
generator: &mut G,
|
||||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||||
cond_fn: CondFn,
|
cond_fn: CondFn,
|
||||||
then_fn: ThenFn,
|
then_fn: ThenFn,
|
||||||
else_fn: &Option<ElseFn>,
|
else_fn: ElseFn,
|
||||||
) -> Result<(), String>
|
) -> Result<(), String>
|
||||||
where
|
where
|
||||||
G: CodeGenerator + ?Sized,
|
G: CodeGenerator + ?Sized,
|
||||||
CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
CondFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<IntValue<'ctx>, String>,
|
||||||
ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
ThenFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
||||||
ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
ElseFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>,
|
||||||
{
|
{
|
||||||
gen_chained_if_callback(
|
gen_if_else_expr_callback(
|
||||||
generator,
|
generator,
|
||||||
ctx,
|
ctx,
|
||||||
&[(cond_fn, then_fn)],
|
cond_fn,
|
||||||
else_fn,
|
|generator, ctx| {
|
||||||
)
|
then_fn(generator, ctx)?;
|
||||||
|
Ok(None::<BasicValueEnum<'ctx>>)
|
||||||
|
},
|
||||||
|
|generator, ctx| {
|
||||||
|
else_fn(generator, ctx)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [`CodeGenerator::gen_if`].
|
/// See [`CodeGenerator::gen_if`].
|
||||||
|
Loading…
Reference in New Issue
Block a user