-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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-vm][closures] Type and Value representation and serialization #15670
base: main
Are you sure you want to change the base?
Conversation
⏱️ 1h 6m total CI duration on this PR
|
4972a5c
to
2fed681
Compare
2fed681
to
9c0814c
Compare
9c0814c
to
8b11451
Compare
8b11451
to
dcb6bc5
Compare
95728c9
to
6eea07c
Compare
dcb6bc5
to
bfc6f4c
Compare
6eea07c
to
06363e0
Compare
bfc6f4c
to
355525d
Compare
31df3f3
to
7e049a7
Compare
d4d5803
to
892bc51
Compare
7e049a7
to
9359cfa
Compare
892bc51
to
bf07d31
Compare
129ffe7
to
474593c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First almost full pass, need to look more, but there are already plenty of comments
let state = self.0.borrow(); | ||
match &*state { | ||
LazyLoadedFunctionState::Resolved { mask, .. } => *mask, | ||
LazyLoadedFunctionState::Unresolved { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we have a resolution_error: Some(..)
stored here? Do we just ignore it?
.iter() | ||
.map(|t| module_storage.fetch_ty(t)) | ||
.collect::<PartialVMResult<Vec<_>>>()?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should call
Type::verify_ty_arg_abilities(function.ty_param_abilities(), &ty_args)
.map_err(|e| e.finish(Location::Module(module_id.clone())))?;
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, Loader
has load_function(...) -> VMResult<LoadedFunction>
, can we reuse it? Just move this as default impl to ModuleStorage
(I did not d it because I hoped to remove V1 faster, but it can go to module storage easily).
} else { | ||
Err( | ||
PartialVMError::new(StatusCode::FUNCTION_RESOLUTION_FAILURE).with_message( | ||
"inconsistency between types of serialized captured \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add more info to debug?
if ok { | ||
for (ty, layout) in captured_arg_types.into_iter().zip(captured_layouts) { | ||
// Convert layout into TypeTag | ||
let serialized_tag: TypeTag = layout.try_into().map_err(|_| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think this even works:
impl TryInto<StructTag> for &MoveStructLayout {
type Error = anyhow::Error;
fn try_into(self) -> Result<StructTag, Self::Error> {
use MoveStructLayout::*;
match self {
Runtime(..) | RuntimeVariants(..) | WithFields(..) | WithVariants(..) => bail!(
"Invalid MoveTypeLayout -> StructTag conversion--needed MoveLayoutType::WithTypes"
),
WithTypes { type_, .. } => Ok(type_.clone()),
}
}
}
because here we have runtime layouts?
"unexpected type tag conversion failure", | ||
) | ||
})?; | ||
let arg_tag = converter.ty_to_ty_tag(ty)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have a generic type parameter in the function, this fails:
// from ty_to_ty_tag implementation
// References and type parameters cannot be converted to tags.
Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => {
return Err(
PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
.with_message(format!("No type tag for {:?}", ty)),
);
},
@@ -127,6 +127,7 @@ impl<'r, 'l> SessionExt<'r, 'l> { | |||
module_storage: &impl ModuleStorage, | |||
) -> VMResult<(VMChangeSet, ModuleWriteSet)> { | |||
let move_vm = self.inner.get_move_vm(); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unneeded change
bf07d31
to
1513955
Compare
474593c
to
bb8390f
Compare
1513955
to
4022451
Compare
bb8390f
to
24a5460
Compare
/// `LoadedFunction`. This is wrapped into a Rc so one can clone the | ||
/// function while sharing the loading state. | ||
#[derive(Clone, Tid)] | ||
pub(crate) struct LazyLoadedFunction(pub(crate) Rc<RefCell<LazyLoadedFunctionState>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should more like lazy loaded closure?
4022451
to
6f6afe3
Compare
24a5460
to
e6560c5
Compare
mask, | ||
captured, | ||
} = self; | ||
let mut s = serializer.serialize_seq(Some(4 + captured.len()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to double the length here as you would be serializing tuples
result | ||
}, | ||
Err(e) => { | ||
*resolution_error = Some(e.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was wondering why the interior mutability is needed here for error. If the resolution failed this function would fail and you don't need to store the error msg here?s
This PR implements the extensions needed in the file format for representing closures: - The type (SignatureToken) has a new variant `Function(args, result, abilities)` to represent a function type - The opcodes are extendeed by operations `ClosPack`, `ClosPackGeneric`, and `ClosEval` This supports bit masks for specifyinng which arguments of a function are captured when creating a closure. Bytecode verification is extended to support the new types and opcodes. The implementation of consistency, type, and reference safety should be complete. What is missing are: - File format serialization - Interpreter and value representation - Value serialization - Connection of the new types with the various other type representations
…d confusion with feature flags.
…re types [PR 2/n vm closures] The type `AbilitySet` is required in `MoveTypeLayout`, and the type `ClosureMask` in `MoveValue`. This PRs moves those both types from file_format into the core types crate.
…nto a trait Type conversions from runtime types to `MoveTypeLayout` and `TypeTag` currently are associated with the `Loader` type. However, they are needed for the `FunctionValueExtension` trait which needs to be constructed in contexts where no loader but only `ModuleStorage` exists. This PR moves the conversion functions into a new trait `TypeConverter`. The trait is then implemented two times based on `ModuleStorage` only and based on the existing `Loader`, for downwards compatibility.
[PR 3/n vm closures] This implements types and values for functions and closures, as well as serialization. For a detailed discussion, see the design doc. It does not yet implement the interpreter and runtime verification. Overview: - `MoveValue` and `MoveTypeLayout` are extended to represent closures and functions. A closure carries the function name, the closure mask, the function layout, and the captured arguments. The function layout enables serialization of the captured arguments without any context. - Runtime `Type` is extended by functions. - `ValueImpl` is extended by a closure variant. This representation uses `trait AbstractFunction` to represent the function value and implement core functionality like comparison and printing. The existing trait `FunctionValueExtension` is used to create `AbstracFunction` from the serialized representation. - Similar as with `MoveValue`, the serialized representation of `ValueImpl::Closure` contains the full function layout, enabling to deserialize the captured arguments context independently. This design has been chosen for robustness, avoiding a dependency of serialization from loaded functions, and to enable fully 'lazy' deserialization of closures. The later allows a contract to store hundreds of function values and on loading them from storage, avoiding loading the associated code until the moment the closure is actually executed. - `AbstractFunction` is implemented in the loader such that it can be either in 'unresolved' or in `resolved' state.
6f6afe3
to
f178823
Compare
e6560c5
to
b3492b5
Compare
f178823
to
2707a22
Compare
Description
[PR 4/n vm closures]
This implements types and values for functions and closures, as well as serialization. For a detailed discussion, see the design doc. It does not yet implement the interpreter and runtime verification.
Overview:
MoveValue
andMoveTypeLayout
are extended to represent closures and functions. A closure carries the function name, the closure mask, the layout of the captured arguments, and their values. The layout enables serialization of the captured arguments without any context.Type
is extended by functions.ValueImpl
is extended by a closure variant. This representation usestrait AbstractFunction
to represent the function value and implement core functionality like comparison and printing. The existing traitFunctionValueExtension
is used to createAbstractFunction
from the serialized representation.MoveValue
, the serialized representation ofValueImpl::Closure
contains the captured parameter layouts, enabling to deserialize the captured arguments context independently. This design has been chosen for robustness, avoiding a dependency of serialization from loaded functions, and to enable fully 'lazy' deserialization of closures. The later allows a contract to store hundreds of function values and on loading them from storage, avoiding loading the associated code until the moment the closure is actually executed.AbstractFunction
is implemented in the loader such that it can be either in 'unresolved' or in `resolved' state.How Has This Been Tested?
Testing postponed until e2e wiring is up
Type of Change
Which Components or Systems Does This Change Impact?