Skip to content

Commit

Permalink
[compiler-v2] Add loop labels to the language
Browse files Browse the repository at this point in the history
Besides the user being able to describe more complex algorithms more efficiently, loop labels are required to express any reducible control flow in the AST language, and create parity of the AST with the bytecode level for this kind of code (which is also what can be generated from Move).
  • Loading branch information
wrwg committed Oct 4, 2024
1 parent 18b921d commit c24ecf7
Show file tree
Hide file tree
Showing 25 changed files with 461 additions and 59 deletions.
7 changes: 2 additions & 5 deletions third_party/move/move-compiler-v2/src/bytecode_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,11 @@ impl<'env> Generator<'env> {
self.emit_with(*id, |attr| Bytecode::Jump(attr, continue_label));
self.emit_with(*id, |attr| Bytecode::Label(attr, break_label));
},
ExpData::LoopCont(id, 0, do_continue) => {
ExpData::LoopCont(id, nest, do_continue) => {
if let Some(LoopContext {
continue_label,
break_label,
}) = self.loops.last()
}) = self.loops.iter().rev().nth(*nest)
{
let target = if *do_continue {
*continue_label
Expand All @@ -463,9 +463,6 @@ impl<'env> Generator<'env> {
self.error(*id, "missing enclosing loop statement")
}
},
ExpData::LoopCont(_, _, _) => {
unimplemented!("continue/break with nesting")
},
ExpData::SpecBlock(id, spec) => {
// Map locals in spec to assigned temporaries.
let mut replacer = |id, target| {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// -- Model dump before bytecode pipeline
module 0x815::test {
private fun f1() {
loop {
loop {
loop {
if true {
loop {
if false {
continue[3]
} else {
break[1]
};
break
}
} else {
continue[2]
}
}
};
break
}
}
} // end 0x815::test

// -- Sourcified model before bytecode pipeline
module 0x815::test {
fun f1() {
'l0: loop {
loop 'l1: loop if (true) loop {
if (false) continue 'l0 else break 'l1;
break
} else continue 'l0;
break
}
}
}

============ initial bytecode ================

[variant baseline]
fun test::f1() {
var $t0: bool
var $t1: bool
0: label L0
1: label L2
2: label L4
3: $t0 := true
4: if ($t0) goto 5 else goto 19
5: label L6
6: label L9
7: $t1 := false
8: if ($t1) goto 9 else goto 12
9: label L11
10: goto 0
11: goto 14
12: label L12
13: goto 23
14: label L13
15: goto 17
16: goto 6
17: label L10
18: goto 21
19: label L7
20: goto 0
21: label L8
22: goto 2
23: label L5
24: goto 1
25: label L3
26: goto 28
27: goto 0
28: label L1
29: return ()
}


============ bytecode verification succeeded ========
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Diagnostics:
error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:3:9
3 │ 'outer: loop {
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:6:17
6 │ 'inner: loop if (true) loop {
│ ^^^^^^ Move language construct `'inner` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:7:41
7 │ if (false) continue 'outer else break 'inner;
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:7:59
7 │ if (false) continue 'outer else break 'inner;
│ ^^^^^^ Move language construct `'inner` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:9:33
9 │ } else continue 'outer
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

Diagnostics:
error: label `'outer` undefined
┌─ tests/checking/control_flow/loop_labels_check_err.move:3:15
3 │ break 'outer;
│ ^^^^^^

error: label `'inner` undefined
┌─ tests/checking/control_flow/loop_labels_check_err.move:5:19
5 │ break 'inner
│ ^^^^^^

error: label `'l1` already used by outer loop
┌─ tests/checking/control_flow/loop_labels_check_err.move:11:19
11 │ 'l1: loop 'l1: loop {};
│ --- ^^^
│ │
│ outer definition of label
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun undefined_label() {
break 'outer;
'outer: loop {
break 'inner
}
}

fun duplicate_label() {
'l1: loop {};
'l1: loop 'l1: loop {};
'l1: loop {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// -- Model dump before bytecode pipeline
module 0x815::test {
private fun f1() {
loop {
loop {
loop {
if true {
loop {
if false {
continue[3]
} else {
break[1]
};
break
}
} else {
continue[2]
}
}
};
break
}
}
} // end 0x815::test

// -- Sourcified model before bytecode pipeline
module 0x815::test {
fun f1() {
'l0: loop {
loop 'l1: loop if (true) loop {
if (false) continue 'l0 else break 'l1;
break
} else continue 'l0;
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Diagnostics:
error: unexpected token
┌─ tests/checking/control_flow/loop_labels_parse_err1.move:3:13
3 │ 'a: if (true) false else true
│ ^^
│ │
│ Unexpected 'if'
│ Expected one of: 'while' or 'loop'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module 0x815::test {
fun f1(): bool {
'a: if (true) false else true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Diagnostics:
error: unexpected token
┌─ tests/checking/control_flow/loop_labels_parse_err2.move:3:13
3 │ 'a: if (true) false else true
│ ^^
│ │
│ Unexpected 'if'
│ Expected one of: 'while' or 'loop'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module 0x815::test {
fun f1(): bool {
'a: if (true) false else true
}
}
4 changes: 2 additions & 2 deletions third_party/move/move-compiler-v2/tests/testsuite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const TEST_CONFIGS: Lazy<BTreeMap<&str, TestConfig>> = Lazy::new(|| {
// Turn optimization on by default. Some configs below may turn it off.
.set_experiment(Experiment::OPTIMIZE, true)
.set_experiment(Experiment::OPTIMIZE_WAITING_FOR_COMPARE_TESTS, true)
.set_language_version(LanguageVersion::V2_0);
.set_language_version(LanguageVersion::V2_1);
opts.testing = true;
let configs = vec![
// --- Tests for checking and ast processing
Expand Down Expand Up @@ -723,7 +723,7 @@ const TEST_CONFIGS: Lazy<BTreeMap<&str, TestConfig>> = Lazy::new(|| {
include: vec!["/op-equal/"],
exclude: vec![],
exp_suffix: None,
options: opts.clone().set_language_version(LanguageVersion::V2_1),
options: opts.clone(),
// Run the entire compiler pipeline to double-check the result
stop_after: StopAfter::FileFormat,
dump_ast: DumpLevel::EndStage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
processed 1 task
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//# run
script {
fun main() {
let result = 0;
'outer: while (result < 100) {
while (result < 50) {
'inner: while (result < 30) {
result += 1;
continue 'outer
};
result += 10;
continue 'outer
};
result += 20
};
assert!(result == 110);
}
}
Loading

0 comments on commit c24ecf7

Please sign in to comment.