Skip to content

Commit d01c60c

Browse files
[mono-move] Global storage support
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 67263a1 commit d01c60c

31 files changed

Lines changed: 3286 additions & 323 deletions

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

third_party/move/mono-move/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rust-version = { workspace = true }
1414
[dependencies]
1515
anyhow = { workspace = true }
1616
arc-swap = { workspace = true }
17+
bytes = { workspace = true }
1718
mono-move-alloc = { workspace = true }
1819
mono-move-gas = { workspace = true }
1920
move-binary-format = { workspace = true }

third_party/move/mono-move/core/src/execution_context.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
77
use crate::{
88
interner::{InternedIdentifier, InternedModuleId},
9+
storage::{ResourceProvider, NO_RESOURCE_PROVIDER},
910
types::InternedTypeList,
1011
FunctionPtr,
1112
};
1213
use mono_move_gas::{GasMeter, NoOpGasMeter, SimpleGasMeter};
1314

1415
/// Runtime context consulted by the interpreter during execution: gas
15-
/// charging and cross-module function resolution.
16+
/// charging, cross-module function or resource resolution.
1617
pub trait ExecutionContext {
1718
/// Access the gas meter.
1819
fn gas_meter(&mut self) -> &mut impl GasMeter;
@@ -26,6 +27,10 @@ pub trait ExecutionContext {
2627
name: InternedIdentifier,
2728
ty_args: InternedTypeList,
2829
) -> anyhow::Result<FunctionPtr>;
30+
31+
/// Access the resource provider to fetch resource from storage on read-set
32+
/// cache miss.
33+
fn resource_provider(&self) -> &dyn ResourceProvider;
2934
}
3035

3136
/// A [`ExecutionContext`] that supports only local execution within a
@@ -35,20 +40,22 @@ pub trait ExecutionContext {
3540
/// Intended for tests and benches that don't exercise cross-module dispatch.
3641
///
3742
// TODO: migrate to a real impl and remove this.
38-
pub struct LocalExecutionContext<G: GasMeter = NoOpGasMeter> {
43+
pub struct LocalExecutionContext<'r, G: GasMeter = NoOpGasMeter> {
3944
gas_meter: G,
45+
resource_provider: &'r dyn ResourceProvider,
4046
}
4147

42-
impl LocalExecutionContext<NoOpGasMeter> {
48+
impl LocalExecutionContext<'static, NoOpGasMeter> {
4349
/// No gas accounting at all (`charge` is a no-op).
4450
pub fn unmetered() -> Self {
4551
Self {
4652
gas_meter: NoOpGasMeter,
53+
resource_provider: &NO_RESOURCE_PROVIDER,
4754
}
4855
}
4956
}
5057

51-
impl LocalExecutionContext<SimpleGasMeter> {
58+
impl LocalExecutionContext<'static, SimpleGasMeter> {
5259
/// [`SimpleGasMeter`] with `u64::MAX` budget.
5360
pub fn with_max_budget() -> Self {
5461
Self::with_budget(u64::MAX)
@@ -58,11 +65,22 @@ impl LocalExecutionContext<SimpleGasMeter> {
5865
pub fn with_budget(amount: u64) -> Self {
5966
Self {
6067
gas_meter: SimpleGasMeter::new(amount),
68+
resource_provider: &NO_RESOURCE_PROVIDER,
69+
}
70+
}
71+
}
72+
73+
impl<'r, G: GasMeter> LocalExecutionContext<'r, G> {
74+
/// Builds a context with the gas meter and resource provider.
75+
pub fn new(gas_meter: G, resource_provider: &'r dyn ResourceProvider) -> Self {
76+
Self {
77+
gas_meter,
78+
resource_provider,
6179
}
6280
}
6381
}
6482

65-
impl<G: GasMeter> ExecutionContext for LocalExecutionContext<G> {
83+
impl<'r, G: GasMeter> ExecutionContext for LocalExecutionContext<'r, G> {
6684
fn gas_meter(&mut self) -> &mut impl GasMeter {
6785
&mut self.gas_meter
6886
}
@@ -75,4 +93,8 @@ impl<G: GasMeter> ExecutionContext for LocalExecutionContext<G> {
7593
) -> anyhow::Result<FunctionPtr> {
7694
anyhow::bail!("LocalExecutionContext: load_function not supported")
7795
}
96+
97+
fn resource_provider(&self) -> &dyn ResourceProvider {
98+
self.resource_provider
99+
}
78100
}

third_party/move/mono-move/core/src/instruction/gas.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ impl HasCfgInfo for MicroOp {
9292
| MicroOp::StoreRandomU64 { .. }
9393
| MicroOp::ForceGC
9494
| MicroOp::PackClosure(_)
95-
| MicroOp::CallClosure(_) => None,
95+
| MicroOp::CallClosure(_)
96+
| MicroOp::Exists { .. }
97+
| MicroOp::BorrowGlobal { .. }
98+
| MicroOp::BorrowGlobalMut { .. }
99+
| MicroOp::MoveFrom { .. }
100+
| MicroOp::MoveTo { .. } => None,
96101
}
97102
}
98103
}
@@ -203,7 +208,12 @@ impl RemapTargets for MicroOp {
203208
| MicroOp::StoreRandomU64 { .. }
204209
| MicroOp::ForceGC
205210
| MicroOp::PackClosure(_)
206-
| MicroOp::CallClosure(_)) => op,
211+
| MicroOp::CallClosure(_)
212+
| MicroOp::Exists { .. }
213+
| MicroOp::BorrowGlobal { .. }
214+
| MicroOp::BorrowGlobalMut { .. }
215+
| MicroOp::MoveFrom { .. }
216+
| MicroOp::MoveTo { .. }) => op,
207217
}
208218
}
209219
}
@@ -305,6 +315,12 @@ impl GasSchedule<MicroOp> for MicroOpGasSchedule {
305315
// --- Closures ---
306316
MicroOp::PackClosure(_) => 20,
307317
MicroOp::CallClosure(_) => 15,
318+
319+
MicroOp::Exists { .. } => 10,
320+
MicroOp::BorrowGlobal { .. } => 10,
321+
MicroOp::BorrowGlobalMut { .. } => 20,
322+
MicroOp::MoveFrom { .. } => 20,
323+
MicroOp::MoveTo { .. } => 20,
308324
})
309325
}
310326
}

third_party/move/mono-move/core/src/instruction/mod.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
use crate::{
117117
align::MAX_ALIGN,
118118
interner::{InternedIdentifier, InternedModuleId},
119-
types::{display_type_list, InternedTypeList},
119+
types::{display_type, display_type_list, InternedType, InternedTypeList},
120120
FunctionPtr,
121121
};
122122
use std::fmt;
@@ -796,6 +796,57 @@ pub enum MicroOp {
796796
/// Unconditionally trigger a garbage collection cycle.
797797
/// Useful for testing GC correctness.
798798
ForceGC,
799+
800+
/// Stores a boolean value at destination offset if resource of the given
801+
/// type exists at the given address.
802+
Exists {
803+
addr: FrameOffset,
804+
ty: InternedType,
805+
dst: FrameOffset,
806+
},
807+
/// Stores a reference to the destination offset pointing to the resource
808+
/// of the given type exists at the given address. Aborts if resource does
809+
/// not exist.
810+
BorrowGlobal {
811+
addr: FrameOffset,
812+
ty: InternedType,
813+
dst: FrameOffset,
814+
},
815+
/// Stores a reference to the destination offset pointing to the resource
816+
/// of the given type exists at the given address. The reference points to
817+
/// the object owned by the current transaction's heap. Aborts if resource
818+
/// does not exist.
819+
///
820+
/// May trigger GC.
821+
BorrowGlobalMut {
822+
addr: FrameOffset,
823+
ty: InternedType,
824+
dst: FrameOffset,
825+
},
826+
/// Stores a reference to the destination offset pointing to the resource
827+
/// of the given type exists at the given address. The resource is then
828+
/// treated as "moved" from the global storage. The reference points to
829+
/// the object owned by the current transaction's heap. Aborts if resource
830+
/// does not exist.
831+
///
832+
/// May trigger GC.
833+
MoveFrom {
834+
addr: FrameOffset,
835+
ty: InternedType,
836+
dst: FrameOffset,
837+
},
838+
/// Stores a reference from the source offset to the global storage for the
839+
/// resource of the given type at the given address. The resource is then
840+
/// treated as "moved" to the global storage. Aborts if resource already
841+
/// exists.
842+
MoveTo {
843+
// TODO(correctness):
844+
// Move requires this to be a signer, using address for simplicity for now.
845+
addr: FrameOffset,
846+
ty: InternedType,
847+
src: FrameOffset,
848+
},
849+
799850
//======================================================================
800851
// Missing instructions
801852
//======================================================================
@@ -804,7 +855,6 @@ pub enum MicroOp {
804855
// - **Casting**: truncation and widening between integer types,
805856
// including signed casts.
806857
// - **Boolean**: logical Not, And, Or (distinct from bitwise).
807-
// - **Global storage**: MoveTo, MoveFrom, BorrowGlobal, Exists.
808858
// - **Runtime instrumentation**: tracing, profiling, coverage hooks.
809859
//======================================================================
810860

@@ -1189,6 +1239,35 @@ impl fmt::Display for MicroOp {
11891239
}
11901240
write!(f, "]")
11911241
},
1242+
MicroOp::Exists { addr, ty, dst } => {
1243+
write!(f, "Exists<")?;
1244+
display_type(f, *ty)?;
1245+
write!(f, "> [{}] <- addr=[{}], ty=", dst.0, addr.0)
1246+
},
1247+
MicroOp::BorrowGlobal { addr, ty, dst } => {
1248+
write!(f, "BorrowGlobal<")?;
1249+
display_type(f, *ty)?;
1250+
write!(f, "> [{}] <- addr=[{}], ty=", dst.0, addr.0)
1251+
},
1252+
MicroOp::BorrowGlobalMut { addr, ty, dst } => {
1253+
write!(f, "BorrowGlobalMut<")?;
1254+
display_type(f, *ty)?;
1255+
write!(f, "> [{}] <- addr=[{}], ty=", dst.0, addr.0)
1256+
},
1257+
MicroOp::MoveFrom { addr, ty, dst } => {
1258+
write!(f, "MoveFrom<")?;
1259+
display_type(f, *ty)?;
1260+
write!(f, "> [{}] <- addr=[{}], ty=", dst.0, addr.0)
1261+
},
1262+
MicroOp::MoveTo {
1263+
addr,
1264+
ty,
1265+
src,
1266+
} => {
1267+
write!(f, "MoveTo<")?;
1268+
display_type(f, *ty)?;
1269+
write!(f, "> addr=[{}], src=[{}]", addr.0, src.0)
1270+
},
11921271
}
11931272
}
11941273
}
@@ -1308,6 +1387,8 @@ impl MicroOp {
13081387
MicroOp::HeapNew { .. }
13091388
| MicroOp::VecPushBack { .. }
13101389
| MicroOp::PackClosure(_)
1390+
| MicroOp::BorrowGlobalMut { .. }
1391+
| MicroOp::MoveFrom { .. }
13111392
| MicroOp::ForceGC => true,
13121393

13131394
// Non-allocating.
@@ -1372,7 +1453,10 @@ impl MicroOp {
13721453
| MicroOp::IntBitXor(_)
13731454
| MicroOp::IntShl(_)
13741455
| MicroOp::IntShr(_)
1375-
| MicroOp::IntNegate(_) => false,
1456+
| MicroOp::IntNegate(_)
1457+
| MicroOp::Exists { .. }
1458+
| MicroOp::BorrowGlobal { .. }
1459+
| MicroOp::MoveTo { .. } => false,
13761460
}
13771461
}
13781462

third_party/move/mono-move/core/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod function;
88
mod instruction;
99
pub mod interner;
1010
mod prepared_module;
11+
pub mod storage;
1112
pub mod types;
1213

1314
pub use align::{
@@ -31,4 +32,8 @@ pub use interner::{Interner, ModuleId};
3132
pub use prepared_module::{
3233
FieldTypes, FunctionInstantiationSignature, FunctionSignature, PreparedModule,
3334
};
35+
pub use storage::{
36+
ModuleProvider, NoResourceProvider, ResourceProvider, ResourceProviderError, ResourceRead,
37+
NO_RESOURCE_PROVIDER,
38+
};
3439
pub use types::{convert_mut_to_immut_ref, strip_ref};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Aptos Foundation
2+
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE
3+
4+
//! Storage abstractions for the VM runtime to provide access to data or code.
5+
6+
pub mod module_provider;
7+
pub mod resource_provider;
8+
9+
pub use module_provider::ModuleProvider;
10+
pub use resource_provider::{
11+
NoResourceProvider, ResourceProvider, ResourceProviderError, ResourceRead, NO_RESOURCE_PROVIDER,
12+
};

third_party/move/mono-move/loader/src/module_provider.rs renamed to third_party/move/mono-move/core/src/storage/module_provider.rs

File renamed without changes.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) Aptos Foundation
2+
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE
3+
4+
//! Resource storage access for the runtime.
5+
6+
use crate::types::InternedType;
7+
use move_core_types::account_address::AccountAddress;
8+
use std::ptr::NonNull;
9+
use thiserror::Error;
10+
11+
/// Version of the read value (which can come from storage or from other
12+
/// transaction write).
13+
// TODO:
14+
// Replace with Block-STM transaction index and incarnation pair.
15+
pub type Version = u64;
16+
17+
/// Errors a [`ResourceProvider`] can surface. Backends classify their
18+
/// own failure modes into this enum as they grow.
19+
#[derive(Debug, Error)]
20+
pub enum ResourceProviderError {
21+
#[error("resource provider invariant violation: {0}")]
22+
InvariantViolation(String),
23+
}
24+
25+
/// Resource read returned to the VM. Every VM execution records reads of any
26+
/// value coming from global storage.
27+
#[derive(Clone, Copy, Debug)]
28+
pub enum ResourceRead {
29+
/// Resource does not exist at this key.
30+
None,
31+
/// Resource is allocated in some other arena or cache. For example, it
32+
/// can be a cached DB read or a write from soe transaction at lower
33+
/// version.
34+
// TODO(safety):
35+
// Figure out how to enforce compile-time guarantees here that owning
36+
// arena is alive.
37+
ExternalHeap {
38+
/// Just like any other VM value, the pointer points to the start of
39+
/// the value allocation. Value's header is at negative offset.
40+
// TODO(refactor): have a Value pointer unified API?
41+
ptr: NonNull<u8>,
42+
/// Version of this read from Block-STM. Used for read-set validation.
43+
version: Version,
44+
},
45+
}
46+
47+
/// Returns resource data from storage. Storage backend is not fixed
48+
/// and can be implemented for different clients:
49+
/// - tests,
50+
/// - Block-STM,
51+
/// - actual DB.
52+
pub trait ResourceProvider {
53+
/// Returns the resource of a particular type at the specified
54+
/// address. Returns [`ResourceRead::None`] if the resource does
55+
/// not exist. Returns a [`ResourceProviderError`] if the backend
56+
/// cannot satisfy the read.
57+
fn get_resource(
58+
&self,
59+
addr: AccountAddress,
60+
ty: InternedType,
61+
) -> Result<ResourceRead, ResourceProviderError>;
62+
}
63+
64+
/// Empty storage with no resources.
65+
pub struct NoResourceProvider;
66+
67+
impl ResourceProvider for NoResourceProvider {
68+
fn get_resource(
69+
&self,
70+
_addr: AccountAddress,
71+
_ty: InternedType,
72+
) -> Result<ResourceRead, ResourceProviderError> {
73+
Ok(ResourceRead::None)
74+
}
75+
}
76+
77+
// TODO(test):
78+
// This is only needed to make current tests work. Remove once specializer can emit
79+
// struct / enum operations or when testing framework is unified.
80+
pub static NO_RESOURCE_PROVIDER: NoResourceProvider = NoResourceProvider;

0 commit comments

Comments
 (0)