forked from move-language/move
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement functions with multiple return values. (move-language#105)
Functions in Move use a second-class tuple-like expression to bind, return, and destructure multiple values. On exit from a function, we generate LLVM IR to wrap them up into a struct, which is returned as a single IR value. Similarly, when a callee that returns such a value is used in an expression, we generate IR to extract each actual value from the struct. Also deduplicated load_call and load_call_store, as the former is just and instance the latter with no return values passed. Added a move-ir-test and a runtime rbpf test to cover above.
- Loading branch information
Showing
6 changed files
with
260 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
...v-llvm-compiler/tests/move-ir-tests/multiple-return-vals-build/modules/0_Test.expected.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
; ModuleID = '0x100__Test' | ||
source_filename = "<unknown>" | ||
|
||
define { i1, i1 } @Test__ret_2vals() { | ||
entry: | ||
%local_0 = alloca i1, align 1 | ||
%local_1 = alloca i1, align 1 | ||
store i1 true, ptr %local_0, align 1 | ||
store i1 false, ptr %local_1, align 1 | ||
%rv.0 = load i1, ptr %local_0, align 1 | ||
%rv.1 = load i1, ptr %local_1, align 1 | ||
%insert_0 = insertvalue { i1, i1 } undef, i1 %rv.0, 0 | ||
%insert_1 = insertvalue { i1, i1 } %insert_0, i1 %rv.1, 1 | ||
ret { i1, i1 } %insert_1 | ||
} | ||
|
||
define { ptr, i8, i128, i32 } @Test__ret_4vals(ptr %0) { | ||
entry: | ||
%local_0 = alloca ptr, align 8 | ||
%local_1 = alloca ptr, align 8 | ||
%local_2 = alloca i8, align 1 | ||
%local_3 = alloca i128, align 8 | ||
%local_4 = alloca i32, align 4 | ||
store ptr %0, ptr %local_0, align 8 | ||
%load_store_tmp = load ptr, ptr %local_0, align 8 | ||
store ptr %load_store_tmp, ptr %local_1, align 8 | ||
store i8 8, ptr %local_2, align 1 | ||
store i128 128, ptr %local_3, align 4 | ||
store i32 32, ptr %local_4, align 4 | ||
%rv.0 = load ptr, ptr %local_1, align 8 | ||
%rv.1 = load i8, ptr %local_2, align 1 | ||
%rv.2 = load i128, ptr %local_3, align 4 | ||
%rv.3 = load i32, ptr %local_4, align 4 | ||
%insert_0 = insertvalue { ptr, i8, i128, i32 } undef, ptr %rv.0, 0 | ||
%insert_1 = insertvalue { ptr, i8, i128, i32 } %insert_0, i8 %rv.1, 1 | ||
%insert_2 = insertvalue { ptr, i8, i128, i32 } %insert_1, i128 %rv.2, 2 | ||
%insert_3 = insertvalue { ptr, i8, i128, i32 } %insert_2, i32 %rv.3, 3 | ||
ret { ptr, i8, i128, i32 } %insert_3 | ||
} | ||
|
||
define void @Test__use_2val_call_result() { | ||
entry: | ||
%local_0 = alloca i1, align 1 | ||
%local_1 = alloca i1, align 1 | ||
%local_2 = alloca i1, align 1 | ||
%retval = call { i1, i1 } @Test__ret_2vals() | ||
%extract_0 = extractvalue { i1, i1 } %retval, 0 | ||
%extract_1 = extractvalue { i1, i1 } %retval, 1 | ||
store i1 %extract_0, ptr %local_0, align 1 | ||
store i1 %extract_1, ptr %local_1, align 1 | ||
%or_src_0 = load i1, ptr %local_0, align 1 | ||
%or_src_1 = load i1, ptr %local_1, align 1 | ||
%or_dst = or i1 %or_src_0, %or_src_1 | ||
store i1 %or_dst, ptr %local_2, align 1 | ||
ret void | ||
} | ||
|
||
define void @Test__use_4val_call_result() { | ||
entry: | ||
%local_0 = alloca i64, align 8 | ||
%local_1 = alloca i8, align 1 | ||
%local_2 = alloca i128, align 8 | ||
%local_3 = alloca i32, align 4 | ||
%local_4 = alloca i64, align 8 | ||
%local_5 = alloca ptr, align 8 | ||
%local_6 = alloca ptr, align 8 | ||
%local_7 = alloca i8, align 1 | ||
%local_8 = alloca i128, align 8 | ||
%local_9 = alloca i32, align 4 | ||
%local_10 = alloca i64, align 8 | ||
%local_11 = alloca i8, align 1 | ||
%local_12 = alloca i128, align 8 | ||
%local_13 = alloca i32, align 4 | ||
store i64 0, ptr %local_4, align 4 | ||
%load_store_tmp = load i64, ptr %local_4, align 4 | ||
store i64 %load_store_tmp, ptr %local_0, align 4 | ||
store ptr %local_0, ptr %local_5, align 8 | ||
%call_arg_0 = load ptr, ptr %local_5, align 8 | ||
%retval = call { ptr, i8, i128, i32 } @Test__ret_4vals(ptr %call_arg_0) | ||
%extract_0 = extractvalue { ptr, i8, i128, i32 } %retval, 0 | ||
%extract_1 = extractvalue { ptr, i8, i128, i32 } %retval, 1 | ||
%extract_2 = extractvalue { ptr, i8, i128, i32 } %retval, 2 | ||
%extract_3 = extractvalue { ptr, i8, i128, i32 } %retval, 3 | ||
store ptr %extract_0, ptr %local_6, align 8 | ||
store i8 %extract_1, ptr %local_7, align 1 | ||
store i128 %extract_2, ptr %local_8, align 4 | ||
store i32 %extract_3, ptr %local_9, align 4 | ||
%load_store_tmp1 = load i32, ptr %local_9, align 4 | ||
store i32 %load_store_tmp1, ptr %local_3, align 4 | ||
%load_store_tmp2 = load i128, ptr %local_8, align 4 | ||
store i128 %load_store_tmp2, ptr %local_2, align 4 | ||
%load_store_tmp3 = load i8, ptr %local_7, align 1 | ||
store i8 %load_store_tmp3, ptr %local_1, align 1 | ||
%load_deref_store_tmp1 = load ptr, ptr %local_6, align 8 | ||
%load_deref_store_tmp2 = load i64, ptr %load_deref_store_tmp1, align 4 | ||
store i64 %load_deref_store_tmp2, ptr %local_10, align 4 | ||
%load_store_tmp4 = load i8, ptr %local_1, align 1 | ||
store i8 %load_store_tmp4, ptr %local_11, align 1 | ||
%load_store_tmp5 = load i128, ptr %local_2, align 4 | ||
store i128 %load_store_tmp5, ptr %local_12, align 4 | ||
%load_store_tmp6 = load i32, ptr %local_3, align 4 | ||
store i32 %load_store_tmp6, ptr %local_13, align 4 | ||
ret void | ||
} |
17 changes: 17 additions & 0 deletions
17
language/tools/move-mv-llvm-compiler/tests/move-ir-tests/multiple-return-vals.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
module 0x100::Test { | ||
fun ret_2vals(): (bool, bool) { (true, false) } | ||
fun ret_4vals(x: &u64): (&u64, u8, u128, u32) { (x, 8, 128, 32) } | ||
|
||
fun use_2val_call_result() { | ||
let (x, y): (bool, bool) = ret_2vals(); | ||
let _t = x || y; | ||
} | ||
fun use_4val_call_result() { | ||
let (a, b, c, d) = ret_4vals(&0); | ||
let _t1 = *a; | ||
let _t2 = b; | ||
let _t3 = c; | ||
let _t4 = d; | ||
} | ||
} | ||
|
32 changes: 32 additions & 0 deletions
32
language/tools/move-mv-llvm-compiler/tests/rbpf-tests/return-multiple-values1.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Multiple return value similar to an example from the Move Book. | ||
|
||
module 0x1::Math { | ||
public fun max(a: u8, b: u8): (u8, bool) { | ||
if (a > b) { | ||
(a, false) | ||
} else if (a < b) { | ||
(b, false) | ||
} else { | ||
(a, true) | ||
} | ||
} | ||
} | ||
|
||
script { | ||
use 0x1::Math; | ||
|
||
fun main() { | ||
let (maxval, is_equal) = Math::max(99, 100); | ||
assert!(maxval == 100, 0xf00); | ||
assert!(!is_equal, 0xf01); | ||
|
||
let (maxval, is_equal) = Math::max(5, 0); | ||
assert!(maxval == 5, 0xf02); | ||
assert!(!is_equal, 0xf03); | ||
|
||
let (maxval, is_equal) = Math::max(123, 123); | ||
assert!(maxval == 123, 0xf04); | ||
assert!(is_equal, 0xf05); | ||
} | ||
} | ||
|
18 changes: 18 additions & 0 deletions
18
language/tools/move-mv-llvm-compiler/tests/rbpf-tests/return-multiple-values2.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module 0x100::Test { | ||
public fun ret_6vals(a: u8, b: u16, c: u32, d: u64, e: u128, f: u256): (u8, u16, u32, u64, u128, u256) { | ||
(a, b, c, d, e, f) | ||
} | ||
} | ||
|
||
script { | ||
fun main() { | ||
let (x1, x2, x3, x4, x5, x6) = 0x100::Test::ret_6vals(1, 2, 3, 4, 5, 6); | ||
assert!(x1 == 1, 0xf00); | ||
assert!(x2 == 2, 0xf01); | ||
assert!(x3 == 3, 0xf02); | ||
assert!(x4 == 4, 0xf03); | ||
assert!(x5 == 5, 0xf04); | ||
assert!(x6 == 6, 0xf05); | ||
} | ||
} | ||
|