@@ -14,9 +14,10 @@ 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:: { StacklessSCG , StacklessStructuredControlFlow } ,
17
18
} ;
18
19
use sha3:: { Digest , Keccak256 } ;
19
- use std:: collections:: { btree_map:: Entry , BTreeMap } ;
20
+ use std:: collections:: { btree_map:: Entry , BTreeMap , BTreeSet } ;
20
21
21
22
/// Mutable state of the function generator.
22
23
pub ( crate ) struct FunctionGenerator < ' a > {
@@ -107,48 +108,260 @@ impl<'a> FunctionGenerator<'a> {
107
108
// Compute control flow graph, entry block, and label map
108
109
let code = target. data . code . as_slice ( ) ;
109
110
let cfg = StacklessControlFlowGraph :: new_forward ( code) ;
110
- let entry_bb = Self :: get_actual_entry_block ( & cfg) ;
111
- let label_map = Self :: compute_label_map ( & cfg, code) ;
112
-
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 ( ctx, fun_id, target, & label_map, & code[ offs as usize ] , false ) ;
111
+
112
+ if !ctx. options . apply_cfg_to_scf ( ) {
113
+ self . emit_cfg ( & cfg, code, ctx, fun_id, target) ;
114
+ } else {
115
+ self . emit_scf_from_cfg ( & cfg, code, ctx, fun_id, target) ;
116
+ }
117
+ } ) ;
118
+ emitln ! ( ctx. writer)
119
+ }
120
+
121
+ fn emit_cfg (
122
+ & mut self ,
123
+ cfg : & StacklessControlFlowGraph ,
124
+ code : & [ Bytecode ] ,
125
+ ctx : & Context ,
126
+ fun_id : & QualifiedInstId < FunId > ,
127
+ target : & FunctionTarget ,
128
+ ) {
129
+ let entry_bb = Self :: get_actual_entry_block ( cfg) ;
130
+ let label_map = Self :: compute_label_map ( cfg, code) ;
131
+ // Emit state machine to represent control flow.
132
+ // TODO: Eliminate the need for this, see also
133
+ // https://medium.com/leaningtech/solving-the-structured-control-flow-problem-once-and-for-all-5123117b1ee2
134
+ if cfg. successors ( entry_bb) . iter ( ) . all ( |b| cfg. is_dummmy ( * b) ) {
135
+ // In this trivial case, we have only one block and can omit the state machine
136
+ if let BlockContent :: Basic { lower, upper } = cfg. content ( entry_bb) {
137
+ for offs in * lower..* upper + 1 {
138
+ self . bytecode (
139
+ ctx,
140
+ fun_id,
141
+ target,
142
+ & label_map,
143
+ & code[ offs as usize ] ,
144
+ false ,
145
+ false ,
146
+ ) ;
147
+ }
148
+ } else {
149
+ panic ! ( "effective entry block is not basic" )
150
+ }
151
+ } else {
152
+ emitln ! ( ctx. writer, "let $block := {}" , entry_bb) ;
153
+ emit ! ( ctx. writer, "for {} true {} " ) ;
154
+ ctx. emit_block ( || {
155
+ emitln ! ( ctx. writer, "switch $block" ) ;
156
+ for blk_id in & cfg. blocks ( ) {
157
+ if let BlockContent :: Basic { lower, upper } = cfg. content ( * blk_id) {
158
+ // Emit code for this basic block.
159
+ emit ! ( ctx. writer, "case {} " , blk_id) ;
160
+ ctx. emit_block ( || {
161
+ for offs in * lower..* upper + 1 {
162
+ self . bytecode (
163
+ ctx,
164
+ fun_id,
165
+ target,
166
+ & label_map,
167
+ & code[ offs as usize ] ,
168
+ true ,
169
+ false ,
170
+ ) ;
171
+ }
172
+ } )
121
173
}
174
+ }
175
+ } )
176
+ }
177
+ }
178
+
179
+ fn emit_scf_from_cfg (
180
+ & mut self ,
181
+ cfg : & StacklessControlFlowGraph ,
182
+ code : & [ Bytecode ] ,
183
+ ctx : & Context ,
184
+ fun_id : & QualifiedInstId < FunId > ,
185
+ target : & FunctionTarget ,
186
+ ) {
187
+ let prover_graph = cfg. to_prover_graph ( ) ;
188
+ let mut reduced_cfg = cfg. clone ( ) ;
189
+ let loop_map = reduced_cfg. reduce_cfg_loop ( & prover_graph) ;
190
+
191
+ let mut scf_top_sort = StacklessStructuredControlFlow :: new ( & reduced_cfg) . top_sort ;
192
+
193
+ let mut scg_vec = vec ! [ ] ;
194
+ let mut visited_blocks = BTreeSet :: new ( ) ;
195
+
196
+ while let Some ( blocks) = scf_top_sort. pop ( ) {
197
+ for blk_id in & blocks {
198
+ if visited_blocks. contains ( blk_id) {
199
+ continue ;
200
+ }
201
+ if let Some ( one_loop) = loop_map. get ( & blk_id) {
202
+ let mut loop_scg_vec = vec ! [ ] ;
203
+ let mut loop_visited_blocks = BTreeSet :: new ( ) ;
204
+ for loop_body_blk_id in & one_loop. loop_body {
205
+ if loop_visited_blocks. contains ( loop_body_blk_id) {
206
+ continue ;
207
+ }
208
+ Self :: push_non_loop_scg (
209
+ & mut loop_scg_vec,
210
+ * loop_body_blk_id,
211
+ & cfg,
212
+ & mut loop_visited_blocks,
213
+ code,
214
+ ) ;
215
+ }
216
+ scg_vec. push ( StacklessSCG :: LoopBlock {
217
+ loop_header : Box :: new ( StacklessSCG :: new ( one_loop. loop_header , & cfg) ) ,
218
+ loop_body : loop_scg_vec,
219
+ } ) ;
122
220
} else {
123
- panic ! ( "effective entry block is not basic" )
221
+ Self :: push_non_loop_scg ( & mut scg_vec , * blk_id , & cfg , & mut visited_blocks , code ) ;
124
222
}
125
- } else {
126
- emitln ! ( ctx. writer, "let $block := {}" , entry_bb) ;
127
- emit ! ( ctx. writer, "for {} true {} " ) ;
223
+ }
224
+ }
225
+
226
+ for scg in scg_vec {
227
+ self . emit_scg ( & scg, & cfg, code, ctx, fun_id, target) ;
228
+ }
229
+ }
230
+
231
+ pub fn emit_scg (
232
+ & mut self ,
233
+ scg : & StacklessSCG ,
234
+ cfg : & StacklessControlFlowGraph ,
235
+ code : & [ Bytecode ] ,
236
+ ctx : & Context ,
237
+ fun_id : & QualifiedInstId < FunId > ,
238
+ target : & FunctionTarget ,
239
+ ) {
240
+ let label_map = Self :: compute_label_map ( cfg, code) ;
241
+ match scg {
242
+ StacklessSCG :: BasicBlock {
243
+ start_offset,
244
+ end_offset,
245
+ } => {
128
246
ctx. emit_block ( || {
129
- emitln ! ( ctx. writer, "switch $block" ) ;
130
- for blk_id in & cfg. blocks ( ) {
131
- if let BlockContent :: Basic { lower, upper } = cfg. content ( * blk_id) {
132
- // Emit code for this basic block.
133
- emit ! ( ctx. writer, "case {} " , blk_id) ;
134
- ctx. emit_block ( || {
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
- true ,
143
- ) ;
144
- }
145
- } )
247
+ for offs in * start_offset..* end_offset + 1 {
248
+ self . bytecode (
249
+ ctx,
250
+ fun_id,
251
+ target,
252
+ & label_map,
253
+ & code[ offs as usize ] ,
254
+ false ,
255
+ true ,
256
+ ) ;
257
+ }
258
+ } ) ;
259
+ }
260
+ StacklessSCG :: IfBlock {
261
+ cond,
262
+ if_true,
263
+ if_false,
264
+ } => {
265
+ emitln ! ( ctx. writer, "switch $t{} " , cond) ;
266
+ emit ! ( ctx. writer, "case 0 " ) ;
267
+ if let StacklessSCG :: BasicBlock {
268
+ start_offset,
269
+ end_offset,
270
+ } = * * if_false
271
+ {
272
+ ctx. emit_block ( || {
273
+ for offs in start_offset..end_offset + 1 {
274
+ self . bytecode (
275
+ ctx,
276
+ fun_id,
277
+ target,
278
+ & label_map,
279
+ & code[ offs as usize ] ,
280
+ false ,
281
+ true ,
282
+ ) ;
283
+ }
284
+ } ) ;
285
+ }
286
+ emit ! ( ctx. writer, "default " ) ;
287
+ if let StacklessSCG :: BasicBlock {
288
+ start_offset,
289
+ end_offset,
290
+ } = * * if_true
291
+ {
292
+ ctx. emit_block ( || {
293
+ for offs in start_offset..end_offset + 1 {
294
+ self . bytecode (
295
+ ctx,
296
+ fun_id,
297
+ target,
298
+ & label_map,
299
+ & code[ offs as usize ] ,
300
+ false ,
301
+ true ,
302
+ ) ;
146
303
}
304
+ } ) ;
305
+ }
306
+ }
307
+ // TODO: need to emit codes of loops based on StacklessSCG::LoopBlock
308
+ // based on new bytecodes of Break and Continue
309
+ StacklessSCG :: LoopBlock {
310
+ loop_header,
311
+ loop_body,
312
+ } => {
313
+ self . emit_scg ( & * loop_header, & cfg, code, ctx, fun_id, target) ;
314
+ emitln ! ( ctx. writer, "for {} true {} " ) ;
315
+ ctx. emit_block ( || {
316
+ for loop_body_scg in loop_body {
317
+ self . emit_scg ( loop_body_scg, & cfg, code, ctx, fun_id, target) ;
147
318
}
148
- } )
319
+ } ) ;
149
320
}
150
- } ) ;
151
- emitln ! ( ctx. writer)
321
+ }
322
+ }
323
+
324
+ pub fn push_non_loop_scg (
325
+ scg_vec : & mut Vec < StacklessSCG > ,
326
+ blk_id : BlockId ,
327
+ cfg : & StacklessControlFlowGraph ,
328
+ visited_blocks : & mut BTreeSet < BlockId > ,
329
+ code : & [ Bytecode ] ,
330
+ ) {
331
+ let label_map = Self :: compute_label_map ( cfg, code) ;
332
+ let get_block = |l| label_map. get ( l) . expect ( "label has corresponding block" ) ;
333
+ if let BlockContent :: Basic { lower, upper } = cfg. content ( blk_id) {
334
+ let mut start = * lower;
335
+ for offs in * lower..* upper + 1 {
336
+ match & code[ offs as usize ] {
337
+ Bytecode :: Branch ( _, if_t, if_f, cond) => {
338
+ scg_vec. push ( StacklessSCG :: BasicBlock {
339
+ start_offset : start as usize ,
340
+ end_offset : offs as usize ,
341
+ } ) ;
342
+ start = offs;
343
+
344
+ let if_block_id = get_block ( if_t) ;
345
+ let else_block_id = get_block ( if_f) ;
346
+ let if_else_scg = StacklessSCG :: IfBlock {
347
+ cond : * cond,
348
+ if_true : Box :: new ( StacklessSCG :: new ( * if_block_id, & cfg) ) ,
349
+ if_false : Box :: new ( StacklessSCG :: new ( * else_block_id, & cfg) ) ,
350
+ } ;
351
+ visited_blocks. insert ( * if_block_id) ;
352
+ visited_blocks. insert ( * else_block_id) ;
353
+ scg_vec. push ( if_else_scg) ;
354
+ }
355
+ _ => { }
356
+ }
357
+ }
358
+ if start != * upper {
359
+ scg_vec. push ( StacklessSCG :: BasicBlock {
360
+ start_offset : start as usize ,
361
+ end_offset : * upper as usize ,
362
+ } ) ;
363
+ }
364
+ }
152
365
}
153
366
154
367
/// Compute the locals in the given function which are borrowed from and which are not
@@ -210,6 +423,7 @@ impl<'a> FunctionGenerator<'a> {
210
423
label_map : & BTreeMap < Label , BlockId > ,
211
424
bc : & Bytecode ,
212
425
has_flow : bool ,
426
+ skip_block : bool ,
213
427
) {
214
428
use Bytecode :: * ;
215
429
emitln ! (
@@ -283,19 +497,23 @@ impl<'a> FunctionGenerator<'a> {
283
497
match bc {
284
498
Jump ( _, l) => {
285
499
print_loc ( ) ;
286
- emitln ! ( ctx. writer, "$block := {}" , get_block( l) )
500
+ if !skip_block {
501
+ emitln ! ( ctx. writer, "$block := {}" , get_block( l) )
502
+ }
287
503
}
288
504
Branch ( _, if_t, if_f, cond) => {
289
505
print_loc ( ) ;
290
- emitln ! (
291
- ctx. writer,
292
- "switch {}\n \
293
- case 0 {{ $block := {} }}\n \
294
- default {{ $block := {} }}",
295
- local( cond) ,
296
- get_block( if_f) ,
297
- get_block( if_t) ,
298
- )
506
+ if !skip_block {
507
+ emitln ! (
508
+ ctx. writer,
509
+ "switch {}\n \
510
+ case 0 {{ $block := {} }}\n \
511
+ default {{ $block := {} }}",
512
+ local( cond) ,
513
+ get_block( if_f) ,
514
+ get_block( if_t) ,
515
+ )
516
+ }
299
517
}
300
518
Assign ( _, dest, src, _) => {
301
519
print_loc ( ) ;
0 commit comments