@@ -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,48 +106,147 @@ 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
- let label_map = Self :: compute_label_map ( & cfg, code) ;
110
109
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 ) ;
119
- }
120
- } else {
121
- panic ! ( "effective entry block is not basic" )
110
+ if !ctx. options . apply_cfg_to_scf ( ) {
111
+ self . emit_cfg ( & cfg, code, ctx, fun_id, target) ;
112
+ } else {
113
+ self . emit_scf_from_cfg ( & cfg, code, ctx, fun_id, target) ;
114
+ }
115
+ } ) ;
116
+ emitln ! ( ctx. writer)
117
+ }
118
+
119
+ fn emit_cfg (
120
+ & mut self ,
121
+ cfg : & StacklessControlFlowGraph ,
122
+ code : & [ Bytecode ] ,
123
+ ctx : & Context ,
124
+ fun_id : & QualifiedInstId < FunId > ,
125
+ target : & FunctionTarget ,
126
+ ) {
127
+ let entry_bb = Self :: get_actual_entry_block ( cfg) ;
128
+ let label_map = Self :: compute_label_map ( cfg, code) ;
129
+ // Emit state machine to represent control flow.
130
+ // TODO: Eliminate the need for this, see also
131
+ // https://medium.com/leaningtech/solving-the-structured-control-flow-problem-once-and-for-all-5123117b1ee2
132
+ if cfg. successors ( entry_bb) . iter ( ) . all ( |b| cfg. is_dummmy ( * b) ) {
133
+ // In this trivial case, we have only one block and can omit the state machine
134
+ if let BlockContent :: Basic { lower, upper } = cfg. content ( entry_bb) {
135
+ for offs in * lower..* upper + 1 {
136
+ self . bytecode (
137
+ ctx,
138
+ fun_id,
139
+ target,
140
+ & label_map,
141
+ & code[ offs as usize ] ,
142
+ & "block" ,
143
+ false ,
144
+ ) ;
122
145
}
123
146
} 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) ;
132
- ctx. emit_block ( || {
133
- for offs in * lower..* upper + 1 {
134
- self . bytecode (
135
- ctx,
136
- fun_id,
137
- target,
138
- & label_map,
139
- & code[ offs as usize ] ,
140
- true ,
141
- ) ;
147
+ panic ! ( "effective entry block is not basic" )
148
+ }
149
+ } else {
150
+ emitln ! ( ctx. writer, "let $block := {}" , entry_bb) ;
151
+ emit ! ( ctx. writer, "for {} true {} " ) ;
152
+ ctx. emit_block ( || {
153
+ emitln ! ( ctx. writer, "switch $block" ) ;
154
+ for blk_id in & cfg. blocks ( ) {
155
+ if let BlockContent :: Basic { lower, upper } = cfg. content ( * blk_id) {
156
+ // Emit code for this basic block.
157
+ emit ! ( ctx. writer, "case {} " , blk_id) ;
158
+ ctx. emit_block ( || {
159
+ for offs in * lower..* upper + 1 {
160
+ self . bytecode (
161
+ ctx,
162
+ fun_id,
163
+ target,
164
+ & label_map,
165
+ & code[ offs as usize ] ,
166
+ & "block" ,
167
+ true ,
168
+ ) ;
169
+ }
170
+ } )
171
+ }
172
+ }
173
+ } )
174
+ }
175
+ }
176
+
177
+ fn emit_scf_from_cfg (
178
+ & mut self ,
179
+ cfg : & StacklessControlFlowGraph ,
180
+ code : & [ Bytecode ] ,
181
+ ctx : & Context ,
182
+ fun_id : & QualifiedInstId < FunId > ,
183
+ target : & FunctionTarget ,
184
+ ) {
185
+ let entry_bb = Self :: get_actual_entry_block ( cfg) ;
186
+ let label_map = Self :: compute_label_map ( cfg, code) ;
187
+
188
+ let prover_graph = cfg. to_prover_graph ( ) ;
189
+ let mut reduced_cfg = cfg. clone ( ) ;
190
+ let loop_map = reduced_cfg. reduce_cfg_loop ( & prover_graph) ;
191
+
192
+ let mut scf_top_sort = StacklessStructuredControlFlow :: new ( & reduced_cfg) . top_sort ;
193
+ emitln ! ( ctx. writer, "let $block := {}" , entry_bb) ;
194
+ emitln ! ( ctx. writer, "switch $block" ) ;
195
+ while let Some ( blocks) = scf_top_sort. pop ( ) {
196
+ for blk_id in blocks {
197
+ if let Some ( one_loop) = loop_map. get ( & blk_id) {
198
+ emit ! ( ctx. writer, "case {} " , blk_id) ;
199
+ ctx. emit_block ( || {
200
+ emitln ! (
201
+ ctx. writer,
202
+ "let $block{} := {}" ,
203
+ one_loop. loop_header,
204
+ one_loop. loop_header
205
+ ) ;
206
+ emit ! ( ctx. writer, "for {} true {} " ) ;
207
+ ctx. emit_block ( || {
208
+ let block_str = format ! ( "block{}" , one_loop. loop_header) ;
209
+ emitln ! ( ctx. writer, "switch ${}" , block_str) ;
210
+ for loop_body_blk_id in & one_loop. loop_body {
211
+ if let BlockContent :: Basic { lower, upper } =
212
+ cfg. content ( * loop_body_blk_id)
213
+ {
214
+ emit ! ( ctx. writer, "case {} " , loop_body_blk_id) ;
215
+ ctx. emit_block ( || {
216
+ for offs in * lower..* upper + 1 {
217
+ self . bytecode (
218
+ ctx,
219
+ fun_id,
220
+ target,
221
+ & label_map,
222
+ & code[ offs as usize ] ,
223
+ block_str. as_str ( ) ,
224
+ true ,
225
+ ) ;
226
+ }
227
+ } )
142
228
}
143
- } )
229
+ }
230
+ } ) ;
231
+ } )
232
+ } else if let BlockContent :: Basic { lower, upper } = cfg. content ( blk_id) {
233
+ emit ! ( ctx. writer, "case {} " , blk_id) ;
234
+ ctx. emit_block ( || {
235
+ for offs in * lower..* upper + 1 {
236
+ self . bytecode (
237
+ ctx,
238
+ fun_id,
239
+ target,
240
+ & label_map,
241
+ & code[ offs as usize ] ,
242
+ & "block" . to_string ( ) ,
243
+ true ,
244
+ ) ;
144
245
}
145
- }
146
- } )
246
+ } )
247
+ }
147
248
}
148
- } ) ;
149
- emitln ! ( ctx. writer)
249
+ }
150
250
}
151
251
152
252
/// Compute the locals in the given function which are borrowed from and which are not
@@ -207,6 +307,7 @@ impl<'a> FunctionGenerator<'a> {
207
307
target : & FunctionTarget ,
208
308
label_map : & BTreeMap < Label , BlockId > ,
209
309
bc : & Bytecode ,
310
+ block_name : & str ,
210
311
has_flow : bool ,
211
312
) {
212
313
use Bytecode :: * ;
@@ -281,17 +382,19 @@ impl<'a> FunctionGenerator<'a> {
281
382
match bc {
282
383
Jump ( _, l) => {
283
384
print_loc ( ) ;
284
- emitln ! ( ctx. writer, "$block := {}" , get_block( l) )
385
+ emitln ! ( ctx. writer, "${} := {}" , block_name , get_block( l) )
285
386
}
286
387
Branch ( _, if_t, if_f, cond) => {
287
388
print_loc ( ) ;
288
389
emitln ! (
289
390
ctx. writer,
290
391
"switch {}\n \
291
- case 0 {{ $block := {} }}\n \
292
- default {{ $block := {} }}",
392
+ case 0 {{ ${} := {} }}\n \
393
+ default {{ ${} := {} }}",
293
394
local( cond) ,
395
+ block_name,
294
396
get_block( if_f) ,
397
+ block_name,
295
398
get_block( if_t) ,
296
399
)
297
400
}
0 commit comments