@@ -14,6 +14,7 @@ use move_stackless_bytecode::{
14
14
function_target_pipeline:: FunctionVariant ,
15
15
stackless_bytecode:: { Bytecode , Constant , Label , Operation } ,
16
16
stackless_control_flow_graph:: { BlockContent , BlockId , StacklessControlFlowGraph } ,
17
+ stackless_structured_control_flow:: StacklessStructuredControlFlow ,
17
18
} ;
18
19
use sha3:: { Digest , Keccak256 } ;
19
20
use std:: collections:: { btree_map:: Entry , BTreeMap } ;
@@ -105,30 +106,119 @@ impl<'a> FunctionGenerator<'a> {
105
106
// Compute control flow graph, entry block, and label map
106
107
let code = target. data . code . as_slice ( ) ;
107
108
let cfg = StacklessControlFlowGraph :: new_forward ( code) ;
108
- let entry_bb = Self :: get_actual_entry_block ( & cfg) ;
109
109
let label_map = Self :: compute_label_map ( & cfg, code) ;
110
110
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" )
119
131
}
120
132
} 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
+ } )
122
156
}
123
157
} 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) ;
132
222
ctx. emit_block ( || {
133
223
for offs in * lower..* upper + 1 {
134
224
self . bytecode (
@@ -143,7 +233,7 @@ impl<'a> FunctionGenerator<'a> {
143
233
} )
144
234
}
145
235
}
146
- } )
236
+ }
147
237
}
148
238
} ) ;
149
239
emitln ! ( ctx. writer)
0 commit comments