mod edges;
mod transform;
use midenc_hir::{
adt::SmallSet, dominance::DominanceInfo, traits::BranchOpInterface, BlockRef, Builder,
OpBuilder, Operation, OperationRef, Region, RegionRef, Report, SmallVec, SourceSpan, Type,
Value, ValueRange, ValueRef, WalkResult,
};
use self::transform::TransformationContext;
pub trait CFGToSCFInterface {
fn create_structured_branch_region_op(
&self,
builder: &mut OpBuilder,
control_flow_cond_op: OperationRef,
result_types: &[Type],
regions: &mut SmallVec<[RegionRef; 2]>,
) -> Result<OperationRef, Report>;
fn create_structured_branch_region_terminator_op(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
branch_region_op: OperationRef,
replaced_control_flow_op: Option<OperationRef>,
results: ValueRange<'_, 2>,
) -> Result<(), Report>;
fn create_structured_do_while_loop_op(
&self,
builder: &mut OpBuilder,
replaced_op: OperationRef,
loop_values_init: ValueRange<'_, 2>,
condition: ValueRef,
loop_values_next_iter: ValueRange<'_, 2>,
loop_body: RegionRef,
) -> Result<OperationRef, Report>;
fn get_cfg_switch_value(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
value: u32,
) -> ValueRef;
#[allow(clippy::too_many_arguments)]
fn create_cfg_switch_op(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
flag: ValueRef,
case_values: &[u32],
case_destinations: &[BlockRef],
case_arguments: &[ValueRange<'_, 2>],
default_dest: BlockRef,
default_args: ValueRange<'_, 2>,
) -> Result<(), Report>;
fn get_undef_value(&self, span: SourceSpan, builder: &mut OpBuilder, ty: Type) -> ValueRef;
fn create_unreachable_terminator(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
region: RegionRef,
) -> Result<OperationRef, Report>;
fn create_single_destination_branch(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
dummy_flag: ValueRef,
destination: BlockRef,
arguments: ValueRange<'_, 2>,
) -> Result<(), Report> {
self.create_cfg_switch_op(span, builder, dummy_flag, &[], &[], &[], destination, arguments)
}
#[allow(clippy::too_many_arguments)]
fn create_conditional_branch(
&self,
span: SourceSpan,
builder: &mut OpBuilder,
condition: ValueRef,
true_dest: BlockRef,
true_args: ValueRange<'_, 2>,
false_dest: BlockRef,
false_args: ValueRange<'_, 2>,
) -> Result<(), Report> {
self.create_cfg_switch_op(
span,
builder,
condition,
&[0],
&[false_dest],
&[false_args],
true_dest,
true_args,
)
}
}
pub fn transform_cfg_to_scf(
region: RegionRef,
interface: &mut dyn CFGToSCFInterface,
dominance_info: &mut DominanceInfo,
) -> Result<bool, Report> {
{
let region = region.borrow();
if region.is_empty() || region.has_one_block() {
return Ok(false);
}
check_transformation_preconditions(®ion)?;
}
let mut transform_ctx = TransformationContext::new(region, interface, dominance_info)?;
let mut worklist = SmallVec::<[BlockRef; 4]>::from_slice(&[transform_ctx.entry()]);
while let Some(current) = worklist.pop() {
let mut new_regions = transform_ctx.transform_cycles_to_scf_loops(current)?;
worklist.extend(new_regions.iter().copied());
if !new_regions.is_empty() {
let parent_region = current.parent().unwrap();
transform_ctx.invalidate_dominance_info_for_region(parent_region);
}
new_regions = transform_ctx.transform_to_structured_cf_branches(current)?;
worklist.extend(new_regions);
}
transform_ctx.garbage_collect();
Ok(true)
}
fn check_transformation_preconditions(region: &Region) -> Result<(), Report> {
use midenc_hir::SuccessorOperands;
for block in region.body() {
if !block.has_predecessors() && !block.is_entry_block() {
return Err(Report::msg("transformation does not support unreachable blocks"));
}
}
let walk_result = region.prewalk(|op: &Operation| {
if !op.has_successors() {
return WalkResult::Skip;
}
let branch_op_interface = match op.as_trait::<dyn BranchOpInterface>().ok_or_else(|| {
Report::msg(
"transformation does not support terminators with successors not implementing \
BranchOpInterface",
)
}) {
Ok(boi) => boi,
Err(err) => return WalkResult::Break(err),
};
if !op.is_memory_effect_free() {
return WalkResult::Break(Report::msg(
"transformation does not support terminators with side effects",
));
}
for index in 0..op.num_successors() {
let succ_ops = branch_op_interface.get_successor_operands(index);
if succ_ops.num_produced() == 0 {
continue;
}
return WalkResult::Break(Report::msg(
"transformation does not support operations with operation-produced successor \
operands",
));
}
WalkResult::Continue(())
});
walk_result.into_result()
}