gbf_core/decompiler/structure_analysis/
cyclic_region_reducer.rs1#![deny(missing_docs)]
2
3use std::backtrace::Backtrace;
4
5use crate::decompiler::ast::{
6 AstKind, control_flow::ControlFlowNode, expr::ExprKind, new_cyclic_condition, new_do_while,
7 ptr::P,
8};
9
10use super::{
11 RegionReducer, StructureAnalysis, StructureAnalysisError,
12 region::{RegionId, RegionType},
13};
14
15pub struct CyclicRegionReducer;
17
18impl CyclicRegionReducer {
19 fn extract_jump_expr(
21 analysis: &mut StructureAnalysis,
22 region_id: RegionId,
23 ) -> Result<ExprKind, StructureAnalysisError> {
24 let region = analysis.regions.get_mut(region_id.index).ok_or(
25 StructureAnalysisError::RegionNotFound {
26 region_id,
27 backtrace: Backtrace::capture(),
28 },
29 )?;
30 region
31 .get_jump_expr()
32 .ok_or(StructureAnalysisError::ExpectedConditionNotFound {
33 backtrace: Backtrace::capture(),
34 })
35 .cloned()
36 }
37
38 fn cleanup_region(
40 analysis: &mut StructureAnalysis,
41 remove_node: RegionId,
42 start_node: RegionId,
43 final_node: RegionId,
44 ) -> Result<(), StructureAnalysisError> {
45 analysis.remove_edge(start_node, remove_node)?;
46 analysis.remove_edge(remove_node, final_node)?;
47 analysis.remove_node(remove_node)?;
48 Ok(())
49 }
50
51 fn merge_conditional(
53 analysis: &mut StructureAnalysis,
54 region_id: RegionId,
55 cond: P<ControlFlowNode>,
56 ) -> Result<(), StructureAnalysisError> {
57 let region = analysis.regions.get_mut(region_id.index).ok_or(
58 StructureAnalysisError::RegionNotFound {
59 region_id,
60 backtrace: Backtrace::capture(),
61 },
62 )?;
63 region.push_node(cond.into());
64 region.set_region_type(RegionType::Linear);
65 region.remove_jump_expr();
66 Ok(())
67 }
68
69 fn replace_region_nodes(
71 analysis: &mut StructureAnalysis,
72 region_id: RegionId,
73 node: P<ControlFlowNode>,
74 ) -> Result<(), StructureAnalysisError> {
75 let region = analysis.regions.get_mut(region_id.index).ok_or(
76 StructureAnalysisError::RegionNotFound {
77 region_id,
78 backtrace: Backtrace::capture(),
79 },
80 )?;
81 region.clear_nodes();
82 region.push_node(node.into());
83 region.set_region_type(RegionType::Linear);
84 region.remove_jump_expr();
85
86 analysis.remove_edge(region_id, region_id)?;
88 Ok(())
89 }
90
91 fn get_region_nodes(
93 analysis: &StructureAnalysis,
94 region_id: RegionId,
95 ) -> Result<Vec<AstKind>, StructureAnalysisError> {
96 let region = analysis.regions.get(region_id.index).ok_or(
97 StructureAnalysisError::RegionNotFound {
98 region_id,
99 backtrace: Backtrace::capture(),
100 },
101 )?;
102 Ok(region.get_nodes().to_vec())
103 }
104}
105
106impl RegionReducer for CyclicRegionReducer {
107 fn reduce_region(
108 &mut self,
109 analysis: &mut StructureAnalysis,
110 region_id: RegionId,
111 ) -> Result<bool, StructureAnalysisError> {
112 let successors = analysis.get_successors(region_id)?;
113 let len = successors.len();
114
115 for successor in &successors {
117 if successor.0 == region_id {
118 analysis.before_reduce(region_id);
120
121 if len == 1 {
122 return Err(StructureAnalysisError::Other {
124 message: "Infinite loop not implemented yet".to_string(),
125 backtrace: Backtrace::capture(),
126 });
127 }
128
129 let jump_expr = Self::extract_jump_expr(analysis, region_id)?;
130 let region_nodes = Self::get_region_nodes(analysis, region_id)?;
131 let cond: P<ControlFlowNode> = new_do_while(jump_expr, region_nodes).into();
132
133 Self::replace_region_nodes(analysis, region_id, cond)?;
134 return Ok(true);
135 }
136 }
137
138 for successor in &successors {
140 if analysis.get_single_linear_successor(successor.0)? == Some(region_id)
141 && analysis.get_single_predecessor(successor.0)? == Some(region_id)
142 {
143 analysis.before_reduce(region_id);
145
146 let jump_expr = Self::extract_jump_expr(analysis, region_id)?;
148 let region_nodes = Self::get_region_nodes(analysis, successor.0)?;
149 let cond: P<ControlFlowNode> = new_cyclic_condition(
150 jump_expr,
151 region_nodes,
152 analysis.get_branch_opcode(region_id)?,
153 )
154 .map_err(|e| StructureAnalysisError::AstNodeError {
155 source: Box::new(e),
156 backtrace: Backtrace::capture(),
157 })?
158 .into();
159
160 Self::merge_conditional(analysis, region_id, cond)?;
161 Self::cleanup_region(analysis, successor.0, region_id, region_id)?;
162 return Ok(true);
163 }
164 }
165 Ok(false)
166 }
167}