@@ -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:: { compute_label_map, generate_scg_vec, StacklessSCG , StacklessSCGBlockKind } ,
17
18
} ;
18
19
use sha3:: { Digest , Keccak256 } ;
19
20
use std:: collections:: { btree_map:: Entry , BTreeMap } ;
@@ -107,48 +108,153 @@ 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
111
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 ) ;
121
- }
122
- } else {
123
- panic ! ( "effective entry block is not basic" )
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 = 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
+ ) ;
124
147
}
125
148
} else {
126
- emitln ! ( ctx. writer, "let $block := {}" , entry_bb) ;
127
- emit ! ( ctx. writer, "for {} true {} " ) ;
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
+ } )
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 scg_vec = generate_scg_vec ( cfg, code) ;
188
+ for scg in scg_vec {
189
+ self . emit_scg ( & scg, & cfg, code, ctx, fun_id, target) ;
190
+ }
191
+ }
192
+
193
+ pub fn emit_scg (
194
+ & mut self ,
195
+ scg : & StacklessSCG ,
196
+ cfg : & StacklessControlFlowGraph ,
197
+ code : & [ Bytecode ] ,
198
+ ctx : & Context ,
199
+ fun_id : & QualifiedInstId < FunId > ,
200
+ target : & FunctionTarget ,
201
+ ) {
202
+ let label_map = compute_label_map ( cfg, code) ;
203
+ match scg {
204
+ StacklessSCG :: BasicBlock {
205
+ start_offset,
206
+ end_offset,
207
+ kind,
208
+ } => {
128
209
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
- } )
210
+ for offs in * start_offset..* end_offset + 1 {
211
+ self . bytecode (
212
+ ctx,
213
+ fun_id,
214
+ target,
215
+ & label_map,
216
+ & code[ offs as usize ] ,
217
+ false ,
218
+ true ,
219
+ ) ;
220
+ }
221
+ match kind {
222
+ StacklessSCGBlockKind :: Break => {
223
+ emitln ! ( ctx. writer, "break" ) ;
146
224
}
225
+ StacklessSCGBlockKind :: Continue => {
226
+ emitln ! ( ctx. writer, "continue" ) ;
227
+ }
228
+ _ => { }
147
229
}
148
- } )
230
+ } ) ;
149
231
}
150
- } ) ;
151
- emitln ! ( ctx. writer)
232
+ StacklessSCG :: IfBlock {
233
+ cond,
234
+ if_true,
235
+ if_false,
236
+ } => {
237
+ emitln ! ( ctx. writer, "switch $t{} " , cond) ;
238
+ emit ! ( ctx. writer, "case 0 " ) ;
239
+ self . emit_scg ( if_false, cfg, code, ctx, fun_id, target) ;
240
+ emit ! ( ctx. writer, "default " ) ;
241
+ self . emit_scg ( if_true, cfg, code, ctx, fun_id, target) ;
242
+ }
243
+ // TODO: need to emit codes of loops based on StacklessSCG::LoopBlock
244
+ // based on new bytecodes of Break and Continue
245
+ StacklessSCG :: LoopBlock {
246
+ loop_header,
247
+ loop_body,
248
+ } => {
249
+ self . emit_scg ( & * loop_header, & cfg, code, ctx, fun_id, target) ;
250
+ emit ! ( ctx. writer, "for {} true {} " ) ;
251
+ ctx. emit_block ( || {
252
+ for loop_body_scg in loop_body {
253
+ self . emit_scg ( loop_body_scg, & cfg, code, ctx, fun_id, target) ;
254
+ }
255
+ } ) ;
256
+ }
257
+ }
152
258
}
153
259
154
260
/// Compute the locals in the given function which are borrowed from and which are not
@@ -179,22 +285,6 @@ impl<'a> FunctionGenerator<'a> {
179
285
}
180
286
entry_bb
181
287
}
182
-
183
- /// Compute a map from labels to block ids which those labels start.
184
- fn compute_label_map (
185
- cfg : & StacklessControlFlowGraph ,
186
- code : & [ Bytecode ] ,
187
- ) -> BTreeMap < Label , BlockId > {
188
- let mut map = BTreeMap :: new ( ) ;
189
- for id in cfg. blocks ( ) {
190
- if let BlockContent :: Basic { lower, .. } = cfg. content ( id) {
191
- if let Bytecode :: Label ( _, l) = & code[ * lower as usize ] {
192
- map. insert ( * l, id) ;
193
- }
194
- }
195
- }
196
- map
197
- }
198
288
}
199
289
200
290
// ================================================================================================
@@ -210,6 +300,7 @@ impl<'a> FunctionGenerator<'a> {
210
300
label_map : & BTreeMap < Label , BlockId > ,
211
301
bc : & Bytecode ,
212
302
has_flow : bool ,
303
+ skip_block : bool ,
213
304
) {
214
305
use Bytecode :: * ;
215
306
emitln ! (
@@ -283,19 +374,23 @@ impl<'a> FunctionGenerator<'a> {
283
374
match bc {
284
375
Jump ( _, l) => {
285
376
print_loc ( ) ;
286
- emitln ! ( ctx. writer, "$block := {}" , get_block( l) )
377
+ if !skip_block {
378
+ emitln ! ( ctx. writer, "$block := {}" , get_block( l) )
379
+ }
287
380
}
288
381
Branch ( _, if_t, if_f, cond) => {
289
382
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
- )
383
+ if !skip_block {
384
+ emitln ! (
385
+ ctx. writer,
386
+ "switch {}\n \
387
+ case 0 {{ $block := {} }}\n \
388
+ default {{ $block := {} }}",
389
+ local( cond) ,
390
+ get_block( if_f) ,
391
+ get_block( if_t) ,
392
+ )
393
+ }
299
394
}
300
395
Assign ( _, dest, src, _) => {
301
396
print_loc ( ) ;
0 commit comments