Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[move][stdlib] Implement mem::swap native move call #14786

Merged
merged 1 commit into from
Nov 14, 2024

Conversation

igor-aptos
Copy link
Contributor

Description

If we have a field that contains non-copyable type, it is impossible to change it, and get the old value back.

Adding two methods:

  • native mem::swap, that implements swap of contents of two mutable references
  • mem::replace, as a simple wrapper based on mem::swap

How Has This Been Tested?

provided unit tests

Type of Change

  • New feature

Which Components or Systems Does This Change Impact?

  • Move/Aptos Virtual Machine

Checklist

  • I have read and followed the CONTRIBUTING doc
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I identified and added all stakeholders and component owners affected by this change as reviewers
  • I tested both happy and unhappy path of the functionality
  • I have made corresponding changes to the documentation

Copy link

trunk-io bot commented Sep 27, 2024

⏱️ 1h 45m total CI duration on this PR
Slowest 15 Jobs Cumulative Duration Recent Runs
rust-move-unit-coverage 16m 🟩
rust-move-unit-coverage 11m 🟩
rust-move-unit-coverage 10m 🟩
rust-move-tests 10m 🟩
rust-move-tests 10m 🟩
rust-move-unit-coverage 10m 🟩
rust-move-tests 10m 🟥
rust-move-tests 9m 🟥
rust-cargo-deny 7m 🟩🟩🟩🟩
check-dynamic-deps 4m 🟩🟩🟩🟩
general-lints 3m 🟩🟩🟩🟩
semgrep/ci 2m 🟩🟩🟩🟩
file_change_determinator 52s 🟩🟩🟩🟩
permission-check 22s 🟩🟩🟩🟩🟩
file_change_determinator 20s 🟩🟩

settingsfeedbackdocs ⋅ learn more about trunk.io

Copy link

codecov bot commented Sep 27, 2024

Codecov Report

Attention: Patch coverage is 80.08658% with 46 lines in your changes missing coverage. Please review.

Project coverage is 60.1%. Comparing base (0e4f5df) to head (8b0d35b).
Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
...party/move/move-vm/types/src/values/values_impl.rs 72.5% 37 Missing ⚠️
...ptos-move/framework/move-stdlib/src/natives/mem.rs 75.0% 9 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #14786    +/-   ##
========================================
  Coverage    60.1%    60.1%            
========================================
  Files         856      857     +1     
  Lines      211110   211341   +231     
========================================
+ Hits       126962   127147   +185     
- Misses      84148    84194    +46     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@lightmark
Copy link
Contributor

What's this for?

@igor-aptos
Copy link
Contributor Author

This is needed to be able to modify fields, when they hold a non-copyable non-dropable type.

For example, you cannot do this today:

struct A<T> {
   field: T,
}

public fun swap<T>(self: &mut A<T>, value: T): T {
   let old_value = self.field;  <-- doesn't compile, expects copy
   self.field = value;          <-- doesn't compile, expects drop
   old_value
}

https://aptos-org.slack.com/archives/C05V6JEKZQ9/p1727393314700309

@vgao1996
Copy link
Contributor

@wrwg I'm also wondering if we should just make this a bytecode instruction. Can probably do it later though.

@igor-aptos igor-aptos requested a review from vgao1996 October 11, 2024 05:37
@igor-aptos igor-aptos force-pushed the igor/move_mem_swap branch 3 times, most recently from c5f381e to 8b0d35b Compare October 16, 2024 21:17
Copy link
Contributor Author

@igor-aptos igor-aptos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to unit-test call_native?

i.e. we are relying for a lot of tests in third_party/move/move-vm/types/src/values/value_tests.rs that are not failing checks internally, to fail checks when native call types get checked.

Otherwise I am not sure how to have that test coverage, as in end-to-end tests - such code would not compile.

@igor-aptos
Copy link
Contributor Author

addressed all comments and added more tests

Copy link
Contributor

@georgemitenkov georgemitenkov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass 1: everything but the values

@@ -0,0 +1,27 @@
/// Module with methods for safe memory manipulation.
/// I.e. swapping/replacing non-copyable/non-droppable types.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I.e. reads a bit weird, maybe For example? Also why break into 2 sentences?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the example is just redundant, removing. swap below has the docstring as well.

@@ -69,7 +69,7 @@
/// global operations.
/// - V1
/// - TBA
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_22;
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_23;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add documentation to new 1.23 in the comment above that new natives have been added

// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

// Copyright (c) The Diem Core Contributors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong, multiple licenses and why we even have Diem here?


/// The feature is not enabled.
/// (0xD is unavailable)
pub const EFEATURE_NOT_ENABLED: u64 = 0x0D_0001;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document this in Move code? Running into these errors is such a pain because you need to search through aptos-core hoping to get the right constant....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, don't we have errors::unavailable Rust helper already? I think it exists in aptos-types though...

types/src/on_chain_config/aptos_features.rs Outdated Show resolved Hide resolved
@@ -317,6 +320,10 @@ impl Features {
self.is_enabled(FeatureFlag::TRANSACTION_SIMULATION_ENHANCEMENT)
}

pub fn is_native_memory_operations_enabled(&self) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: are instead of is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything is using is?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just reads weird to me because it is plural, but maybe a personal taste

fn swap_contents(&self, other: &Self) -> PartialVMResult<()> {
use Container::*;

// TODO: check if unique ownership?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add a todo, add a github handle or create a ticket to track this so someone else can have more context about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this was from Victor's change, missed it. removing, and adding comment.

/// Swap contents of two passed mutable references.
public(friend) native fun swap<T>(left: &mut T, right: &mut T);

/// Replace value reference points to with the given new value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think articles are missing, but I am not a native speaker 😄

debug_assert!(args.len() == 2);

if args.len() != 2 {
return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a message?

Copy link
Contributor Author

@igor-aptos igor-aptos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I missed your previous review, addressing that one and the new one

@@ -0,0 +1,27 @@
/// Module with methods for safe memory manipulation.
/// I.e. swapping/replacing non-copyable/non-droppable types.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the example is just redundant, removing. swap below has the docstring as well.

fn swap_contents(&self, other: &Self) -> PartialVMResult<()> {
use Container::*;

// TODO: check if unique ownership?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this was from Victor's change, missed it. removing, and adding comment.

types/src/on_chain_config/aptos_features.rs Outdated Show resolved Hide resolved
@@ -317,6 +320,10 @@ impl Features {
self.is_enabled(FeatureFlag::TRANSACTION_SIMULATION_ENHANCEMENT)
}

pub fn is_native_memory_operations_enabled(&self) -> bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything is using is?

Copy link
Contributor

@ziaptos ziaptos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few minor comments

* gas cost: MEM_SWAP_BASE
*
**************************************************************************************************/
fn native_swap(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a comment that function pre-condition is that the two references are not the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we generally don't put much. but added into mem.move and into value_impl.rs, same places as we did for vector::move_range.

@@ -0,0 +1,65 @@
#[test_only]
module std::mem_tests {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igor-aptos
specialized indexed references are references to vec, vec, etc....
indexed ref is a reference to a, for instance, struct member, and vec

let a = 1;
let b = 2;
let v = vector[3, 4, 5, 6];

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a struct with an integral member, and test swap with the member?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see below

}};
}

macro_rules! swap_g_s {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swap_general_with_specialized, etc...
let's lower the future code readers' cognitive load.

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

assert!(&s2 == &SomeStruct { f: 8, v: vector[9, 10]}, 6);
assert!(vector::borrow(&vs, 0) == &SomeStruct { f: 2, v: vector[3, 4]}, 7);

swap(&mut s1.f, vector::borrow_mut(&mut v, 0));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a struct with an integral member, and test swap with the member?

this line should cover that?

let a = 1;
let b = 2;
let v = vector[3, 4, 5, 6];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see below

@@ -0,0 +1,65 @@
#[test_only]
module std::mem_tests {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think tests now mostly cover things, but if you see anything that would be useful to add, let me know

* gas cost: MEM_SWAP_BASE
*
**************************************************************************************************/
fn native_swap(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we generally don't put much. but added into mem.move and into value_impl.rs, same places as we did for vector::move_range.

Copy link
Contributor

✅ Forge suite realistic_env_max_load success on ac25522950f1b46ab74e8d3167a5d6aabecdd8fb

two traffics test: inner traffic : committed: 14563.63 txn/s, latency: 2731.16 ms, (p50: 2700 ms, p70: 2700, p90: 2900 ms, p99: 3000 ms), latency samples: 5537520
two traffics test : committed: 99.97 txn/s, latency: 1403.33 ms, (p50: 1300 ms, p70: 1400, p90: 1500 ms, p99: 3000 ms), latency samples: 1720
Latency breakdown for phase 0: ["MempoolToBlockCreation: max: 1.977, avg: 1.562", "ConsensusProposalToOrdered: max: 0.319, avg: 0.291", "ConsensusOrderedToCommit: max: 0.362, avg: 0.351", "ConsensusProposalToCommit: max: 0.656, avg: 0.643"]
Max non-epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 1.07s no progress at version 1988630 (avg 0.20s) [limit 15].
Max epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 8.55s no progress at version 1988628 (avg 7.73s) [limit 15].
Test Ok

Copy link
Contributor

✅ Forge suite framework_upgrade success on a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb

Compatibility test results for a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb (PR)
Upgrade the nodes to version: ac25522950f1b46ab74e8d3167a5d6aabecdd8fb
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1378.04 txn/s, submitted: 1382.95 txn/s, failed submission: 4.91 txn/s, expired: 4.91 txn/s, latency: 2276.22 ms, (p50: 2100 ms, p70: 2400, p90: 3300 ms, p99: 4500 ms), latency samples: 123520
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1260.07 txn/s, submitted: 1261.88 txn/s, failed submission: 1.80 txn/s, expired: 1.80 txn/s, latency: 2441.15 ms, (p50: 2400 ms, p70: 2700, p90: 3600 ms, p99: 5400 ms), latency samples: 111780
5. check swarm health
Compatibility test for a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb passed
Upgrade the remaining nodes to version: ac25522950f1b46ab74e8d3167a5d6aabecdd8fb
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1376.45 txn/s, submitted: 1379.11 txn/s, failed submission: 2.66 txn/s, expired: 2.66 txn/s, latency: 2191.84 ms, (p50: 2100 ms, p70: 2400, p90: 3300 ms, p99: 4200 ms), latency samples: 124060
Test Ok

Copy link
Contributor

✅ Forge suite compat success on a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb

Compatibility test results for a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb (PR)
1. Check liveness of validators at old version: a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe
compatibility::simple-validator-upgrade::liveness-check : committed: 14995.31 txn/s, latency: 2130.58 ms, (p50: 2000 ms, p70: 2100, p90: 2200 ms, p99: 7600 ms), latency samples: 569440
2. Upgrading first Validator to new version: ac25522950f1b46ab74e8d3167a5d6aabecdd8fb
compatibility::simple-validator-upgrade::single-validator-upgrading : committed: 7326.91 txn/s, latency: 3840.11 ms, (p50: 4200 ms, p70: 4400, p90: 4900 ms, p99: 5100 ms), latency samples: 144680
compatibility::simple-validator-upgrade::single-validator-upgrade : committed: 7836.42 txn/s, latency: 4069.80 ms, (p50: 4100 ms, p70: 4300, p90: 6300 ms, p99: 6500 ms), latency samples: 257600
3. Upgrading rest of first batch to new version: ac25522950f1b46ab74e8d3167a5d6aabecdd8fb
compatibility::simple-validator-upgrade::half-validator-upgrading : committed: 7455.23 txn/s, latency: 3906.61 ms, (p50: 4300 ms, p70: 4500, p90: 4600 ms, p99: 4700 ms), latency samples: 143220
compatibility::simple-validator-upgrade::half-validator-upgrade : committed: 7009.11 txn/s, latency: 4554.19 ms, (p50: 4500 ms, p70: 4600, p90: 6700 ms, p99: 7000 ms), latency samples: 264860
4. upgrading second batch to new version: ac25522950f1b46ab74e8d3167a5d6aabecdd8fb
compatibility::simple-validator-upgrade::rest-validator-upgrading : committed: 11352.23 txn/s, latency: 2395.28 ms, (p50: 2300 ms, p70: 2500, p90: 3700 ms, p99: 4100 ms), latency samples: 196200
compatibility::simple-validator-upgrade::rest-validator-upgrade : committed: 11018.49 txn/s, latency: 2835.04 ms, (p50: 2600 ms, p70: 2700, p90: 5100 ms, p99: 6500 ms), latency samples: 357360
5. check swarm health
Compatibility test for a0ec6ba11bfe4cfc5b586edc9e227aba4909e8fe ==> ac25522950f1b46ab74e8d3167a5d6aabecdd8fb passed
Test Ok

@igor-aptos igor-aptos merged commit 6255194 into main Nov 14, 2024
87 of 92 checks passed
@igor-aptos igor-aptos deleted the igor/move_mem_swap branch November 14, 2024 08:33
rahxephon89 pushed a commit that referenced this pull request Nov 15, 2024
## Description
If we have a field that contains non-copyable type, it is impossible to change it, and get the old value back. 

Adding two methods:
* native mem::swap, that implements swap of contents of two mutable references
* mem::replace, as a simple wrapper based on mem::swap 

## How Has This Been Tested?
provided unit tests

## Type of Change
- [x] New feature

## Which Components or Systems Does This Change Impact?
- [x] Move/Aptos Virtual Machine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants