Skip to content

Commit 1060513

Browse files
committed
control flow graph to structured control flow
1 parent 06e9b0e commit 1060513

File tree

9 files changed

+724
-26
lines changed

9 files changed

+724
-26
lines changed

language/evm/move-to-yul/src/experiments.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ impl Experiment {
2424
/// during tests this is off.
2525
/// Retention: permanent
2626
pub const CAPTURE_SOURCE_INFO: &'static str = "capture-source-info";
27+
/// Transform control flow graph to structured control flow.
28+
/// This is off by default for now, we might want to make it default
29+
/// if the performance improvement is significant.
30+
pub const APPLY_CFG_TO_SCF: &'static str = "apply-cfg-to-scf";
2731
}

language/evm/move-to-yul/src/functions.rs

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use move_stackless_bytecode::{
1414
function_target_pipeline::FunctionVariant,
1515
stackless_bytecode::{Bytecode, Constant, Label, Operation},
1616
stackless_control_flow_graph::{BlockContent, BlockId, StacklessControlFlowGraph},
17+
stackless_structured_control_flow::StacklessStructuredControlFlow,
1718
};
1819
use sha3::{Digest, Keccak256};
1920
use std::collections::{btree_map::Entry, BTreeMap};
@@ -105,30 +106,119 @@ impl<'a> FunctionGenerator<'a> {
105106
// Compute control flow graph, entry block, and label map
106107
let code = target.data.code.as_slice();
107108
let cfg = StacklessControlFlowGraph::new_forward(code);
108-
let entry_bb = Self::get_actual_entry_block(&cfg);
109109
let label_map = Self::compute_label_map(&cfg, code);
110110

111-
// Emit state machine to represent control flow.
112-
// TODO: Eliminate the need for this, see also
113-
// https://medium.com/leaningtech/solving-the-structured-control-flow-problem-once-and-for-all-5123117b1ee2
114-
if cfg.successors(entry_bb).iter().all(|b| cfg.is_dummmy(*b)) {
115-
// In this trivial case, we have only one block and can omit the state machine
116-
if let BlockContent::Basic { lower, upper } = cfg.content(entry_bb) {
117-
for offs in *lower..*upper + 1 {
118-
self.bytecode(ctx, fun_id, target, &label_map, &code[offs as usize], false);
111+
if !ctx.options.apply_cfg_to_scf() {
112+
let entry_bb = Self::get_actual_entry_block(&cfg);
113+
// Emit state machine to represent control flow.
114+
// TODO: Eliminate the need for this, see also
115+
// https://medium.com/leaningtech/solving-the-structured-control-flow-problem-once-and-for-all-5123117b1ee2
116+
if cfg.successors(entry_bb).iter().all(|b| cfg.is_dummmy(*b)) {
117+
// In this trivial case, we have only one block and can omit the state machine
118+
if let BlockContent::Basic { lower, upper } = cfg.content(entry_bb) {
119+
for offs in *lower..*upper + 1 {
120+
self.bytecode(
121+
ctx,
122+
fun_id,
123+
target,
124+
&label_map,
125+
&code[offs as usize],
126+
false,
127+
);
128+
}
129+
} else {
130+
panic!("effective entry block is not basic")
119131
}
120132
} else {
121-
panic!("effective entry block is not basic")
133+
emitln!(ctx.writer, "let $block := {}", entry_bb);
134+
emit!(ctx.writer, "for {} true {} ");
135+
ctx.emit_block(|| {
136+
emitln!(ctx.writer, "switch $block");
137+
for blk_id in &cfg.blocks() {
138+
if let BlockContent::Basic { lower, upper } = cfg.content(*blk_id) {
139+
// Emit code for this basic block.
140+
emit!(ctx.writer, "case {} ", blk_id);
141+
ctx.emit_block(|| {
142+
for offs in *lower..*upper + 1 {
143+
self.bytecode(
144+
ctx,
145+
fun_id,
146+
target,
147+
&label_map,
148+
&code[offs as usize],
149+
true,
150+
);
151+
}
152+
})
153+
}
154+
}
155+
})
122156
}
123157
} else {
124-
emitln!(ctx.writer, "let $block := {}", entry_bb);
125-
emit!(ctx.writer, "for {} true {} ");
126-
ctx.emit_block(|| {
127-
emitln!(ctx.writer, "switch $block");
128-
for blk_id in &cfg.blocks() {
129-
if let BlockContent::Basic { lower, upper } = cfg.content(*blk_id) {
130-
// Emit code for this basic block.
131-
emit!(ctx.writer, "case {} ", blk_id);
158+
let prover_graph = cfg.to_prover_graph();
159+
let mut reduced_cfg = cfg.clone();
160+
let loop_map = reduced_cfg.reduce_cfg_loop(&prover_graph);
161+
162+
let mut scf_top_sort = StacklessStructuredControlFlow::new(&reduced_cfg).top_sort;
163+
while let Some(blocks) = scf_top_sort.pop() {
164+
for blk_id in blocks {
165+
if let Some(one_loop) = loop_map.get(&blk_id) {
166+
emitln!(ctx.writer, "let $block := {}", one_loop.loop_header);
167+
emit!(ctx.writer, "for {} true {} ");
168+
ctx.emit_block(|| {
169+
if let BlockContent::Basic { lower, upper } =
170+
cfg.content(one_loop.loop_header)
171+
{
172+
ctx.emit_block(|| {
173+
for offs in *lower..*upper + 1 {
174+
self.bytecode(
175+
ctx,
176+
fun_id,
177+
target,
178+
&label_map,
179+
&code[offs as usize],
180+
true,
181+
);
182+
}
183+
})
184+
}
185+
for loop_body_blk_id in &one_loop.loop_body {
186+
if let BlockContent::Basic { lower, upper } =
187+
cfg.content(*loop_body_blk_id)
188+
{
189+
ctx.emit_block(|| {
190+
for offs in *lower..*upper + 1 {
191+
self.bytecode(
192+
ctx,
193+
fun_id,
194+
target,
195+
&label_map,
196+
&code[offs as usize],
197+
true,
198+
);
199+
}
200+
})
201+
}
202+
}
203+
if let BlockContent::Basic { lower, upper } =
204+
cfg.content(one_loop.loop_latch)
205+
{
206+
ctx.emit_block(|| {
207+
for offs in *lower..*upper + 1 {
208+
self.bytecode(
209+
ctx,
210+
fun_id,
211+
target,
212+
&label_map,
213+
&code[offs as usize],
214+
true,
215+
);
216+
}
217+
})
218+
}
219+
});
220+
} else if let BlockContent::Basic { lower, upper } = cfg.content(blk_id) {
221+
emitln!(ctx.writer, "let $block := {}", blk_id);
132222
ctx.emit_block(|| {
133223
for offs in *lower..*upper + 1 {
134224
self.bytecode(
@@ -143,7 +233,7 @@ impl<'a> FunctionGenerator<'a> {
143233
})
144234
}
145235
}
146-
})
236+
}
147237
}
148238
});
149239
emitln!(ctx.writer)

language/evm/move-to-yul/src/options.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ impl Options {
6262
pub fn generate_source_info(&self) -> bool {
6363
!self.testing || self.experiment_on(Experiment::CAPTURE_SOURCE_INFO)
6464
}
65+
66+
/// Returns true if control flow graph to structured control flow is applied.
67+
pub fn apply_cfg_to_scf(&self) -> bool {
68+
self.experiment_on(Experiment::APPLY_CFG_TO_SCF)
69+
}
6570
}

0 commit comments

Comments
 (0)