From 025b3cd02f3fc7a38db84cc02a943f885240ad10 Mon Sep 17 00:00:00 2001 From: David Mak Date: Tue, 16 Apr 2024 16:30:18 +0800 Subject: [PATCH] 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. --- nac3core/src/codegen/stmt.rs | 255 +++++++++-------------------------- 1 file changed, 61 insertions(+), 194 deletions(-) diff --git a/nac3core/src/codegen/stmt.rs b/nac3core/src/codegen/stmt.rs index 88231d20..c16fbaba 100644 --- a/nac3core/src/codegen/stmt.rs +++ b/nac3core/src/codegen/stmt.rs @@ -29,7 +29,6 @@ use nac3parser::ast::{ Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef, }; use std::convert::TryFrom; -use itertools::Itertools; /// See [`CodeGenerator::gen_var_alloc`]. pub fn gen_var<'ctx>( @@ -695,173 +694,6 @@ pub fn gen_while( 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, -) -> Result>, String> - where - G: CodeGenerator + ?Sized, - CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, - ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, - ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, 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::, _>>()?; - - 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, -) -> Result<(), String> - where - G: CodeGenerator + ?Sized, - CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, 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::>) - }, - ) - }) - .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: /// /// ```c @@ -872,9 +704,6 @@ pub fn gen_chained_if_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn>( /// 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>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, 'a>, @@ -884,17 +713,51 @@ pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>( ) -> Result>, String> where G: CodeGenerator + ?Sized, - CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, - ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, - ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, + CondFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, + ThenFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, + ElseFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, R: BasicValue<'ctx>, { - gen_chained_if_expr_callback( - generator, - ctx, - &[(cond_fn, then_fn)], - Some(else_fn), - ) + 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 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: @@ -903,33 +766,37 @@ pub fn gen_if_else_expr_callback<'ctx, 'a, G, CondFn, ThenFn, ElseFn, R>( /// if (cond_fn()) { /// then_fn(); /// } 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>( generator: &mut G, ctx: &mut CodeGenContext<'ctx, 'a>, cond_fn: CondFn, then_fn: ThenFn, - else_fn: &Option, + else_fn: ElseFn, ) -> Result<(), String> where G: CodeGenerator + ?Sized, - CondFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, - ThenFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>, - ElseFn: Fn(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result<(), String>, + CondFn: FnOnce(&mut G, &mut CodeGenContext<'ctx, 'a>) -> Result, String>, + ThenFn: FnOnce(&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, ctx, - &[(cond_fn, then_fn)], - else_fn, - ) + cond_fn, + |generator, ctx| { + then_fn(generator, ctx)?; + Ok(None::>) + }, + |generator, ctx| { + else_fn(generator, ctx)?; + Ok(None) + } + )?; + + Ok(()) } /// See [`CodeGenerator::gen_if`].