diff --git a/third_party/move/mono-move/core/src/instruction/mod.rs b/third_party/move/mono-move/core/src/instruction/mod.rs index 8fa7ba6dfdf..073ca9bc263 100644 --- a/third_party/move/mono-move/core/src/instruction/mod.rs +++ b/third_party/move/mono-move/core/src/instruction/mod.rs @@ -176,7 +176,7 @@ impl std::fmt::Display for DescriptorId { } } -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub enum MicroOp { //====================================================================== // Data movement diff --git a/third_party/move/mono-move/gas/src/lib.rs b/third_party/move/mono-move/gas/src/lib.rs index ef3cc0d9463..9cab9f657a3 100644 --- a/third_party/move/mono-move/gas/src/lib.rs +++ b/third_party/move/mono-move/gas/src/lib.rs @@ -78,6 +78,19 @@ impl GasMeter for SimpleGasMeter { } } +/// A no-op gas meter for testing. +pub struct NoOpGasMeter; + +impl GasMeter for NoOpGasMeter { + fn charge(&mut self, _amount: u64) -> Result<(), GasExhaustedError> { + Ok(()) + } + + fn balance(&self) -> u64 { + u64::MAX + } +} + // --------------------------------------------------------------------------- // Gas schedule // --------------------------------------------------------------------------- diff --git a/third_party/move/mono-move/programs/Cargo.toml b/third_party/move/mono-move/programs/Cargo.toml index 587ea0bbed5..ef7a4a91de3 100644 --- a/third_party/move/mono-move/programs/Cargo.toml +++ b/third_party/move/mono-move/programs/Cargo.toml @@ -51,3 +51,7 @@ harness = false [[bench]] name = "bst" harness = false + +[[bench]] +name = "match_sum" +harness = false diff --git a/third_party/move/mono-move/programs/benches/bst.rs b/third_party/move/mono-move/programs/benches/bst.rs index fb351332b96..d4a2f9e1e94 100644 --- a/third_party/move/mono-move/programs/benches/bst.rs +++ b/third_party/move/mono-move/programs/benches/bst.rs @@ -2,6 +2,10 @@ // Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use mono_move_gas::NoOpGasMeter; + +#[path = "helpers.rs"] +mod helpers; const N_OPS: u64 = 5000; const KEY_RANGE: u64 = 2500; @@ -28,15 +32,39 @@ fn bench_bst(c: &mut Criterion) { b.iter(|| native_run_ops(black_box(&ops))); }); + // plain (no gas instrumentation) let (functions, descriptors, _arena) = micro_op_bst(); // SAFETY: Exclusive access during bench setup; arena is alive. unsafe { mono_move_core::Function::resolve_calls(&functions) }; group.bench_function("micro_op", |b| { + b.iter_batched( + || { + let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { + functions[6].unwrap().as_ref_unchecked() + }); + let vec_ptr = ctx + .alloc_u64_vec(mono_move_core::DescriptorId(0), &ops) + .unwrap(); + ctx.set_root_arg(0, &vec_ptr.to_le_bytes()); + ctx + }, + |mut ctx| ctx.run().unwrap(), + BatchSize::SmallInput, + ); + }); + + // with gas instrumentation + let (functions, _, _arena) = micro_op_bst(); + // SAFETY: Exclusive access during bench setup; arena is alive. + let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&functions) }; + // SAFETY: Exclusive access during bench setup; arena is alive. + unsafe { mono_move_core::Function::resolve_calls(&functions_gas) }; + group.bench_function("micro_op/gas", |b| { b.iter_batched( || { let gas_meter = SimpleGasMeter::new(u64::MAX); let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { - functions[6].unwrap().as_ref_unchecked() + functions_gas[6].unwrap().as_ref_unchecked() }); let vec_ptr = ctx .alloc_u64_vec(mono_move_core::DescriptorId(0), &ops) @@ -48,6 +76,7 @@ fn bench_bst(c: &mut Criterion) { BatchSize::SmallInput, ); }); + group.finish(); } diff --git a/third_party/move/mono-move/programs/benches/fib.rs b/third_party/move/mono-move/programs/benches/fib.rs index d7fbda5638a..d7ea56b62bd 100644 --- a/third_party/move/mono-move/programs/benches/fib.rs +++ b/third_party/move/mono-move/programs/benches/fib.rs @@ -3,10 +3,13 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +#[path = "helpers.rs"] +mod helpers; + const N: u64 = 25; fn bench_fib(c: &mut Criterion) { - use mono_move_gas::SimpleGasMeter; + use mono_move_gas::{NoOpGasMeter, SimpleGasMeter}; use mono_move_programs::{ fib::{micro_op_fib, move_bytecode_fib, native_fib}, testing, @@ -24,15 +27,39 @@ fn bench_fib(c: &mut Criterion) { b.iter(|| black_box(native_fib(N))); }); + // plain (no gas instrumentation) let (functions, descriptors, _arena) = micro_op_fib(); // SAFETY: Exclusive access during bench setup; arena is alive. unsafe { mono_move_core::Function::resolve_calls(&functions) }; group.bench_function("micro_op", |b| { + b.iter_batched( + || { + let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { + functions[0].unwrap().as_ref_unchecked() + }); + ctx.set_root_arg(0, &N.to_le_bytes()); + ctx + }, + |mut ctx| { + ctx.run().unwrap(); + black_box(ctx.root_result()) + }, + BatchSize::SmallInput, + ); + }); + + // with gas instrumentation + let (functions, _, _arena) = micro_op_fib(); + // SAFETY: Exclusive access during bench setup; arena is alive. + let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&functions) }; + // SAFETY: Exclusive access during bench setup; arena is alive. + unsafe { mono_move_core::Function::resolve_calls(&functions_gas) }; + group.bench_function("micro_op/gas", |b| { b.iter_batched( || { let gas_meter = SimpleGasMeter::new(u64::MAX); let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { - functions[0].unwrap().as_ref_unchecked() + functions_gas[0].unwrap().as_ref_unchecked() }); ctx.set_root_arg(0, &N.to_le_bytes()); ctx @@ -44,6 +71,7 @@ fn bench_fib(c: &mut Criterion) { BatchSize::SmallInput, ); }); + group.finish(); } diff --git a/third_party/move/mono-move/programs/benches/helpers.rs b/third_party/move/mono-move/programs/benches/helpers.rs new file mode 100644 index 00000000000..c95970eece2 --- /dev/null +++ b/third_party/move/mono-move/programs/benches/helpers.rs @@ -0,0 +1,54 @@ +// Copyright (c) Aptos Foundation +// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE + +//! Shared bench helpers. Included via `#[path = "helpers.rs"] mod helpers;` +//! in each bench binary. Not listed as a `[[bench]]` target, so it is never +//! compiled standalone. + +use mono_move_alloc::{ExecutableArena, ExecutableArenaPtr}; +use mono_move_core::{FrameLayoutInfo, Function, MicroOpGasSchedule, SortedSafePointEntries}; +use mono_move_gas::GasInstrumentor; + +/// Build a gas-instrumented copy of `func_ptrs`, re-allocated in a fresh arena. +/// +/// The caller should build the program fresh (without calling +/// `Function::resolve_calls`) before passing it here, so the code still +/// contains `CallFunc` (index-based) ops. After this call, invoke +/// `Function::resolve_calls` on the returned table to patch those ops to +/// direct pointers into the instrumented arena. +/// +/// Frame layouts are re-created as empty; these benchmark programs do not +/// trigger GC, so the omission has no effect on execution. +/// +/// # Safety +/// +/// Each pointer in `func_ptrs` must be valid (the owning arena must outlive +/// this call). +pub unsafe fn gas_instrument( + func_ptrs: &[Option>], +) -> (Vec>>, ExecutableArena) { + let arena = ExecutableArena::new(); + let instrumentor = GasInstrumentor::new(MicroOpGasSchedule); + let new_fns = func_ptrs + .iter() + .map(|f| { + let fp = (*f)?; + // SAFETY: caller guarantees the pointer is valid. + let func = unsafe { fp.as_ref_unchecked() }; + let raw = unsafe { func.code.as_ref_unchecked() }; + let instrumented = instrumentor.run(raw.to_vec()); + let code = arena.alloc_slice_copy(&instrumented); + Some(arena.alloc(Function { + name: func.name, + code, + args_size: func.args_size, + args_and_locals_size: func.args_and_locals_size, + extended_frame_size: func.extended_frame_size, + zero_frame: func.zero_frame, + frame_layout: FrameLayoutInfo::empty(&arena), + safe_point_layouts: SortedSafePointEntries::empty(&arena), + })) + }) + .collect(); + (new_fns, arena) +} diff --git a/third_party/move/mono-move/programs/benches/match_sum.rs b/third_party/move/mono-move/programs/benches/match_sum.rs new file mode 100644 index 00000000000..ac2c42493e6 --- /dev/null +++ b/third_party/move/mono-move/programs/benches/match_sum.rs @@ -0,0 +1,97 @@ +// Copyright (c) Aptos Foundation +// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE + +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; + +#[path = "helpers.rs"] +mod helpers; + +const N: u64 = 1_000_000; + +fn bench_match_sum(c: &mut Criterion) { + use mono_move_gas::{NoOpGasMeter, SimpleGasMeter}; + use mono_move_programs::{ + match_sum::{micro_op_match_sum, move_bytecode_match_sum, native_match_sum}, + testing, + }; + use mono_move_runtime::InterpreterContext; + + // -- native & micro_op ------------------------------------------------- + { + let mut group = c.benchmark_group("match_sum"); + group + .warm_up_time(std::time::Duration::from_secs(1)) + .measurement_time(std::time::Duration::from_secs(3)); + + group.bench_function("native", |b| { + b.iter(|| black_box(native_match_sum(N))); + }); + + // plain (no gas instrumentation) + let (functions, descriptors, _arena) = micro_op_match_sum(); + group.bench_function("micro_op", |b| { + b.iter_batched( + || { + let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { + functions[0].as_ref_unchecked() + }); + ctx.set_root_arg(0, &N.to_le_bytes()); + ctx + }, + |mut ctx| { + ctx.run().unwrap(); + black_box(ctx.root_result()) + }, + BatchSize::SmallInput, + ); + }); + + // with gas instrumentation + let (functions, _, _arena) = micro_op_match_sum(); + let wrapped = functions.iter().map(|f| Some(*f)).collect::>(); + // SAFETY: Exclusive access during bench setup; arena is alive. + let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&wrapped) }; + group.bench_function("micro_op/gas", |b| { + b.iter_batched( + || { + let gas_meter = SimpleGasMeter::new(u64::MAX); + let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { + functions_gas[0].unwrap().as_ref_unchecked() + }); + ctx.set_root_arg(0, &N.to_le_bytes()); + ctx + }, + |mut ctx| { + ctx.run().unwrap(); + black_box(ctx.root_result()) + }, + BatchSize::SmallInput, + ); + }); + + group.finish(); + } + + // -- move_vm ----------------------------------------------------------- + { + let mut group = c.benchmark_group("match_sum"); + group + .sample_size(10) + .warm_up_time(std::time::Duration::from_secs(1)) + .measurement_time(std::time::Duration::from_secs(3)); + + let module = move_bytecode_match_sum(); + testing::with_loaded_move_function(&module, "match_sum", |env| { + group.bench_function("move_vm", |b| { + b.iter(|| { + let result = env.run(vec![testing::arg_u64(N)]); + black_box(testing::return_u64(&result)) + }); + }); + }); + group.finish(); + } +} + +criterion_group!(benches, bench_match_sum); +criterion_main!(benches); diff --git a/third_party/move/mono-move/programs/benches/merge_sort.rs b/third_party/move/mono-move/programs/benches/merge_sort.rs index 246f4689144..469147f88d3 100644 --- a/third_party/move/mono-move/programs/benches/merge_sort.rs +++ b/third_party/move/mono-move/programs/benches/merge_sort.rs @@ -3,10 +3,13 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +#[path = "helpers.rs"] +mod helpers; + const N: u64 = 1000; fn bench_merge_sort(c: &mut Criterion) { - use mono_move_gas::SimpleGasMeter; + use mono_move_gas::{NoOpGasMeter, SimpleGasMeter}; use mono_move_programs::{ merge_sort::{ micro_op_merge_sort, move_bytecode_merge_sort, native_merge_sort, shuffled_range, @@ -35,15 +38,39 @@ fn bench_merge_sort(c: &mut Criterion) { ); }); + // plain (no gas instrumentation) let (functions, descriptors, _arena) = micro_op_merge_sort(); // SAFETY: Exclusive access during bench setup; arena is alive. unsafe { mono_move_core::Function::resolve_calls(&functions) }; group.bench_function("micro_op", |b| { + b.iter_batched( + || { + let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { + functions[0].unwrap().as_ref_unchecked() + }); + let vec_ptr = ctx + .alloc_u64_vec(mono_move_core::DescriptorId(0), &input) + .unwrap(); + ctx.set_root_arg(0, &vec_ptr.to_le_bytes()); + ctx + }, + |mut ctx| ctx.run().unwrap(), + BatchSize::SmallInput, + ); + }); + + // with gas instrumentation + let (functions, _, _arena) = micro_op_merge_sort(); + // SAFETY: Exclusive access during bench setup; arena is alive. + let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&functions) }; + // SAFETY: Exclusive access during bench setup; arena is alive. + unsafe { mono_move_core::Function::resolve_calls(&functions_gas) }; + group.bench_function("micro_op/gas", |b| { b.iter_batched( || { let gas_meter = SimpleGasMeter::new(u64::MAX); let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { - functions[0].unwrap().as_ref_unchecked() + functions_gas[0].unwrap().as_ref_unchecked() }); let vec_ptr = ctx .alloc_u64_vec(mono_move_core::DescriptorId(0), &input) @@ -55,6 +82,7 @@ fn bench_merge_sort(c: &mut Criterion) { BatchSize::SmallInput, ); }); + group.finish(); } diff --git a/third_party/move/mono-move/programs/benches/nested_loop.rs b/third_party/move/mono-move/programs/benches/nested_loop.rs index 724a564b6a8..978a13da92d 100644 --- a/third_party/move/mono-move/programs/benches/nested_loop.rs +++ b/third_party/move/mono-move/programs/benches/nested_loop.rs @@ -3,10 +3,13 @@ use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +#[path = "helpers.rs"] +mod helpers; + const N: u64 = 1000; fn bench_nested_loop(c: &mut Criterion) { - use mono_move_gas::SimpleGasMeter; + use mono_move_gas::{NoOpGasMeter, SimpleGasMeter}; use mono_move_programs::{ nested_loop::{micro_op_nested_loop, move_bytecode_nested_loop, native_nested_loop}, testing, @@ -24,13 +27,36 @@ fn bench_nested_loop(c: &mut Criterion) { b.iter(|| black_box(native_nested_loop(N))); }); + // plain (no gas instrumentation) let (functions, descriptors, _arena) = micro_op_nested_loop(); group.bench_function("micro_op", |b| { + b.iter_batched( + || { + let mut ctx = InterpreterContext::new(&descriptors, NoOpGasMeter, unsafe { + functions[0].as_ref_unchecked() + }); + ctx.set_root_arg(0, &N.to_le_bytes()); + ctx + }, + |mut ctx| { + ctx.run().unwrap(); + black_box(ctx.root_result()) + }, + BatchSize::SmallInput, + ); + }); + + // with gas instrumentation + let (functions, _, _arena) = micro_op_nested_loop(); + let wrapped = functions.iter().map(|f| Some(*f)).collect::>(); + // SAFETY: Exclusive access during bench setup; arena is alive. + let (functions_gas, _arena) = unsafe { helpers::gas_instrument(&wrapped) }; + group.bench_function("micro_op/gas", |b| { b.iter_batched( || { let gas_meter = SimpleGasMeter::new(u64::MAX); let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { - functions[0].as_ref_unchecked() + functions_gas[0].unwrap().as_ref_unchecked() }); ctx.set_root_arg(0, &N.to_le_bytes()); ctx @@ -42,6 +68,7 @@ fn bench_nested_loop(c: &mut Criterion) { BatchSize::SmallInput, ); }); + group.finish(); } diff --git a/third_party/move/mono-move/programs/src/lib.rs b/third_party/move/mono-move/programs/src/lib.rs index 6703dbdb674..971c402b8a5 100644 --- a/third_party/move/mono-move/programs/src/lib.rs +++ b/third_party/move/mono-move/programs/src/lib.rs @@ -10,6 +10,7 @@ pub mod bst; pub mod fib; +pub mod match_sum; pub mod merge_sort; pub mod nested_loop; #[cfg(feature = "testing")] diff --git a/third_party/move/mono-move/programs/src/match_sum.rs b/third_party/move/mono-move/programs/src/match_sum.rs new file mode 100644 index 00000000000..551bb52c81d --- /dev/null +++ b/third_party/move/mono-move/programs/src/match_sum.rs @@ -0,0 +1,201 @@ +// Copyright (c) Aptos Foundation +// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE + +//! Match sum — O(n) loop with a 4-arm match inside. +//! +//! The inner body computes `r = i % 4` and branches to one of four arms, +//! all converging to a single merge block. This creates a "wide diamond" +//! CFG shape (multiple basic blocks with edges to the same successor), +//! which stresses gas instrumentation at basic-block boundaries. + +/// Test cases: (input, expected output). +/// +/// Each full cycle of 4 iterations contributes 10+20+30+40 = 100. +pub const MATCH_SUM_CASES: &[(u64, u64)] = &[ + (0, 0), + (1, 10), + (2, 30), + (3, 60), + (4, 100), + (8, 200), + (100, 2500), +]; + +// --------------------------------------------------------------------------- +// Native Rust +// --------------------------------------------------------------------------- + +pub fn native_match_sum(n: u64) -> u64 { + let mut sum = 0u64; + let mut i = 0u64; + while i < n { + sum += match i % 4 { + 0 => 10, + 1 => 20, + 2 => 30, + _ => 40, + }; + i += 1; + } + sum +} + +// --------------------------------------------------------------------------- +// Micro-op +// --------------------------------------------------------------------------- + +/// Pseudocode: +/// fn match_sum(n: u64) -> u64 { +/// let sum: u64 = 0; +/// let i: u64 = 0; +/// while i < n { +/// match i % 4 { +/// 0 => sum += 10, +/// 1 => sum += 20, +/// 2 => sum += 30, +/// _ => sum += 40, +/// } +/// i += 1; +/// } +/// sum +/// } +/// +/// Frame layout: +/// [0] n (arg) / result +/// [8] sum +/// [16] i +/// [24] r (i % 4) +/// [32] const4 +/// +/// CFG shape — "wide diamond": +/// +/// LOOP ──► BODY ──► head ──► CASE0 ────────────────────╮ +/// └─► GE1 ──► CASE1 ───────────┤ +/// └─► GE2 ──► CASE2 ──┤ +/// └─► CASE3 ──┤ (fallthrough) +/// │ +/// MERGE ──► LOOP / END +/// +/// All four arms (CASE0–CASE3) jump (or fall through) to MERGE, giving it +/// four predecessors — the "wide diamond" this benchmark is designed to test. +#[cfg(feature = "micro-op")] +mod micro_op { + use mono_move_alloc::{ExecutableArena, ExecutableArenaPtr, GlobalArenaPtr}; + use mono_move_core::{ + CodeOffset as CO, FrameLayoutInfo, FrameOffset as FO, Function, MicroOp::*, + SortedSafePointEntries, + }; + use mono_move_runtime::ObjectDescriptor; + + pub fn program() -> ( + Vec>, + Vec, + ExecutableArena, + ) { + let arena = ExecutableArena::new(); + let n = 0u32; + let sum = 8u32; + let i = 16u32; + let r = 24u32; + let c4 = 32u32; + let args_and_locals_size = 40u32; + + #[rustfmt::skip] + let code = vec![ + StoreImm8 { dst: FO(sum), imm: 0 }, // 0: sum = 0 + StoreImm8 { dst: FO(i), imm: 0 }, // 1: i = 0 + StoreImm8 { dst: FO(c4), imm: 4 }, // 2: const4 = 4 + + // LOOP (3): if i < n goto BODY else goto END + JumpLessU64 { target: CO(6), lhs: FO(i), rhs: FO(n) }, // 3 + Move8 { dst: FO(n), src: FO(sum) }, // 4: result = sum + Return, // 5 + + // BODY (6): r = i % 4 + ModU64 { dst: FO(r), lhs: FO(i), rhs: FO(c4) }, // 6 + + // if r >= 1 goto GE1 else CASE0 + JumpGreaterEqualU64Imm { target: CO(10), src: FO(r), imm: 1 }, // 7 + // CASE0 (8): sum += 10; goto MERGE + AddU64Imm { dst: FO(sum), src: FO(sum), imm: 10 }, // 8 + Jump { target: CO(17) }, // 9 + + // GE1 (10): if r >= 2 goto GE2 else CASE1 + JumpGreaterEqualU64Imm { target: CO(13), src: FO(r), imm: 2 }, // 10 + // CASE1 (11): sum += 20; goto MERGE + AddU64Imm { dst: FO(sum), src: FO(sum), imm: 20 }, // 11 + Jump { target: CO(17) }, // 12 + + // GE2 (13): if r >= 3 goto CASE3 else CASE2 + JumpGreaterEqualU64Imm { target: CO(16), src: FO(r), imm: 3 }, // 13 + // CASE2 (14): sum += 30; goto MERGE + AddU64Imm { dst: FO(sum), src: FO(sum), imm: 30 }, // 14 + Jump { target: CO(17) }, // 15 + + // CASE3 (16): sum += 40 (fallthrough to MERGE) + AddU64Imm { dst: FO(sum), src: FO(sum), imm: 40 }, // 16 + + // MERGE (17): i += 1; goto LOOP + AddU64Imm { dst: FO(i), src: FO(i), imm: 1 }, // 17 + Jump { target: CO(3) }, // 18 + ]; + + let code = arena.alloc_slice_fill_iter(code); + + let func = arena.alloc(Function { + name: GlobalArenaPtr::from_static("match_sum"), + code, + args_size: 8, + args_and_locals_size: args_and_locals_size as usize, + extended_frame_size: args_and_locals_size as usize + + mono_move_core::FRAME_METADATA_SIZE, + zero_frame: false, + frame_layout: FrameLayoutInfo::empty(&arena), + safe_point_layouts: SortedSafePointEntries::empty(&arena), + }); + + (vec![func], vec![ObjectDescriptor::Trivial], arena) + } +} + +#[cfg(feature = "micro-op")] +pub use micro_op::program as micro_op_match_sum; + +// --------------------------------------------------------------------------- +// Move bytecode +// --------------------------------------------------------------------------- + +#[cfg(feature = "move-bytecode")] +mod move_bytecode { + use move_binary_format::file_format::CompiledModule; + + pub const SOURCE: &str = " +module 0x1::match_sum { + public fun match_sum(n: u64): u64 { + let sum: u64 = 0; + let i: u64 = 0; + while (i < n) { + let r = i % 4; + if (r == 0) { + sum = sum + 10; + } else if (r == 1) { + sum = sum + 20; + } else if (r == 2) { + sum = sum + 30; + } else { + sum = sum + 40; + }; + i = i + 1; + }; + sum + } +} +"; + + pub fn program() -> CompiledModule { + crate::compile_move_source(SOURCE) + } +} + +#[cfg(feature = "move-bytecode")] +pub use move_bytecode::program as move_bytecode_match_sum; diff --git a/third_party/move/mono-move/programs/tests/match_sum.rs b/third_party/move/mono-move/programs/tests/match_sum.rs new file mode 100644 index 00000000000..27b322ed609 --- /dev/null +++ b/third_party/move/mono-move/programs/tests/match_sum.rs @@ -0,0 +1,55 @@ +// Copyright (c) Aptos Foundation +// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE + +use mono_move_programs::match_sum::{native_match_sum, MATCH_SUM_CASES}; + +#[test] +fn native() { + for &(n, expected) in MATCH_SUM_CASES { + assert_eq!(native_match_sum(n), expected, "native_match_sum({n})"); + } +} + +#[cfg(feature = "micro-op")] +mod micro_op { + use mono_move_gas::SimpleGasMeter; + use mono_move_programs::match_sum::{micro_op_match_sum, MATCH_SUM_CASES}; + use mono_move_runtime::InterpreterContext; + + fn run(n: u64) -> u64 { + let (functions, descriptors, _arena) = micro_op_match_sum(); + let gas_meter = SimpleGasMeter::new(u64::MAX); + let mut ctx = InterpreterContext::new(&descriptors, gas_meter, unsafe { + functions[0].as_ref_unchecked() + }); + ctx.set_root_arg(0, &n.to_le_bytes()); + ctx.run().unwrap(); + ctx.root_result() + } + + #[test] + fn correctness() { + for &(n, expected) in MATCH_SUM_CASES { + assert_eq!(run(n), expected, "micro_op match_sum({n})"); + } + } +} + +#[cfg(feature = "move-bytecode")] +mod move_bytecode { + use super::MATCH_SUM_CASES; + use mono_move_programs::{match_sum::move_bytecode_match_sum, testing}; + + fn run(n: u64) -> u64 { + let module = move_bytecode_match_sum(); + let result = testing::run_move_function(&module, "match_sum", vec![testing::arg_u64(n)]); + testing::return_u64(&result) + } + + #[test] + fn correctness() { + for &(n, expected) in MATCH_SUM_CASES { + assert_eq!(run(n), expected, "move_bytecode match_sum({n})"); + } + } +}