diff --git a/language/evm/move-to-yul/src/abi_native_functions.rs b/language/evm/move-to-yul/src/abi_native_functions.rs new file mode 100644 index 0000000000..5ada970d66 --- /dev/null +++ b/language/evm/move-to-yul/src/abi_native_functions.rs @@ -0,0 +1,364 @@ +// Copyright (c) The Diem Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + context::Context, + functions::FunctionGenerator, + native_functions::NativeFunctions, + solidity_ty::{SignatureDataLocation, SoliditySignature, SolidityType}, + yul_functions::{substitute_placeholders, YulFunction}, +}; +use itertools::Itertools; +use move_model::{ + emit, emitln, + model::{FunId, FunctionEnv, Parameter, QualifiedInstId}, +}; +use move_stackless_bytecode::function_target_pipeline::FunctionVariant; + +impl NativeFunctions { + /// Generate decode functions + pub(crate) fn define_decode_fun( + &self, + gen: &mut FunctionGenerator, + ctx: &Context, + fun_id: &QualifiedInstId, + solidity_sig_str_opt: Option, + ) { + let fun = ctx.env.get_function(fun_id.to_qualified_id()); + let mut sig = SoliditySignature::create_default_sig_for_decode_fun(ctx, &fun); + if let Some(solidity_sig_str) = solidity_sig_str_opt { + let parsed_sig_opt = + SoliditySignature::parse_into_solidity_signature(&solidity_sig_str, &fun); + if let Ok(parsed_sig) = parsed_sig_opt { + sig = parsed_sig; + } else if let Err(msg) = parsed_sig_opt { + ctx.env.error(&fun.get_loc(), &format!("{}", msg)); + return; + } + } + // Check compatibility + if !sig.check_sig_compatibility_for_decode_fun(ctx, &fun) { + return; + } + // Emit function header + let target = &ctx.targets.get_target(&fun, &FunctionVariant::Baseline); + let param = (0..target.get_parameter_count()) + .map(|idx| ctx.make_local_name(target, idx)) + .collect_vec(); + assert!(param.len() == 1); // the decode function only has one argument + let results = if target.get_return_count() == 0 { + "".to_string() + } else { + (0..target.get_return_count()) + .map(|i| ctx.make_result_name(target, i)) + .join(", ") + }; + let ret_results = if results.is_empty() { + "".to_string() + } else { + format!(" -> {} ", results) + }; + emit!( + ctx.writer, + "function {}({}){} ", + ctx.make_function_name(fun_id), + param[0], + ret_results + ); + // Generate the function body + ctx.emit_block(|| { + let mut local_name_idx = target.get_parameter_count(); // local variable + let pos_var = ctx.make_local_name(target, local_name_idx); + local_name_idx += 1; + emitln!(ctx.writer, "let {} := add({}, 32)", pos_var, param[0]); + let size_fun = gen.parent.call_builtin_str( + ctx, + YulFunction::MemoryLoadU64, + std::iter::once(param[0].clone()), + ); + let size_var = ctx.make_local_name(target, local_name_idx); + emitln!(ctx.writer, "let {} := {}", size_var, size_fun); + local_name_idx += 1; + let offset_var = ctx.make_local_name(target, local_name_idx); + emitln!( + ctx.writer, + "let {} := add({}, {})", + offset_var, + pos_var, + size_var + ); + + let fun_ret_types = fun.get_return_types(); + let abi_decode_from_memory = + gen.parent + .generate_abi_tuple_decoding_ret(ctx, &sig, fun_ret_types.clone(), true); + + emitln!( + ctx.writer, + "if gt({}, 0xffffffffffffffff) {{ {} }}", + pos_var, + gen.parent + .call_builtin_str(ctx, YulFunction::AbortBuiltin, std::iter::empty()) + ); + emitln!( + ctx.writer, + "if gt({}, 0xffffffffffffffff) {{ {} }}", + offset_var, + gen.parent + .call_builtin_str(ctx, YulFunction::AbortBuiltin, std::iter::empty()) + ); + + if !fun_ret_types.is_empty() { + emit!(ctx.writer, "{} := ", results); + } + emitln!( + ctx.writer, + "{}({}, {})", + abi_decode_from_memory, + pos_var, + offset_var + ); + }); + } + + /// Generate encode functions + pub(crate) fn define_encode_fun( + &self, + gen: &mut FunctionGenerator, + ctx: &Context, + fun_id: &QualifiedInstId, + solidity_sig_str_opt: Option, + packed_flag: bool, + ) { + let fun = ctx.env.get_function(fun_id.to_qualified_id()); + let mut sig = SoliditySignature::create_default_sig_for_encode_fun(ctx, &fun); + if let Some(solidity_sig_str) = solidity_sig_str_opt { + let parsed_sig_opt = + SoliditySignature::parse_into_solidity_signature(&solidity_sig_str, &fun); + if let Ok(parsed_sig) = parsed_sig_opt { + sig = parsed_sig; + } else if let Err(msg) = parsed_sig_opt { + ctx.env.error(&fun.get_loc(), &format!("{}", msg)); + return; + } + } + // Check compatibility + if !sig.check_sig_compatibility_for_encode_fun(ctx, &fun) { + return; + } + // Emit function header + let target = &ctx.targets.get_target(&fun, &FunctionVariant::Baseline); + let params = (0..target.get_parameter_count()) + .map(|idx| ctx.make_local_name(target, idx)) + .join(","); + let results = (0..target.get_return_count()) + .map(|i| ctx.make_result_name(target, i)) + .collect_vec(); + assert!(results.len() == 1); // the encode function only has one return value + let ret_results = format!(" -> {} ", results[0]); + emit!( + ctx.writer, + "function {}({}){} ", + ctx.make_function_name(fun_id), + params, + ret_results + ); + // Generate the function body + ctx.emit_block(|| { + emitln!( + ctx.writer, + "{} := mload({})", + results[0], + substitute_placeholders("${MEM_SIZE_LOC}").unwrap() + ); + let mut local_name_idx = target.get_parameter_count(); + let encode_start_var = ctx.make_local_name(target, local_name_idx); + local_name_idx += 1; + emitln!( + ctx.writer, + "let {} := add({}, 32)", + encode_start_var, + results[0] + ); + emitln!( + ctx.writer, + "if gt({}, 0xffffffffffffffff) {{ {} }}", + encode_start_var, + gen.parent + .call_builtin_str(ctx, YulFunction::AbortBuiltin, std::iter::empty()) + ); + + let encode_end_var = ctx.make_local_name(target, local_name_idx); + local_name_idx += 1; + + let fun_para_types = fun.get_parameter_types(); + let abi_encode = + gen.parent + .generate_abi_tuple_encoding_para(ctx, &sig, fun_para_types, packed_flag); + emit!(ctx.writer, "let {} := ", encode_end_var); + let args = if params.is_empty() { + "".to_string() + } else { + format!(",{}", params) + }; + emitln!(ctx.writer, "{}({}{})", abi_encode, encode_start_var, args); + emitln!( + ctx.writer, + "if gt({}, 0xffffffffffffffff) {{ {} }}", + encode_end_var, + gen.parent + .call_builtin_str(ctx, YulFunction::AbortBuiltin, std::iter::empty()) + ); + let encode_size_var = ctx.make_local_name(target, local_name_idx); + local_name_idx += 1; + emitln!( + ctx.writer, + "let {} := sub({}, {})", + encode_size_var, + encode_end_var, + encode_start_var + ); + // store the current length of vector + gen.parent.call_builtin( + ctx, + YulFunction::MemoryStoreU64, + vec![results[0].clone(), encode_size_var.clone()].into_iter(), + ); + + // store the capacity of vector + let encode_capacity_var = ctx.make_local_name(target, local_name_idx); + let capacity_call = gen.parent.call_builtin_str( + ctx, + YulFunction::ClosestGreaterPowerOfTwo, + std::iter::once(encode_size_var), + ); + emitln!( + ctx.writer, + "let {} := {}", + encode_capacity_var, + capacity_call + ); + gen.parent.call_builtin( + ctx, + YulFunction::MemoryStoreU64, + vec![format!("add({}, 8)", results[0]), encode_capacity_var].into_iter(), + ); + emitln!( + ctx.writer, + "mstore({}, {})", + substitute_placeholders("${MEM_SIZE_LOC}").unwrap(), + encode_end_var + ); + }); + } +} + +impl SoliditySignature { + pub(crate) fn create_default_sig_for_decode_fun(ctx: &Context, fun: &FunctionEnv<'_>) -> Self { + let fun_name = fun.symbol_pool().string(fun.get_name()).to_string(); + let mut para_type_lst = vec![]; + for Parameter(para_name, move_ty) in fun.get_parameters() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, true); + para_type_lst.push(( + solidity_ty, + fun.symbol_pool().string(para_name).to_string(), + SignatureDataLocation::Memory, + )); + } + let mut ret_type_lst = vec![]; + + for move_ty in fun.get_return_types() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); + ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); + } + + SoliditySignature { + sig_name: fun_name, + para_types: para_type_lst, + ret_types: ret_type_lst, + } + } + + pub(crate) fn create_default_sig_for_encode_fun(ctx: &Context, fun: &FunctionEnv<'_>) -> Self { + let fun_name = fun.symbol_pool().string(fun.get_name()).to_string(); + let mut para_type_lst = vec![]; + for Parameter(para_name, move_ty) in fun.get_parameters() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); + para_type_lst.push(( + solidity_ty, + fun.symbol_pool().string(para_name).to_string(), + SignatureDataLocation::Memory, + )); + } + let mut ret_type_lst = vec![]; + + for move_ty in fun.get_return_types() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, true); + ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); + } + + SoliditySignature { + sig_name: fun_name, + para_types: para_type_lst, + ret_types: ret_type_lst, + } + } + + /// Check whether the user defined solidity signature is compatible with the Move signature + pub(crate) fn check_sig_compatibility_for_decode_fun( + &self, + ctx: &Context, + fun: &FunctionEnv<'_>, + ) -> bool { + if !self.check_sig_compatibility(ctx, fun) { + ctx.env.error( + &fun.get_loc(), + "solidity signature is not compatible with the move signature", + ); + return false; + } + let sig_para_vec = self + .para_types + .iter() + .map(|(ty, _, _)| ty) + .collect::>(); + if sig_para_vec.len() == 1 { + if let SolidityType::Bytes = sig_para_vec[0] { + // the only argument must be of type bytes (vector) + return true; + } + } + ctx.env.error( + &fun.get_loc(), + "decode function must only has one argument of type bytes", + ); + false + } + + /// Check whether the user defined solidity signature is compatible with the Move signature + pub(crate) fn check_sig_compatibility_for_encode_fun( + &self, + ctx: &Context, + fun: &FunctionEnv<'_>, + ) -> bool { + if !self.check_sig_compatibility(ctx, fun) { + ctx.env.error( + &fun.get_loc(), + "solidity signature is not compatible with the move signature", + ); + return false; + } + let sig_ret_vec = self.ret_types.iter().map(|(ty, _)| ty).collect::>(); + if sig_ret_vec.len() == 1 { + if let SolidityType::Bytes = sig_ret_vec[0] { + // the only return value must be of type bytes (vector) + return true; + } + } + ctx.env.error( + &fun.get_loc(), + "encode function must only has one return value of type bytes", + ); + false + } +} diff --git a/language/evm/move-to-yul/src/attributes.rs b/language/evm/move-to-yul/src/attributes.rs index cf7d48d8c5..49c00e7816 100644 --- a/language/evm/move-to-yul/src/attributes.rs +++ b/language/evm/move-to-yul/src/attributes.rs @@ -21,6 +21,9 @@ const SIGNATURE: &str = "sig"; const EVENT_ATTR: &str = "event"; const VIEW_ATTR: &str = "view"; const PURE_ATTR: &str = "pure"; +const DECODE_ATTR: &str = "decode"; +const ENCODE_ATTR: &str = "encode"; +const ENCODE_PACKED_ATTR: &str = "encode_packed"; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum FunctionAttribute { @@ -90,6 +93,26 @@ pub fn extract_event_signature(st: &StructEnv<'_>) -> Option { ) } +/// Extract the solidity signature from the decode attribute +pub fn extract_decode_signature(fun: &FunctionEnv<'_>) -> Option { + extract_attr_value_str( + fun.module_env.env, + fun.get_attributes(), + DECODE_ATTR, + SIGNATURE, + ) +} + +/// Extract the solidity signature from the encode attribute +pub fn extract_encode_signature(fun: &FunctionEnv<'_>, packed_flag: bool) -> Option { + let attr = if packed_flag { + ENCODE_PACKED_ATTR + } else { + ENCODE_ATTR + }; + extract_attr_value_str(fun.module_env.env, fun.get_attributes(), attr, SIGNATURE) +} + /// Check whether an attribute is present in an attribute list. pub fn has_attr(env: &GlobalEnv, attrs: &[Attribute], name: &str, simple_flag: bool) -> bool { let is_empty = |args: &Vec| { @@ -218,3 +241,20 @@ pub(crate) fn construct_fun_attribute(fun: &FunctionEnv<'_>) -> Option) -> bool { + has_attr(fun.module_env.env, fun.get_attributes(), DECODE_ATTR, false) +} + +pub fn is_encode(fun: &FunctionEnv<'_>) -> bool { + has_attr(fun.module_env.env, fun.get_attributes(), ENCODE_ATTR, false) +} + +pub fn is_encode_packed(fun: &FunctionEnv<'_>) -> bool { + has_attr( + fun.module_env.env, + fun.get_attributes(), + ENCODE_PACKED_ATTR, + false, + ) +} diff --git a/language/evm/move-to-yul/src/dispatcher_generator.rs b/language/evm/move-to-yul/src/dispatcher_generator.rs index e08d8c7b9b..f46bd964fc 100644 --- a/language/evm/move-to-yul/src/dispatcher_generator.rs +++ b/language/evm/move-to-yul/src/dispatcher_generator.rs @@ -109,7 +109,7 @@ impl Generator { ) -> SoliditySignature { let extracted_sig_opt = attributes::extract_callable_or_create_signature(fun, callable_flag); - let mut sig = SoliditySignature::create_default_solidity_signature(ctx, fun, None); + let mut sig = SoliditySignature::create_default_solidity_signature(ctx, fun); if let Some(extracted_sig) = extracted_sig_opt { let parsed_sig_opt = SoliditySignature::parse_into_solidity_signature(&extracted_sig, fun); @@ -344,6 +344,34 @@ impl Generator { self.need_auxiliary_function(function_name, Box::new(generate_fun)) } + fn generate_left_align(&mut self, ty: &SolidityType) -> String { + use crate::solidity_ty::{SolidityPrimitiveType::*, SolidityType::*}; + assert!(ty.is_value_type()); + let ty = ty.clone(); + let name_prefix = "left_align"; + let function_name = format!("{}_{}", name_prefix, ty); + let generate_fun = move |_gen: &mut Generator, ctx: &Context| { + emit!(ctx.writer, "(value) -> aligned "); + ctx.emit_block(|| { + let bits = 256 + - match ty { + Primitive(p) => match p { + Bool => 8, + Int(size) | Uint(size) => size, + Address(_) => 160, + _ => panic!("wrong types"), + }, + BytesStatic(_) => 256, + _ => panic!("wrong types"), + }; + if bits > 0 { + emitln!(ctx.writer, "aligned := shl({}, value)", bits); + } + }); + }; + self.need_auxiliary_function(function_name, Box::new(generate_fun)) + } + /// Generate the validator function, which is used in the decode function. fn generate_validator(&mut self, ty: &SolidityType) -> String { let name_prefix = "validator"; @@ -676,6 +704,11 @@ impl Generator { ) ); // store the capacity of vector + let compute_capacity_str = gen.call_builtin_str( + ctx, + YulFunction::ClosestGreaterPowerOfTwo, + std::iter::once("length".to_string()), + ); emitln!( ctx.writer, "{}", @@ -685,7 +718,7 @@ impl Generator { vec![ // TODO: simplify implementation of MemoryStoreU64? "add(array, 8)".to_string(), // skip the length which is a u64 (8 bytes) - "length".to_string() + compute_capacity_str ] .into_iter() ) @@ -787,13 +820,18 @@ impl Generator { ) ); // capacity of vector + let compute_capacity_str = gen.call_builtin_str( + ctx, + YulFunction::ClosestGreaterPowerOfTwo, + std::iter::once("length".to_string()), + ); emitln!( ctx.writer, "{}", gen.call_builtin_str( ctx, YulFunction::MemoryStoreU64, - vec!["add(array, 8)".to_string(), "length".to_string()].into_iter() + vec!["add(array, 8)".to_string(), compute_capacity_str].into_iter() ) ); @@ -847,18 +885,22 @@ impl Generator { } /// Generate encoding functions for primitive types. - fn generate_abi_encoding_primitive_type(&mut self, ty: &SolidityType) -> String { + fn generate_abi_encoding_primitive_type( + &mut self, + ty: &SolidityType, + options: EncodingOptions, + ) -> String { let name_prefix = "abi_encode"; let function_name = format!("{}_{}", name_prefix, ty); let ty = ty.clone(); // need to move into lambda let generate_fun = move |gen: &mut Generator, ctx: &Context| { emit!(ctx.writer, "(value, pos) "); ctx.emit_block(|| { - emitln!( - ctx.writer, - "mstore(pos, {}(value))", - gen.generate_cleanup(&ty) - ); + let mut store_str = format!("{}(value)", gen.generate_cleanup(&ty)); + if !options.padded { + store_str = format!("{}({})", gen.generate_left_align(&ty), store_str); + } + emitln!(ctx.writer, "mstore(pos, {})", store_str); }); }; self.need_auxiliary_function(function_name, Box::new(generate_fun)) @@ -876,7 +918,7 @@ impl Generator { use SolidityType::*; // TODO: Struct match ty { - Primitive(_) => self.generate_abi_encoding_primitive_type(ty), + Primitive(_) => self.generate_abi_encoding_primitive_type(ty, options), DynamicArray(_) | StaticArray(_, _) | Bytes | BytesStatic(_) | SolidityString => { self.generate_abi_encoding_array_type(ctx, ty, move_ty, options) } @@ -898,7 +940,7 @@ impl Generator { let move_ty = move_ty.clone(); let encoding_function_name = match ty { - Primitive(_) => self.generate_abi_encoding_primitive_type(&ty), + Primitive(_) => self.generate_abi_encoding_primitive_type(&ty, options.clone()), DynamicArray(_) | StaticArray(_, _) | Bytes | BytesStatic(_) | SolidityString => { self.generate_abi_encoding_array_type(ctx, &ty, &move_ty, options.clone()) } @@ -971,7 +1013,7 @@ impl Generator { // copy the memory to pos gen.call_builtin( ctx, - YulFunction::CopyMemoryU8, + YulFunction::CopyMemory, vec![ "add(value, 0x20)".to_string(), "pos".to_string(), @@ -1240,6 +1282,32 @@ impl Generator { self.generate_abi_tuple_encoding(ctx, param_types, param_locs, move_tys) } + /// Generate encoding functions for tuple in parameters. + pub(crate) fn generate_abi_tuple_encoding_para( + &mut self, + ctx: &Context, + sig: &SoliditySignature, + move_tys: Vec, + packed_flag: bool, + ) -> String { + let param_types = sig + .para_types + .iter() + .map(|(ty, _, _)| ty.clone()) + .collect_vec(); // need to move into lambda + let param_locs = sig + .para_types + .iter() + .map(|(_, _, loc)| loc.clone()) + .collect_vec(); + if packed_flag { + self.generate_abi_tuple_encoding_packed(ctx, param_types, param_locs, move_tys) + } else { + self.generate_abi_tuple_encoding(ctx, param_types, param_locs, move_tys) + } + } + + /// Generate encodePacked function pub(crate) fn generate_abi_tuple_encoding_packed( &mut self, ctx: &Context, @@ -1271,7 +1339,7 @@ impl Generator { } emit!(ctx.writer, "(pos {}) -> end ", value_params); ctx.emit_block(|| { - let overall_type_head_vec = abi_head_sizes_vec(¶m_types, true); + let overall_type_head_vec = abi_head_sizes_vec(¶m_types, options.padded); for (stack_pos, (((ty, ty_size), _loc), move_ty)) in overall_type_head_vec .iter() .zip(param_locs.iter()) @@ -1303,7 +1371,8 @@ impl Generator { self.need_auxiliary_function(function_name, Box::new(generate_fun)) } - pub(crate) fn generate_pack( + /// Generate encodePacked function with keccak256 hashing + pub(crate) fn generate_packed_hashed( &mut self, ctx: &Context, tys: Vec, @@ -1339,6 +1408,11 @@ impl Generator { generate_encoding_packed, value_params ); + emitln!( + ctx.writer, + "mstore({}, end)", + substitute_placeholders("${MEM_SIZE_LOC}").unwrap(), + ); emitln!(ctx.writer, "hash := keccak256(pos, sub(end, pos))"); }); }; diff --git a/language/evm/move-to-yul/src/events.rs b/language/evm/move-to-yul/src/events.rs index 932eeed2df..c521254aaf 100644 --- a/language/evm/move-to-yul/src/events.rs +++ b/language/evm/move-to-yul/src/events.rs @@ -64,7 +64,7 @@ impl EventSignature { .map(|(offset, ty, field_name)| (offset, ty, field_name)) .collect_vec() { - let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty); // implicit mapping from a move type to a solidity type + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); // implicit mapping from a move type to a solidity type para_type_lst.push((offset, solidity_ty, move_ty.clone(), false, field_name)); // no index by default } @@ -243,7 +243,7 @@ pub(crate) fn define_emit_fun( ctx.writer, "let {} := {}({})", new_var, - gen.parent.generate_pack( + gen.parent.generate_packed_hashed( ctx, vec![solidity_ty.clone()], vec![SignatureDataLocation::Memory], @@ -316,6 +316,12 @@ pub(crate) fn define_emit_fun( pos_var, indexed_str ); + emitln!( + ctx.writer, + "mstore({}, {})", + substitute_placeholders("${MEM_SIZE_LOC}").unwrap(), + end_var + ); }); }); } diff --git a/language/evm/move-to-yul/src/external_functions.rs b/language/evm/move-to-yul/src/external_functions.rs index 981b1634be..9a9dcb6f9e 100644 --- a/language/evm/move-to-yul/src/external_functions.rs +++ b/language/evm/move-to-yul/src/external_functions.rs @@ -6,13 +6,13 @@ use crate::{ dispatcher_generator::TARGET_CONTRACT_DOES_NOT_CONTAIN_CODE, functions::FunctionGenerator, native_functions::NativeFunctions, - solidity_ty::{abi_head_sizes_sum, SoliditySignature, SolidityType}, + solidity_ty::{abi_head_sizes_sum, SignatureDataLocation, SoliditySignature, SolidityType}, yul_functions::{substitute_placeholders, YulFunction}, }; use itertools::Itertools; use move_model::{ emit, emitln, - model::{FunId, FunctionEnv, QualifiedInstId}, + model::{FunId, FunctionEnv, Parameter, QualifiedInstId}, ty::Type, }; use move_stackless_bytecode::function_target_pipeline::FunctionVariant; @@ -25,27 +25,31 @@ impl NativeFunctions { gen: &mut FunctionGenerator, ctx: &Context, fun_id: &QualifiedInstId, - solidity_sig_str: &str, + solidity_sig_str_opt: Option, ) { let fun = ctx.env.get_function(fun_id.to_qualified_id()); let (external_flag, result_ty) = self.check_external_result(ctx, &fun); - let mut sig = - SoliditySignature::create_default_solidity_signature(ctx, &fun, result_ty.clone()); - let parsed_sig_opt = - SoliditySignature::parse_into_solidity_signature(solidity_sig_str, &fun); - // Check compatibility - if let Ok(parsed_sig) = parsed_sig_opt { - if !parsed_sig.check_sig_compatibility_for_external_fun(ctx, &fun, result_ty.clone()) { - ctx.env.error( - &fun.get_loc(), - "solidity signature is not compatible with the move signature", - ); - return; - } else { + let mut sig = SoliditySignature::create_default_solidity_signature_for_external_fun( + ctx, + &fun, + result_ty.clone(), + ); + if let Some(solidity_sig_str) = solidity_sig_str_opt { + let parsed_sig_opt = + SoliditySignature::parse_into_solidity_signature(&solidity_sig_str, &fun); + // Check compatibility + if let Ok(parsed_sig) = parsed_sig_opt { sig = parsed_sig; + } else if let Err(msg) = parsed_sig_opt { + ctx.env.error(&fun.get_loc(), &format!("{}", msg)); + return; } - } else if let Err(msg) = parsed_sig_opt { - ctx.env.error(&fun.get_loc(), &format!("{}", msg)); + } + if !sig.check_sig_compatibility_for_external_fun(ctx, &fun, result_ty.clone()) { + ctx.env.error( + &fun.get_loc(), + "solidity signature is not compatible with the move signature", + ); return; } @@ -428,6 +432,50 @@ impl NativeFunctions { } impl SoliditySignature { + /// Create a default solidity signature from a move function signature + pub(crate) fn create_default_solidity_signature_for_external_fun( + ctx: &Context, + fun: &FunctionEnv<'_>, + external_result_ty_opt: Option, + ) -> Self { + let fun_name = fun.symbol_pool().string(fun.get_name()).to_string(); + let mut para_type_lst = vec![]; + if fun.get_parameter_count() < 1 { + ctx.env.error( + &fun.get_loc(), + "external function must have at least one argument", + ); + } else { + for Parameter(para_name, move_ty) in fun.get_parameters().into_iter().skip(1) { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); // implicit mapping from a move type to a solidity type + para_type_lst.push(( + solidity_ty, + fun.symbol_pool().string(para_name).to_string(), + SignatureDataLocation::Memory, // memory is used by default + )); + } + } + + let mut ret_type_lst = vec![]; + if let Some(move_ty) = external_result_ty_opt { + if !ctx.is_unit_ty(&move_ty) { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); + ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); + } + } else { + for move_ty in fun.get_return_types() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); + ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); + } + } + + SoliditySignature { + sig_name: fun_name, + para_types: para_type_lst, + ret_types: ret_type_lst, + } + } + /// Check whether the user defined solidity signature is compatible with the Move signature pub(crate) fn check_sig_compatibility_for_external_fun( &self, diff --git a/language/evm/move-to-yul/src/lib.rs b/language/evm/move-to-yul/src/lib.rs index a538e2a37a..1d367dfe71 100644 --- a/language/evm/move-to-yul/src/lib.rs +++ b/language/evm/move-to-yul/src/lib.rs @@ -3,6 +3,7 @@ #![forbid(unsafe_code)] +mod abi_native_functions; mod abi_signature; mod attributes; mod context; diff --git a/language/evm/move-to-yul/src/native_functions.rs b/language/evm/move-to-yul/src/native_functions.rs index 9ea83d82c8..c8b31eeca7 100644 --- a/language/evm/move-to-yul/src/native_functions.rs +++ b/language/evm/move-to-yul/src/native_functions.rs @@ -51,19 +51,12 @@ impl NativeFunctions { } else { let fun = &ctx.env.get_function(fun_id.to_qualified_id()); if attributes::is_external_fun(fun) { - if let Some(extracted_sig_str) = attributes::extract_external_signature(fun) { - self.define_external_fun(gen, ctx, fun_id, &extracted_sig_str) - } else { - ctx.env.error( - &gen.parent.contract_loc, - &format!( - "external function {} must have a valid solidity signature", - ctx.env - .get_function(fun_id.to_qualified_id()) - .get_full_name_str() - ), - ) - } + self.define_external_fun( + gen, + ctx, + fun_id, + attributes::extract_external_signature(fun), + ) } else if self.is_emit_fun(fun) { let elem_type = fun_id.inst.get(0).unwrap(); // obtain the event type if let Type::Struct(mid, sid, inst) = elem_type { @@ -88,6 +81,24 @@ impl NativeFunctions { ) } } + } else if attributes::is_decode(fun) { + self.define_decode_fun(gen, ctx, fun_id, attributes::extract_decode_signature(fun)); + } else if attributes::is_encode(fun) { + self.define_encode_fun( + gen, + ctx, + fun_id, + attributes::extract_encode_signature(fun, false), + false, + ) + } else if attributes::is_encode_packed(fun) { + self.define_encode_fun( + gen, + ctx, + fun_id, + attributes::extract_encode_signature(fun, true), + true, + ) } else { ctx.env.error( &gen.parent.contract_loc, @@ -198,7 +209,7 @@ impl NativeFunctions { ), gen.parent.call_builtin_str( ctx, - YulFunction::CopyMemoryU8, + YulFunction::CopyMemory, vec![ "add(message, 32)".to_string(), "pos".to_string(), diff --git a/language/evm/move-to-yul/src/solidity_ty.rs b/language/evm/move-to-yul/src/solidity_ty.rs index 1bdccd1a74..e719deb08b 100644 --- a/language/evm/move-to-yul/src/solidity_ty.rs +++ b/language/evm/move-to-yul/src/solidity_ty.rs @@ -194,13 +194,13 @@ impl SolidityType { } /// Parse a move type into a solidity type - pub(crate) fn translate_from_move(ctx: &Context, ty: &Type) -> Self { + pub(crate) fn translate_from_move(ctx: &Context, ty: &Type, bytes_flag: bool) -> Self { use PrimitiveType::*; use Type::*; let generate_tuple = |tys: &Vec| { let s_type = tys .iter() - .map(|t| Self::translate_from_move(ctx, t)) + .map(|t| Self::translate_from_move(ctx, t, bytes_flag)) .collect::>(); SolidityType::Tuple(s_type) }; @@ -217,7 +217,15 @@ impl SolidityType { } }, Vector(ety) => { - SolidityType::DynamicArray(Box::new(Self::translate_from_move(ctx, ety))) + if bytes_flag { + if let Primitive(U8) = **ety { + // translate vector to Bytes + return SolidityType::Bytes; + } + } + SolidityType::DynamicArray(Box::new(Self::translate_from_move( + ctx, ety, bytes_flag, + ))) } Tuple(tys) => generate_tuple(tys), Struct(mid, sid, _) => { @@ -420,7 +428,7 @@ impl SolidityType { } }, StaticArray(ty, size) => { - let mut size = ty.abi_head_size(padded) * size; + let mut size = ty.abi_head_size(true) * size; if padded { size = ((size + 31) / 32) * 32; } @@ -491,15 +499,11 @@ impl fmt::Display for SoliditySignature { impl SoliditySignature { /// Create a default solidity signature from a move function signature - pub fn create_default_solidity_signature( - ctx: &Context, - fun: &FunctionEnv<'_>, - ty_opt: Option, - ) -> Self { + pub(crate) fn create_default_solidity_signature(ctx: &Context, fun: &FunctionEnv<'_>) -> Self { let fun_name = fun.symbol_pool().string(fun.get_name()).to_string(); let mut para_type_lst = vec![]; for Parameter(para_name, move_ty) in fun.get_parameters() { - let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty); // implicit mapping from a move type to a solidity type + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); // implicit mapping from a move type to a solidity type para_type_lst.push(( solidity_ty, fun.symbol_pool().string(para_name).to_string(), @@ -507,16 +511,9 @@ impl SoliditySignature { )); } let mut ret_type_lst = vec![]; - if let Some(move_ty) = ty_opt { - if !ctx.is_unit_ty(&move_ty) { - let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty); - ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); - } - } else { - for move_ty in fun.get_return_types() { - let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty); - ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); - } + for move_ty in fun.get_return_types() { + let solidity_ty = SolidityType::translate_from_move(ctx, &move_ty, false); + ret_type_lst.push((solidity_ty, SignatureDataLocation::Memory)); } SoliditySignature { diff --git a/language/evm/move-to-yul/src/yul_functions.rs b/language/evm/move-to-yul/src/yul_functions.rs index 2092c69058..7823ce0cb7 100644 --- a/language/evm/move-to-yul/src/yul_functions.rs +++ b/language/evm/move-to-yul/src/yul_functions.rs @@ -603,20 +603,7 @@ CopyMemory: "(src, dst, size) { let src_word := and(mload(add(src, overflow_offs)), not(mask)) mstore(add(dst, overflow_offs), or(dst_word, src_word)) } -}", - -CopyMemoryU8: "(src, dst, size) { - let i := 0 - for { } lt(i, size) { i := add(i, 32) } { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } - } -}", +}" dep ToWordOffs, CheckMemorySize: "(len) -> checked_len { if gt(len, 0xffffffffffffffff) { $AbortBuiltin() } diff --git a/language/evm/move-to-yul/tests/NativeFunctions.exp b/language/evm/move-to-yul/tests/NativeFunctions.exp index 5ade690cbb..049b35da70 100644 --- a/language/evm/move-to-yul/tests/NativeFunctions.exp +++ b/language/evm/move-to-yul/tests/NativeFunctions.exp @@ -199,7 +199,7 @@ object "test_A2_NativeFunctions_test_abort" { let size := $MemoryLoadU64(message) mstore(pos, size) pos := add(pos, 32) - $CopyMemoryU8(add(message, 32), pos, size) + $CopyMemory(add(message, 32), pos, size) size := $RoundUp(size) let end := add(pos, size) revert(head, sub(end, head)) @@ -240,6 +240,10 @@ object "test_A2_NativeFunctions_test_abort" { function $MaskForSize(size) -> mask { mask := sub(shl(shl(3, size), 1), 1) } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } function $MemoryLoadBytes(offs, size) -> val { // Lower bit where the value in the higher bytes ends let bit_end := shl(3, sub(32, size)) @@ -260,16 +264,18 @@ object "test_A2_NativeFunctions_test_abort" { function $MemoryStoreU64(offs, val) { $MemoryStoreBytes(offs, 8, val) } - function $CopyMemoryU8(src, dst, size) { + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) let i := 0 - for { } lt(i, size) { i := add(i, 32) } { + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } function $ClosestGreaterPowerOfTwo(x) -> r { @@ -286,7 +292,7 @@ object "test_A2_NativeFunctions_test_abort" { } } } -===> Test result of NativeFunctions::test_abort: Revert(Reverted) (used_gas=673): [8, 195, 121, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 101, 114, 114, 111, 114, 32, 109, 101, 115, 115, 97, 103, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +===> Test result of NativeFunctions::test_abort: Revert(Reverted) (used_gas=696): [8, 195, 121, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 101, 114, 114, 111, 114, 32, 109, 101, 115, 115, 97, 103, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // test of NativeFunctions::test_concat /* ======================================= diff --git a/language/evm/move-to-yul/tests/TestABINative.exp b/language/evm/move-to-yul/tests/TestABINative.exp new file mode 100644 index 0000000000..663a217f13 --- /dev/null +++ b/language/evm/move-to-yul/tests/TestABINative.exp @@ -0,0 +1,2681 @@ +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "Empty" { + code { + codecopy(0, dataoffset("Empty_deployed"), datasize("Empty_deployed")) + return(0, datasize("Empty_deployed")) + } + object "Empty_deployed" { + code { + mstore(0, memoryguard(160)) + if iszero(lt(calldatasize(), 4)) + { + let selector := $Shr(calldataload(0), 224) + switch selector + default {} + } + $Abort(97) + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $Shr(x, y) -> r { + r := shr(y, x) + } + } + } +} + + +!! Succeeded compiling Yul + + +!! Unit tests + +// test of M::test_decode_two_bytes1 +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_decode_two_bytes1" { + code { + mstore(0, memoryguard(160)) + A2_M_test_decode_two_bytes1() + return (0, 0) + function A2_M_test_decode_two_bytes1() { + let i, i_1, $t5, $t6, $t7, $t8, $t9, $t10, $t11, $t12, $t13, $t14, $t15, $t16, $t17, $t18, $t19, $t20, $t21, $t22, $t23, $t24, $t25, $t26, $t27, $t28, $t29, $t30, $t31, $t32, $t33, $t34, $t35, $t36, $t37, $t38, $t39, $t40, $t41, $t42, $t43, $t44, $t45, $t46, $t47 + let $locals := $Malloc(96) + let $block := 3 + for {} true {} { + switch $block + case 2 { + // label L3 + // $t8 := 31 + $t8 := 31 + // $t9 := <=($t0, $t8) + $t9 := $LtEq(i, $t8) + // if ($t9) goto L0 else goto L2 + switch $t9 + case 0 { $block := 5 } + default { $block := 4 } + } + case 3 { + // $t2 := Vector::empty() + mstore($locals, A1_Vector_empty$u8$()) + // $t5 := borrow_local($t2) + $t5 := $MakePtr(false, $locals) + // $t6 := 42 + $t6 := 42 + // Vector::push_back($t5, $t6) + A1_Vector_push_back$u8$($t5, $t6) + // $t7 := 1 + $t7 := 1 + // $t0 := $t7 + i := $t7 + // goto L3 + $block := 2 + } + case 4 { + // label L0 + // $t10 := borrow_local($t2) + $t10 := $MakePtr(false, $locals) + // $t11 := 0 + $t11 := 0 + // Vector::push_back($t10, $t11) + A1_Vector_push_back$u8$($t10, $t11) + // $t12 := 1 + $t12 := 1 + // $t0 := +($t0, $t12) + i := $AddU64(i, $t12) + // goto L3 + $block := 2 + } + case 5 { + // label L2 + // $t13 := borrow_local($t2) + $t13 := $MakePtr(false, $locals) + // $t14 := 43 + $t14 := 43 + // Vector::push_back($t13, $t14) + A1_Vector_push_back$u8$($t13, $t14) + // $t15 := 1 + $t15 := 1 + // $t1 := $t15 + i_1 := $t15 + // goto L7 + $block := 6 + } + case 6 { + // label L7 + // $t16 := 31 + $t16 := 31 + // $t17 := <=($t1, $t16) + $t17 := $LtEq(i_1, $t16) + // if ($t17) goto L4 else goto L6 + switch $t17 + case 0 { $block := 8 } + default { $block := 7 } + } + case 7 { + // label L4 + // $t18 := borrow_local($t2) + $t18 := $MakePtr(false, $locals) + // $t19 := 0 + $t19 := 0 + // Vector::push_back($t18, $t19) + A1_Vector_push_back$u8$($t18, $t19) + // $t20 := 1 + $t20 := 1 + // $t1 := +($t1, $t20) + i_1 := $AddU64(i_1, $t20) + // goto L7 + $block := 6 + } + case 8 { + // label L6 + // $t21 := move($t2) + $t21 := mload($locals) + // ($t22, $t23) := M::decode_two_bytes1($t21) + $t22, $t23 := A2_M_decode_two_bytes1($t21) + // $t4 := $t23 + mstore(add($locals, 64), $t23) + // $t3 := $t22 + mstore(add($locals, 32), $t22) + // $t24 := borrow_local($t3) + $t24 := $MakePtr(false, add($locals, 32)) + // $t25 := Vector::length($t24) + $t25 := A1_Vector_length$u8$($t24) + // $t26 := 1 + $t26 := 1 + // $t27 := ==($t25, $t26) + $t27 := $Eq($t25, $t26) + // if ($t27) goto L8 else goto L10 + switch $t27 + case 0 { $block := 10 } + default { $block := 9 } + } + case 9 { + // label L8 + // goto L11 + $block := 11 + } + case 10 { + // label L10 + // $t28 := 101 + $t28 := 101 + // abort($t28) + $Abort($t28) + } + case 11 { + // label L11 + // $t29 := borrow_local($t4) + $t29 := $MakePtr(false, add($locals, 64)) + // $t30 := Vector::length($t29) + $t30 := A1_Vector_length$u8$($t29) + // $t31 := 1 + $t31 := 1 + // $t32 := ==($t30, $t31) + $t32 := $Eq($t30, $t31) + // if ($t32) goto L12 else goto L14 + switch $t32 + case 0 { $block := 13 } + default { $block := 12 } + } + case 12 { + // label L12 + // goto L15 + $block := 14 + } + case 13 { + // label L14 + // $t33 := 102 + $t33 := 102 + // abort($t33) + $Abort($t33) + } + case 14 { + // label L15 + // $t34 := borrow_local($t3) + $t34 := $MakePtr(false, add($locals, 32)) + // $t35 := 0 + $t35 := 0 + // $t36 := Vector::borrow($t34, $t35) + $t36 := A1_Vector_borrow$u8$($t34, $t35) + // $t37 := read_ref($t36) + $t37 := $LoadU8($t36) + // $t38 := 42 + $t38 := 42 + // $t39 := ==($t37, $t38) + $t39 := $Eq($t37, $t38) + // if ($t39) goto L16 else goto L18 + switch $t39 + case 0 { $block := 16 } + default { $block := 15 } + } + case 15 { + // label L16 + // goto L19 + $block := 17 + } + case 16 { + // label L18 + // $t40 := 103 + $t40 := 103 + // abort($t40) + $Abort($t40) + } + case 17 { + // label L19 + // $t41 := borrow_local($t4) + $t41 := $MakePtr(false, add($locals, 64)) + // $t42 := 0 + $t42 := 0 + // $t43 := Vector::borrow($t41, $t42) + $t43 := A1_Vector_borrow$u8$($t41, $t42) + // $t44 := read_ref($t43) + $t44 := $LoadU8($t43) + // $t45 := 43 + $t45 := 43 + // $t46 := ==($t44, $t45) + $t46 := $Eq($t44, $t45) + // if ($t46) goto L20 else goto L22 + switch $t46 + case 0 { $block := 19 } + default { $block := 18 } + } + case 18 { + // label L20 + // goto L23 + $block := 20 + } + case 19 { + // label L22 + // $t47 := 104 + $t47 := 104 + // abort($t47) + $Abort($t47) + } + case 20 { + // label L23 + // return () + $Free($locals, 96) + leave + } + } + } + + function A1_Vector_borrow$u8$(v_ref, i) -> e_ptr { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + let size := $LoadU64(v_ptr) + if $GtEq(i, size) { $AbortBuiltin() } + e_ptr := $IndexPtr(v_ptr, add(32, mul(i, 1))) + } + function A1_Vector_length$u8$(v_ref) -> len { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + len := $LoadU64(v_ptr) + } + function A2_M_decode_two_bytes1(input) -> $result0, $result1 { + let $t1 := add(input, 32) + let $t2 := $MemoryLoadU64(input) + let $t3 := add($t1, $t2) + if gt($t1, 0xffffffffffffffff) { $AbortBuiltin() } + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + $result0, $result1 := abi_decode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$_from_memory($t1, $t3) + } + function A1_Vector_push_back$u8$(v_ref, e) { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + let size := $LoadU64(v_ptr) + let e_ptr := $IndexPtr(v_ptr, add(32, mul(size, 1))) + $StoreU8(e_ptr, e) + size := add(size, 1) + $StoreU64(v_ptr, size) + let capacity := $LoadU64($IndexPtr(v_ptr, 8)) + if and(iszero($IsStoragePtr(v_ptr)), eq(size, capacity)) { + let new_v_offs := $ResizeVector(v_offs, capacity, 1) + $StoreU256(v_ref, new_v_offs) + } + } + function A1_Vector_empty$u8$() -> vector { + vector := $Malloc(34) + $MemoryStoreU64(add(vector, 8), 2) + } + function abi_decode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$_from_memory(headStart, dataEnd) -> value_0, value_1 { + if slt(sub(dataEnd, headStart), 64) { $Abort(96) } + { + let offset := 0 + value_0 := abi_decode_bytes1_vec$u8$_from_memory(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value_1 := abi_decode_bytes1_vec$u8$_from_memory(add(headStart, offset), dataEnd) + } + } + function abi_decode_bytes1_vec$u8$_from_memory(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { $Abort(94) } + let length := 1 + let size := 33 + array := abi_decode_available_length__bytes1_from_memory(offset, length, size, end) + } + function abi_decode_available_length__bytes1_from_memory(src, length, size, end) -> array { + array := $Malloc($CheckMemorySize(size)) + $MemoryStoreU64(array, length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) + let dst := add(array, 32) + if gt(add(src, sub(size, 32)), end) { $Abort(93) } + $CopyFromMemoryToMemory(src, dst, length) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $Free(offs, size) { + } + function $MakePtr(is_storage, offs) -> ptr { + ptr := or(is_storage, shl(1, offs)) + } + function $IsStoragePtr(ptr) -> b { + b := and(ptr, 0x1) + } + function $OffsetPtr(ptr) -> offs { + offs := shr(1, ptr) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ExtractBytes(word, start, size) -> bytes { + switch size + case 1 { + // use the faster byte primitive + bytes := byte(start, word) + } + default { + // As we have big endian, we need to right shift the value from + // where the highest byte starts in the word (32 - start), minus + // the size. + let shift_bits := shl(3, sub(sub(32, start), size)) + bytes := and(shr(shift_bits, word), $MaskForSize(size)) + } + } + function $InjectBytes(word, start, size, bytes) -> new_word { + let shift_bits := shl(3, sub(sub(32, start), size)) + // Blend out the bits which we inject + let neg_mask := not(shl(shift_bits, $MaskForSize(size))) + word := and(word, neg_mask) + // Overlay the bits we inject + new_word := or(word, shl(shift_bits, bytes)) + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $OverflowBytes(byte_offset, size) -> overflow_bytes { + let available_bytes := sub(32, byte_offset) + switch gt(size, available_bytes) + case 0 { + overflow_bytes := 0 + } + default { + overflow_bytes := sub(size, available_bytes) + } + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $StorageLoadBytes(offs, size) -> val { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + val := $ExtractBytes(sload(key), byte_offs, size) + let overflow_bytes := $OverflowBytes(byte_offs, size) + if $LogicalNot(iszero(overflow_bytes)) { + key := $StorageKey(0, add(word_offs, 1)) + let extra_bytes := $ExtractBytes(sload(key), 0, overflow_bytes) + val := or(shl(shl(3, overflow_bytes), val), extra_bytes) + } + } + function $StorageStoreBytes(offs, size, bytes) { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + let overflow_bytes := $OverflowBytes(byte_offs, size) + switch overflow_bytes + case 0 { + sstore(key, $InjectBytes(sload(key), byte_offs, size, bytes)) + } + default { + // Shift the higher bytes to the right + let used_bytes := sub(size, overflow_bytes) + let higher_bytes := shr(used_bytes, bytes) + let lower_bytes := and(bytes, $MaskForSize(overflow_bytes)) + sstore(key, $InjectBytes(sload(key), byte_offs, used_bytes, higher_bytes)) + key := $StorageKey(0, add(word_offs, 1)) + sstore(key, $InjectBytes(sload(key), 0, overflow_bytes, lower_bytes)) + } + } + function $StorageKey(group, word) -> key { + mstore(32, word) + mstore(64, shl(224, group)) + key := keccak256(32, 36) + } + function $IndexPtr(ptr, offs) -> new_ptr { + new_ptr := $MakePtr($IsStoragePtr(ptr), add($OffsetPtr(ptr), offs)) + } + function $LoadU8(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU8(offs) + } + default { + val := $StorageLoadU8(offs) + } + } + function $MemoryLoadU8(offs) -> val { + val := $MemoryLoadBytes(offs, 1) + } + function $StorageLoadU8(offs) -> val { + val := $StorageLoadBytes(offs, 1) + } + function $StoreU8(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU8(offs, val) + } + default { + $StorageStoreU8(offs, val) + } + } + function $MemoryStoreU8(offs, val) { + // Shortcut via special instruction + mstore8(offs, val) + } + function $StorageStoreU8(offs, val) { + $StorageStoreBytes(offs, 1, val) + } + function $LoadU64(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU64(offs) + } + default { + val := $StorageLoadU64(offs) + } + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $StorageLoadU64(offs) -> val { + val := $StorageLoadBytes(offs, 8) + } + function $StoreU64(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU64(offs, val) + } + default { + $StorageStoreU64(offs, val) + } + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $StorageStoreU64(offs, val) { + $StorageStoreBytes(offs, 8, val) + } + function $LoadU256(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU256(offs) + } + default { + val := $StorageLoadU256(offs) + } + } + function $MemoryLoadU256(offs) -> val { + val := $MemoryLoadBytes(offs, 32) + } + function $StorageLoadU256(offs) -> val { + val := $StorageLoadBytes(offs, 32) + } + function $StoreU256(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU256(offs, val) + } + default { + $StorageStoreU256(offs, val) + } + } + function $MemoryStoreU256(offs, val) { + $MemoryStoreBytes(offs, 32, val) + } + function $StorageStoreU256(offs, val) { + $StorageStoreBytes(offs, 32, val) + } + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + mstore(add(dst, i), mload(add(src, i))) + } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) + } + } + function $CheckMemorySize(len) -> checked_len { + if gt(len, 0xffffffffffffffff) { $AbortBuiltin() } + checked_len := len + } + function $CopyFromMemoryToMemory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + function $ResizeVector(v_offs, capacity, type_size) -> new_v_offs { + let new_capacity := mul(capacity, 2) + let data_size := add(32, mul(capacity, type_size)) + let new_data_size := add(32, mul(new_capacity, type_size)) + new_v_offs := $Malloc(new_data_size) + $CopyMemory(v_offs, new_v_offs, data_size) + // update capacity at new location + $MemoryStoreU64(add(new_v_offs, 8), new_capacity) + $Free(v_offs, data_size) + } + function $AddU64(x, y) -> r { + if lt(sub(0xffffffffffffffff, x), y) { $AbortBuiltin() } + r := add(x, y) + } + function $GtEq(x, y) -> r { + r := or(gt(x, y), eq(x, y)) + } + function $LtEq(x, y) -> r { + r := or(lt(x, y), eq(x, y)) + } + function $Eq(x, y) -> r { + r := eq(x, y) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } + } +} +===> Test result of M::test_decode_two_bytes1: Succeed(Stopped) (used_gas=66864): [] + +// test of M::test_decode_two_u8 +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_decode_two_u8" { + code { + mstore(0, memoryguard(160)) + A2_M_test_decode_two_u8() + return (0, 0) + function A2_M_test_decode_two_u8() { + let i, i_1, v1, v2, $t5, $t6, $t7, $t8, $t9, $t10, $t11, $t12, $t13, $t14, $t15, $t16, $t17, $t18, $t19, $t20, $t21, $t22, $t23, $t24, $t25, $t26, $t27, $t28, $t29 + let $locals := $Malloc(32) + let $block := 3 + for {} true {} { + switch $block + case 2 { + // label L3 + // $t6 := 31 + $t6 := 31 + // $t7 := <=($t0, $t6) + $t7 := $LtEq(i, $t6) + // if ($t7) goto L0 else goto L2 + switch $t7 + case 0 { $block := 5 } + default { $block := 4 } + } + case 3 { + // $t2 := Vector::empty() + mstore($locals, A1_Vector_empty$u8$()) + // $t5 := 1 + $t5 := 1 + // $t0 := $t5 + i := $t5 + // goto L3 + $block := 2 + } + case 4 { + // label L0 + // $t8 := borrow_local($t2) + $t8 := $MakePtr(false, $locals) + // $t9 := 0 + $t9 := 0 + // Vector::push_back($t8, $t9) + A1_Vector_push_back$u8$($t8, $t9) + // $t10 := 1 + $t10 := 1 + // $t0 := +($t0, $t10) + i := $AddU64(i, $t10) + // goto L3 + $block := 2 + } + case 5 { + // label L2 + // $t11 := borrow_local($t2) + $t11 := $MakePtr(false, $locals) + // $t12 := 42 + $t12 := 42 + // Vector::push_back($t11, $t12) + A1_Vector_push_back$u8$($t11, $t12) + // $t13 := 1 + $t13 := 1 + // $t1 := $t13 + i_1 := $t13 + // goto L7 + $block := 6 + } + case 6 { + // label L7 + // $t14 := 31 + $t14 := 31 + // $t15 := <=($t1, $t14) + $t15 := $LtEq(i_1, $t14) + // if ($t15) goto L4 else goto L6 + switch $t15 + case 0 { $block := 8 } + default { $block := 7 } + } + case 7 { + // label L4 + // $t16 := borrow_local($t2) + $t16 := $MakePtr(false, $locals) + // $t17 := 0 + $t17 := 0 + // Vector::push_back($t16, $t17) + A1_Vector_push_back$u8$($t16, $t17) + // $t18 := 1 + $t18 := 1 + // $t1 := +($t1, $t18) + i_1 := $AddU64(i_1, $t18) + // goto L7 + $block := 6 + } + case 8 { + // label L6 + // $t19 := borrow_local($t2) + $t19 := $MakePtr(false, $locals) + // $t20 := 43 + $t20 := 43 + // Vector::push_back($t19, $t20) + A1_Vector_push_back$u8$($t19, $t20) + // $t21 := move($t2) + $t21 := mload($locals) + // ($t22, $t23) := M::decode_two_u8($t21) + $t22, $t23 := A2_M_decode_two_u8($t21) + // $t24 := 42 + $t24 := 42 + // $t25 := ==($t22, $t24) + $t25 := $Eq($t22, $t24) + // if ($t25) goto L8 else goto L10 + switch $t25 + case 0 { $block := 10 } + default { $block := 9 } + } + case 9 { + // label L8 + // goto L11 + $block := 11 + } + case 10 { + // label L10 + // $t26 := 101 + $t26 := 101 + // abort($t26) + $Abort($t26) + } + case 11 { + // label L11 + // $t27 := 43 + $t27 := 43 + // $t28 := ==($t23, $t27) + $t28 := $Eq($t23, $t27) + // if ($t28) goto L12 else goto L14 + switch $t28 + case 0 { $block := 13 } + default { $block := 12 } + } + case 12 { + // label L12 + // goto L15 + $block := 14 + } + case 13 { + // label L14 + // $t29 := 102 + $t29 := 102 + // abort($t29) + $Abort($t29) + } + case 14 { + // label L15 + // return () + $Free($locals, 32) + leave + } + } + } + + function A2_M_decode_two_u8(input) -> $result0, $result1 { + let $t1 := add(input, 32) + let $t2 := $MemoryLoadU64(input) + let $t3 := add($t1, $t2) + if gt($t1, 0xffffffffffffffff) { $AbortBuiltin() } + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + $result0, $result1 := abi_decode_tuple_$uint8_uint8$_$u8_u8$_from_memory($t1, $t3) + } + function A1_Vector_push_back$u8$(v_ref, e) { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + let size := $LoadU64(v_ptr) + let e_ptr := $IndexPtr(v_ptr, add(32, mul(size, 1))) + $StoreU8(e_ptr, e) + size := add(size, 1) + $StoreU64(v_ptr, size) + let capacity := $LoadU64($IndexPtr(v_ptr, 8)) + if and(iszero($IsStoragePtr(v_ptr)), eq(size, capacity)) { + let new_v_offs := $ResizeVector(v_offs, capacity, 1) + $StoreU256(v_ref, new_v_offs) + } + } + function A1_Vector_empty$u8$() -> vector { + vector := $Malloc(34) + $MemoryStoreU64(add(vector, 8), 2) + } + function abi_decode_tuple_$uint8_uint8$_$u8_u8$_from_memory(headStart, dataEnd) -> value_0, value_1 { + if slt(sub(dataEnd, headStart), 64) { $Abort(96) } + { + let offset := 0 + value_0 := abi_decode_uint8_from_memory(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value_1 := abi_decode_uint8_from_memory(add(headStart, offset), dataEnd) + } + } + function abi_decode_uint8_from_memory(offset, end) -> value { + value := mload(offset) + validator_uint8(value) + } + function validator_uint8(value) { + if iszero(eq(value, cleanup_uint8(value))) { $Abort(95) } + } + function cleanup_uint8(value) -> cleaned { + cleaned := and(value, 0xff) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $Free(offs, size) { + } + function $MakePtr(is_storage, offs) -> ptr { + ptr := or(is_storage, shl(1, offs)) + } + function $IsStoragePtr(ptr) -> b { + b := and(ptr, 0x1) + } + function $OffsetPtr(ptr) -> offs { + offs := shr(1, ptr) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ExtractBytes(word, start, size) -> bytes { + switch size + case 1 { + // use the faster byte primitive + bytes := byte(start, word) + } + default { + // As we have big endian, we need to right shift the value from + // where the highest byte starts in the word (32 - start), minus + // the size. + let shift_bits := shl(3, sub(sub(32, start), size)) + bytes := and(shr(shift_bits, word), $MaskForSize(size)) + } + } + function $InjectBytes(word, start, size, bytes) -> new_word { + let shift_bits := shl(3, sub(sub(32, start), size)) + // Blend out the bits which we inject + let neg_mask := not(shl(shift_bits, $MaskForSize(size))) + word := and(word, neg_mask) + // Overlay the bits we inject + new_word := or(word, shl(shift_bits, bytes)) + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $OverflowBytes(byte_offset, size) -> overflow_bytes { + let available_bytes := sub(32, byte_offset) + switch gt(size, available_bytes) + case 0 { + overflow_bytes := 0 + } + default { + overflow_bytes := sub(size, available_bytes) + } + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $StorageLoadBytes(offs, size) -> val { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + val := $ExtractBytes(sload(key), byte_offs, size) + let overflow_bytes := $OverflowBytes(byte_offs, size) + if $LogicalNot(iszero(overflow_bytes)) { + key := $StorageKey(0, add(word_offs, 1)) + let extra_bytes := $ExtractBytes(sload(key), 0, overflow_bytes) + val := or(shl(shl(3, overflow_bytes), val), extra_bytes) + } + } + function $StorageStoreBytes(offs, size, bytes) { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + let overflow_bytes := $OverflowBytes(byte_offs, size) + switch overflow_bytes + case 0 { + sstore(key, $InjectBytes(sload(key), byte_offs, size, bytes)) + } + default { + // Shift the higher bytes to the right + let used_bytes := sub(size, overflow_bytes) + let higher_bytes := shr(used_bytes, bytes) + let lower_bytes := and(bytes, $MaskForSize(overflow_bytes)) + sstore(key, $InjectBytes(sload(key), byte_offs, used_bytes, higher_bytes)) + key := $StorageKey(0, add(word_offs, 1)) + sstore(key, $InjectBytes(sload(key), 0, overflow_bytes, lower_bytes)) + } + } + function $StorageKey(group, word) -> key { + mstore(32, word) + mstore(64, shl(224, group)) + key := keccak256(32, 36) + } + function $IndexPtr(ptr, offs) -> new_ptr { + new_ptr := $MakePtr($IsStoragePtr(ptr), add($OffsetPtr(ptr), offs)) + } + function $StoreU8(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU8(offs, val) + } + default { + $StorageStoreU8(offs, val) + } + } + function $MemoryStoreU8(offs, val) { + // Shortcut via special instruction + mstore8(offs, val) + } + function $StorageStoreU8(offs, val) { + $StorageStoreBytes(offs, 1, val) + } + function $LoadU64(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU64(offs) + } + default { + val := $StorageLoadU64(offs) + } + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $StorageLoadU64(offs) -> val { + val := $StorageLoadBytes(offs, 8) + } + function $StoreU64(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU64(offs, val) + } + default { + $StorageStoreU64(offs, val) + } + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $StorageStoreU64(offs, val) { + $StorageStoreBytes(offs, 8, val) + } + function $LoadU256(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU256(offs) + } + default { + val := $StorageLoadU256(offs) + } + } + function $MemoryLoadU256(offs) -> val { + val := $MemoryLoadBytes(offs, 32) + } + function $StorageLoadU256(offs) -> val { + val := $StorageLoadBytes(offs, 32) + } + function $StoreU256(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU256(offs, val) + } + default { + $StorageStoreU256(offs, val) + } + } + function $MemoryStoreU256(offs, val) { + $MemoryStoreBytes(offs, 32, val) + } + function $StorageStoreU256(offs, val) { + $StorageStoreBytes(offs, 32, val) + } + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + mstore(add(dst, i), mload(add(src, i))) + } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) + } + } + function $ResizeVector(v_offs, capacity, type_size) -> new_v_offs { + let new_capacity := mul(capacity, 2) + let data_size := add(32, mul(capacity, type_size)) + let new_data_size := add(32, mul(new_capacity, type_size)) + new_v_offs := $Malloc(new_data_size) + $CopyMemory(v_offs, new_v_offs, data_size) + // update capacity at new location + $MemoryStoreU64(add(new_v_offs, 8), new_capacity) + $Free(v_offs, data_size) + } + function $AddU64(x, y) -> r { + if lt(sub(0xffffffffffffffff, x), y) { $AbortBuiltin() } + r := add(x, y) + } + function $LtEq(x, y) -> r { + r := or(lt(x, y), eq(x, y)) + } + function $Eq(x, y) -> r { + r := eq(x, y) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + } +} +===> Test result of M::test_decode_two_u8: Succeed(Stopped) (used_gas=64646): [] + +// test of M::test_encode_packed_string +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_encode_packed_string" { + code { + mstore(0, memoryguard(160)) + A2_M_test_encode_packed_string() + return (0, 0) + function A2_M_test_encode_packed_string() { + let $t0, $t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8, $t9, $t10, $t11, $t12, $t13, $t14, $t15, $t16, $t17, $t18, $t19, $t20, $t21, $t22, $t23, $t24, $t25, $t26, $t27, $t28, $t29, $t30, $t31 + let $block := 4 + for {} true {} { + switch $block + case 2 { + // label L0 + // goto L3 + $block := 5 + } + case 3 { + // label L2 + // $t5 := 100 + $t5 := 100 + // abort($t5) + $Abort($t5) + } + case 4 { + // $t0 := [] + $t0 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(0))) + $MemoryStoreU64($t0, 0) + $MemoryStoreU64(add($t0, 8), $ClosestGreaterPowerOfTwo(0)) + copy_literal_string_to_memory_21418693(add($t0, 32)) + // $t1 := [] + $t1 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(0))) + $MemoryStoreU64($t1, 0) + $MemoryStoreU64(add($t1, 8), $ClosestGreaterPowerOfTwo(0)) + copy_literal_string_to_memory_21418693(add($t1, 32)) + // $t2 := M::encode_packed_string($t0, $t1) + $t2 := A2_M_encode_packed_string($t0, $t1) + // $t3 := [] + $t3 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(0))) + $MemoryStoreU64($t3, 0) + $MemoryStoreU64(add($t3, 8), $ClosestGreaterPowerOfTwo(0)) + copy_literal_string_to_memory_21418693(add($t3, 32)) + // $t4 := ==($t2, $t3) + $t4 := $EqVector($t2, $t3, 1) + // if ($t4) goto L0 else goto L2 + switch $t4 + case 0 { $block := 3 } + default { $block := 2 } + } + case 5 { + // label L3 + // $t6 := [49] + $t6 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(1))) + $MemoryStoreU64($t6, 1) + $MemoryStoreU64(add($t6, 8), $ClosestGreaterPowerOfTwo(1)) + copy_literal_string_to_memory_2868747976(add($t6, 32)) + // $t7 := [50] + $t7 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(1))) + $MemoryStoreU64($t7, 1) + $MemoryStoreU64(add($t7, 8), $ClosestGreaterPowerOfTwo(1)) + copy_literal_string_to_memory_4015750317(add($t7, 32)) + // $t8 := M::encode_packed_string($t6, $t7) + $t8 := A2_M_encode_packed_string($t6, $t7) + // $t9 := [49, 50] + $t9 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(2))) + $MemoryStoreU64($t9, 2) + $MemoryStoreU64(add($t9, 8), $ClosestGreaterPowerOfTwo(2)) + copy_literal_string_to_memory_141265791(add($t9, 32)) + // $t10 := ==($t8, $t9) + $t10 := $EqVector($t8, $t9, 1) + // if ($t10) goto L4 else goto L6 + switch $t10 + case 0 { $block := 7 } + default { $block := 6 } + } + case 6 { + // label L4 + // goto L7 + $block := 8 + } + case 7 { + // label L6 + // $t11 := 101 + $t11 := 101 + // abort($t11) + $Abort($t11) + } + case 8 { + // label L7 + // $t12 := [] + $t12 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(0))) + $MemoryStoreU64($t12, 0) + $MemoryStoreU64(add($t12, 8), $ClosestGreaterPowerOfTwo(0)) + copy_literal_string_to_memory_21418693(add($t12, 32)) + // $t13 := [97, 98, 99] + $t13 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(3))) + $MemoryStoreU64($t13, 3) + $MemoryStoreU64(add($t13, 8), $ClosestGreaterPowerOfTwo(3)) + copy_literal_string_to_memory_2053440334(add($t13, 32)) + // $t14 := M::encode_packed_string($t12, $t13) + $t14 := A2_M_encode_packed_string($t12, $t13) + // $t15 := [97, 98, 99] + $t15 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(3))) + $MemoryStoreU64($t15, 3) + $MemoryStoreU64(add($t15, 8), $ClosestGreaterPowerOfTwo(3)) + copy_literal_string_to_memory_2053440334(add($t15, 32)) + // $t16 := ==($t14, $t15) + $t16 := $EqVector($t14, $t15, 1) + // if ($t16) goto L8 else goto L10 + switch $t16 + case 0 { $block := 10 } + default { $block := 9 } + } + case 9 { + // label L8 + // goto L11 + $block := 11 + } + case 10 { + // label L10 + // $t17 := 102 + $t17 := 102 + // abort($t17) + $Abort($t17) + } + case 11 { + // label L11 + // $t18 := [97] + $t18 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(1))) + $MemoryStoreU64($t18, 1) + $MemoryStoreU64(add($t18, 8), $ClosestGreaterPowerOfTwo(1)) + copy_literal_string_to_memory_371573306(add($t18, 32)) + // $t19 := [98, 99] + $t19 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(2))) + $MemoryStoreU64($t19, 2) + $MemoryStoreU64(add($t19, 8), $ClosestGreaterPowerOfTwo(2)) + copy_literal_string_to_memory_3119208230(add($t19, 32)) + // $t20 := M::encode_packed_string($t18, $t19) + $t20 := A2_M_encode_packed_string($t18, $t19) + // $t21 := [100, 101] + $t21 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(2))) + $MemoryStoreU64($t21, 2) + $MemoryStoreU64(add($t21, 8), $ClosestGreaterPowerOfTwo(2)) + copy_literal_string_to_memory_1933910203(add($t21, 32)) + // $t22 := M::encode_packed_string($t20, $t21) + $t22 := A2_M_encode_packed_string($t20, $t21) + // $t23 := [97, 98, 99, 100, 101] + $t23 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(5))) + $MemoryStoreU64($t23, 5) + $MemoryStoreU64(add($t23, 8), $ClosestGreaterPowerOfTwo(5)) + copy_literal_string_to_memory_3871831907(add($t23, 32)) + // $t24 := ==($t22, $t23) + $t24 := $EqVector($t22, $t23, 1) + // if ($t24) goto L12 else goto L14 + switch $t24 + case 0 { $block := 13 } + default { $block := 12 } + } + case 12 { + // label L12 + // goto L15 + $block := 14 + } + case 13 { + // label L14 + // $t25 := 103 + $t25 := 103 + // abort($t25) + $Abort($t25) + } + case 14 { + // label L15 + // $t26 := [116, 101, 115, 116] + $t26 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(4))) + $MemoryStoreU64($t26, 4) + $MemoryStoreU64(add($t26, 8), $ClosestGreaterPowerOfTwo(4)) + copy_literal_string_to_memory_1610556060(add($t26, 32)) + // $t27 := [] + $t27 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(0))) + $MemoryStoreU64($t27, 0) + $MemoryStoreU64(add($t27, 8), $ClosestGreaterPowerOfTwo(0)) + copy_literal_string_to_memory_21418693(add($t27, 32)) + // $t28 := M::encode_packed_string($t26, $t27) + $t28 := A2_M_encode_packed_string($t26, $t27) + // $t29 := [116, 101, 115, 116] + $t29 := $Malloc(add(32, $ClosestGreaterPowerOfTwo(4))) + $MemoryStoreU64($t29, 4) + $MemoryStoreU64(add($t29, 8), $ClosestGreaterPowerOfTwo(4)) + copy_literal_string_to_memory_1610556060(add($t29, 32)) + // $t30 := ==($t28, $t29) + $t30 := $EqVector($t28, $t29, 1) + // if ($t30) goto L16 else goto L18 + switch $t30 + case 0 { $block := 16 } + default { $block := 15 } + } + case 15 { + // label L16 + // goto L19 + $block := 17 + } + case 16 { + // label L18 + // $t31 := 104 + $t31 := 104 + // abort($t31) + $Abort($t31) + } + case 17 { + // label L19 + // return () + leave + } + } + } + + function A2_M_encode_packed_string(input_1,input_2) -> $result { + $result := mload(0) + let $t2 := add($result, 32) + if gt($t2, 0xffffffffffffffff) { $AbortBuiltin() } + let $t3 := abi_encode_tuple_packed_$string_string$_$vec$u8$_vec$u8$$_not_padded_inplace($t2,input_1,input_2) + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + let $t4 := sub($t3, $t2) + $MemoryStoreU64($result, $t4) + let $t5 := $ClosestGreaterPowerOfTwo($t4) + $MemoryStoreU64(add($result, 8), $t5) + mstore(0, $t3) + } + function abi_encode_tuple_packed_$string_string$_$vec$u8$_vec$u8$$_not_padded_inplace(pos ,value_0, value_1) -> end { + pos := abi_encode_string_not_padded_inplace(value_0, pos) + pos := abi_encode_string_not_padded_inplace(value_1, pos) + end := pos + } + function abi_encode_string_not_padded_inplace(value, pos) -> end{ + let size := $MemoryLoadU64(value) + $CopyMemory(add(value, 0x20), pos, size) + end := add(pos, size) + } + function copy_literal_string_to_memory_1610556060(value) { + $MemoryStoreU8(value, 116) + value := add(value, 1) + $MemoryStoreU8(value, 101) + value := add(value, 1) + $MemoryStoreU8(value, 115) + value := add(value, 1) + $MemoryStoreU8(value, 116) + value := add(value, 1) + } + function copy_literal_string_to_memory_21418693(value) { + } + function copy_literal_string_to_memory_3871831907(value) { + $MemoryStoreU8(value, 97) + value := add(value, 1) + $MemoryStoreU8(value, 98) + value := add(value, 1) + $MemoryStoreU8(value, 99) + value := add(value, 1) + $MemoryStoreU8(value, 100) + value := add(value, 1) + $MemoryStoreU8(value, 101) + value := add(value, 1) + } + function copy_literal_string_to_memory_1933910203(value) { + $MemoryStoreU8(value, 100) + value := add(value, 1) + $MemoryStoreU8(value, 101) + value := add(value, 1) + } + function copy_literal_string_to_memory_3119208230(value) { + $MemoryStoreU8(value, 98) + value := add(value, 1) + $MemoryStoreU8(value, 99) + value := add(value, 1) + } + function copy_literal_string_to_memory_371573306(value) { + $MemoryStoreU8(value, 97) + value := add(value, 1) + } + function copy_literal_string_to_memory_2053440334(value) { + $MemoryStoreU8(value, 97) + value := add(value, 1) + $MemoryStoreU8(value, 98) + value := add(value, 1) + $MemoryStoreU8(value, 99) + value := add(value, 1) + } + function copy_literal_string_to_memory_141265791(value) { + $MemoryStoreU8(value, 49) + value := add(value, 1) + $MemoryStoreU8(value, 50) + value := add(value, 1) + } + function copy_literal_string_to_memory_4015750317(value) { + $MemoryStoreU8(value, 50) + value := add(value, 1) + } + function copy_literal_string_to_memory_2868747976(value) { + $MemoryStoreU8(value, 49) + value := add(value, 1) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $MemoryStoreU8(offs, val) { + // Shortcut via special instruction + mstore8(offs, val) + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + mstore(add(dst, i), mload(add(src, i))) + } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) + } + } + function $EqVector(x, y, elem_size) -> r { + let len_x := $MemoryLoadU64(x) + let len_y := $MemoryLoadU64(y) + if $Neq(len_x, len_y) { + r := false + leave + } + let data_size_bytes := mul(elem_size, len_x) + let num_words, overflow_bytes := $ToWordOffs(data_size_bytes) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + if $Neq(mload(add(x, add(i, 32))), mload(add(y, add(i, 32)))) { + r := false + leave + } + } + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let x_overflow := mload(add(x, add(overflow_offs, 32))) + let y_overflow := mload(add(y, add(overflow_offs, 32))) + r := eq(or(mask, x_overflow), or(mask, y_overflow)) + } + function $Neq(x, y) -> r { + r := $LogicalNot(eq(x, y)) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } + } +} +===> Test result of M::test_encode_packed_string: Succeed(Stopped) (used_gas=10647): [] + +// test of M::test_encode_packed_uint16 +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_encode_packed_uint16" { + code { + mstore(0, memoryguard(160)) + A2_M_test_encode_packed_uint16() + return (0, 0) + function A2_M_test_encode_packed_uint16() { + let v1, v2, $t3, $t4, $t5, $t6, $t7, $t8, $t9 + let $locals := $Malloc(32) + let $block := 4 + for {} true {} { + switch $block + case 2 { + // label L0 + // goto L3 + $block := 5 + } + case 3 { + // label L2 + // $t9 := 101 + $t9 := 101 + // abort($t9) + $Abort($t9) + } + case 4 { + // $t3 := 41 + $t3 := 41 + // $t4 := 42 + $t4 := 42 + // $t0 := M::encode_packed($t3, $t4) + mstore($locals, A2_M_encode_packed($t3, $t4)) + // $t5 := borrow_local($t0) + $t5 := $MakePtr(false, $locals) + // $t6 := Vector::length($t5) + $t6 := A1_Vector_length$u8$($t5) + // $t7 := 4 + $t7 := 4 + // $t8 := ==($t6, $t7) + $t8 := $Eq($t6, $t7) + // if ($t8) goto L0 else goto L2 + switch $t8 + case 0 { $block := 3 } + default { $block := 2 } + } + case 5 { + // label L3 + // return () + $Free($locals, 32) + leave + } + } + } + + function A1_Vector_length$u8$(v_ref) -> len { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + len := $LoadU64(v_ptr) + } + function A2_M_encode_packed(input_1,input_2) -> $result { + $result := mload(0) + let $t2 := add($result, 32) + if gt($t2, 0xffffffffffffffff) { $AbortBuiltin() } + let $t3 := abi_encode_tuple_packed_$uint16_uint16$_$u64_u64$_not_padded_inplace($t2,input_1,input_2) + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + let $t4 := sub($t3, $t2) + $MemoryStoreU64($result, $t4) + let $t5 := $ClosestGreaterPowerOfTwo($t4) + $MemoryStoreU64(add($result, 8), $t5) + mstore(0, $t3) + } + function abi_encode_tuple_packed_$uint16_uint16$_$u64_u64$_not_padded_inplace(pos ,value_0, value_1) -> end { + abi_encode_uint16(value_0, pos) + pos := add(pos, 2) + abi_encode_uint16(value_1, pos) + pos := add(pos, 2) + end := pos + } + function abi_encode_uint16(value, pos) { + mstore(pos, left_align_uint16(cleanup_uint16(value))) + } + function left_align_uint16(value) -> aligned { + aligned := shl(240, value) + } + function cleanup_uint16(value) -> cleaned { + cleaned := and(value, 0xffff) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $Free(offs, size) { + } + function $MakePtr(is_storage, offs) -> ptr { + ptr := or(is_storage, shl(1, offs)) + } + function $IsStoragePtr(ptr) -> b { + b := and(ptr, 0x1) + } + function $OffsetPtr(ptr) -> offs { + offs := shr(1, ptr) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ExtractBytes(word, start, size) -> bytes { + switch size + case 1 { + // use the faster byte primitive + bytes := byte(start, word) + } + default { + // As we have big endian, we need to right shift the value from + // where the highest byte starts in the word (32 - start), minus + // the size. + let shift_bits := shl(3, sub(sub(32, start), size)) + bytes := and(shr(shift_bits, word), $MaskForSize(size)) + } + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $OverflowBytes(byte_offset, size) -> overflow_bytes { + let available_bytes := sub(32, byte_offset) + switch gt(size, available_bytes) + case 0 { + overflow_bytes := 0 + } + default { + overflow_bytes := sub(size, available_bytes) + } + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $StorageLoadBytes(offs, size) -> val { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + val := $ExtractBytes(sload(key), byte_offs, size) + let overflow_bytes := $OverflowBytes(byte_offs, size) + if $LogicalNot(iszero(overflow_bytes)) { + key := $StorageKey(0, add(word_offs, 1)) + let extra_bytes := $ExtractBytes(sload(key), 0, overflow_bytes) + val := or(shl(shl(3, overflow_bytes), val), extra_bytes) + } + } + function $StorageKey(group, word) -> key { + mstore(32, word) + mstore(64, shl(224, group)) + key := keccak256(32, 36) + } + function $LoadU64(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU64(offs) + } + default { + val := $StorageLoadU64(offs) + } + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $StorageLoadU64(offs) -> val { + val := $StorageLoadBytes(offs, 8) + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $LoadU256(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU256(offs) + } + default { + val := $StorageLoadU256(offs) + } + } + function $MemoryLoadU256(offs) -> val { + val := $MemoryLoadBytes(offs, 32) + } + function $StorageLoadU256(offs) -> val { + val := $StorageLoadBytes(offs, 32) + } + function $Eq(x, y) -> r { + r := eq(x, y) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } + } +} +===> Test result of M::test_encode_packed_uint16: Succeed(Stopped) (used_gas=776): [] + +// test of M::test_marshalling_two_bytes1 +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_marshalling_two_bytes1" { + code { + mstore(0, memoryguard(160)) + A2_M_test_marshalling_two_bytes1() + return (0, 0) + function A2_M_test_marshalling_two_bytes1() { + let i, i_1, v1, v2, v_, $t6, $t7, $t8, $t9, $t10, $t11, $t12, $t13, $t14, $t15, $t16, $t17, $t18, $t19, $t20, $t21, $t22, $t23, $t24, $t25, $t26, $t27, $t28 + let $locals := $Malloc(32) + let $block := 3 + for {} true {} { + switch $block + case 2 { + // label L3 + // $t9 := 31 + $t9 := 31 + // $t10 := <=($t0, $t9) + $t10 := $LtEq(i, $t9) + // if ($t10) goto L0 else goto L2 + switch $t10 + case 0 { $block := 5 } + default { $block := 4 } + } + case 3 { + // $t2 := Vector::empty() + mstore($locals, A1_Vector_empty$u8$()) + // $t6 := borrow_local($t2) + $t6 := $MakePtr(false, $locals) + // $t7 := 42 + $t7 := 42 + // Vector::push_back($t6, $t7) + A1_Vector_push_back$u8$($t6, $t7) + // $t8 := 1 + $t8 := 1 + // $t0 := $t8 + i := $t8 + // goto L3 + $block := 2 + } + case 4 { + // label L0 + // $t11 := borrow_local($t2) + $t11 := $MakePtr(false, $locals) + // $t12 := 0 + $t12 := 0 + // Vector::push_back($t11, $t12) + A1_Vector_push_back$u8$($t11, $t12) + // $t13 := 1 + $t13 := 1 + // $t0 := +($t0, $t13) + i := $AddU64(i, $t13) + // goto L3 + $block := 2 + } + case 5 { + // label L2 + // $t14 := borrow_local($t2) + $t14 := $MakePtr(false, $locals) + // $t15 := 43 + $t15 := 43 + // Vector::push_back($t14, $t15) + A1_Vector_push_back$u8$($t14, $t15) + // $t16 := 1 + $t16 := 1 + // $t1 := $t16 + i_1 := $t16 + // goto L7 + $block := 6 + } + case 6 { + // label L7 + // $t17 := 31 + $t17 := 31 + // $t18 := <=($t1, $t17) + $t18 := $LtEq(i_1, $t17) + // if ($t18) goto L4 else goto L6 + switch $t18 + case 0 { $block := 8 } + default { $block := 7 } + } + case 7 { + // label L4 + // $t19 := borrow_local($t2) + $t19 := $MakePtr(false, $locals) + // $t20 := 0 + $t20 := 0 + // Vector::push_back($t19, $t20) + A1_Vector_push_back$u8$($t19, $t20) + // $t21 := 1 + $t21 := 1 + // $t1 := +($t1, $t21) + i_1 := $AddU64(i_1, $t21) + // goto L7 + $block := 6 + } + case 8 { + // label L6 + // $t22 := copy($t2) + $t22 := mload($locals) + // ($t23, $t24) := M::decode_two_bytes1($t22) + $t23, $t24 := A2_M_decode_two_bytes1($t22) + // $t25 := M::encode_two_bytes1($t23, $t24) + $t25 := A2_M_encode_two_bytes1($t23, $t24) + // $t26 := move($t2) + $t26 := mload($locals) + // $t27 := ==($t26, $t25) + $t27 := $EqVector($t26, $t25, 1) + // if ($t27) goto L8 else goto L10 + switch $t27 + case 0 { $block := 10 } + default { $block := 9 } + } + case 9 { + // label L8 + // goto L11 + $block := 11 + } + case 10 { + // label L10 + // $t28 := 101 + $t28 := 101 + // abort($t28) + $Abort($t28) + } + case 11 { + // label L11 + // return () + $Free($locals, 32) + leave + } + } + } + + function A2_M_encode_two_bytes1(input_1,input_2) -> $result { + $result := mload(0) + let $t2 := add($result, 32) + if gt($t2, 0xffffffffffffffff) { $AbortBuiltin() } + let $t3 := abi_encode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$($t2,input_1,input_2) + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + let $t4 := sub($t3, $t2) + $MemoryStoreU64($result, $t4) + let $t5 := $ClosestGreaterPowerOfTwo($t4) + $MemoryStoreU64(add($result, 8), $t5) + mstore(0, $t3) + } + function A2_M_decode_two_bytes1(input) -> $result0, $result1 { + let $t1 := add(input, 32) + let $t2 := $MemoryLoadU64(input) + let $t3 := add($t1, $t2) + if gt($t1, 0xffffffffffffffff) { $AbortBuiltin() } + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + $result0, $result1 := abi_decode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$_from_memory($t1, $t3) + } + function A1_Vector_push_back$u8$(v_ref, e) { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + let size := $LoadU64(v_ptr) + let e_ptr := $IndexPtr(v_ptr, add(32, mul(size, 1))) + $StoreU8(e_ptr, e) + size := add(size, 1) + $StoreU64(v_ptr, size) + let capacity := $LoadU64($IndexPtr(v_ptr, 8)) + if and(iszero($IsStoragePtr(v_ptr)), eq(size, capacity)) { + let new_v_offs := $ResizeVector(v_offs, capacity, 1) + $StoreU256(v_ref, new_v_offs) + } + } + function A1_Vector_empty$u8$() -> vector { + vector := $Malloc(34) + $MemoryStoreU64(add(vector, 8), 2) + } + function abi_decode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$_from_memory(headStart, dataEnd) -> value_0, value_1 { + if slt(sub(dataEnd, headStart), 64) { $Abort(96) } + { + let offset := 0 + value_0 := abi_decode_bytes1_vec$u8$_from_memory(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value_1 := abi_decode_bytes1_vec$u8$_from_memory(add(headStart, offset), dataEnd) + } + } + function abi_decode_bytes1_vec$u8$_from_memory(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { $Abort(94) } + let length := 1 + let size := 33 + array := abi_decode_available_length__bytes1_from_memory(offset, length, size, end) + } + function abi_decode_available_length__bytes1_from_memory(src, length, size, end) -> array { + array := $Malloc($CheckMemorySize(size)) + $MemoryStoreU64(array, length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) + let dst := add(array, 32) + if gt(add(src, sub(size, 32)), end) { $Abort(93) } + $CopyFromMemoryToMemory(src, dst, length) + } + function abi_encode_tuple_$bytes1_bytes1$_$vec$u8$_vec$u8$$(headStart ,value_0, value_1) -> tail { + tail := add(headStart, 64) + abi_encode_bytes1(value_0, add(headStart, 0)) + abi_encode_bytes1(value_1, add(headStart, 32)) + } + function abi_encode_bytes1(value, pos) { + let size := $MemoryLoadU64(value) + if iszero(eq(size, 1)) { $Abort(92) } + $CopyMemory(add(value, 0x20), pos, size) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $Free(offs, size) { + } + function $MakePtr(is_storage, offs) -> ptr { + ptr := or(is_storage, shl(1, offs)) + } + function $IsStoragePtr(ptr) -> b { + b := and(ptr, 0x1) + } + function $OffsetPtr(ptr) -> offs { + offs := shr(1, ptr) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ExtractBytes(word, start, size) -> bytes { + switch size + case 1 { + // use the faster byte primitive + bytes := byte(start, word) + } + default { + // As we have big endian, we need to right shift the value from + // where the highest byte starts in the word (32 - start), minus + // the size. + let shift_bits := shl(3, sub(sub(32, start), size)) + bytes := and(shr(shift_bits, word), $MaskForSize(size)) + } + } + function $InjectBytes(word, start, size, bytes) -> new_word { + let shift_bits := shl(3, sub(sub(32, start), size)) + // Blend out the bits which we inject + let neg_mask := not(shl(shift_bits, $MaskForSize(size))) + word := and(word, neg_mask) + // Overlay the bits we inject + new_word := or(word, shl(shift_bits, bytes)) + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $OverflowBytes(byte_offset, size) -> overflow_bytes { + let available_bytes := sub(32, byte_offset) + switch gt(size, available_bytes) + case 0 { + overflow_bytes := 0 + } + default { + overflow_bytes := sub(size, available_bytes) + } + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $StorageLoadBytes(offs, size) -> val { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + val := $ExtractBytes(sload(key), byte_offs, size) + let overflow_bytes := $OverflowBytes(byte_offs, size) + if $LogicalNot(iszero(overflow_bytes)) { + key := $StorageKey(0, add(word_offs, 1)) + let extra_bytes := $ExtractBytes(sload(key), 0, overflow_bytes) + val := or(shl(shl(3, overflow_bytes), val), extra_bytes) + } + } + function $StorageStoreBytes(offs, size, bytes) { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + let overflow_bytes := $OverflowBytes(byte_offs, size) + switch overflow_bytes + case 0 { + sstore(key, $InjectBytes(sload(key), byte_offs, size, bytes)) + } + default { + // Shift the higher bytes to the right + let used_bytes := sub(size, overflow_bytes) + let higher_bytes := shr(used_bytes, bytes) + let lower_bytes := and(bytes, $MaskForSize(overflow_bytes)) + sstore(key, $InjectBytes(sload(key), byte_offs, used_bytes, higher_bytes)) + key := $StorageKey(0, add(word_offs, 1)) + sstore(key, $InjectBytes(sload(key), 0, overflow_bytes, lower_bytes)) + } + } + function $StorageKey(group, word) -> key { + mstore(32, word) + mstore(64, shl(224, group)) + key := keccak256(32, 36) + } + function $IndexPtr(ptr, offs) -> new_ptr { + new_ptr := $MakePtr($IsStoragePtr(ptr), add($OffsetPtr(ptr), offs)) + } + function $StoreU8(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU8(offs, val) + } + default { + $StorageStoreU8(offs, val) + } + } + function $MemoryStoreU8(offs, val) { + // Shortcut via special instruction + mstore8(offs, val) + } + function $StorageStoreU8(offs, val) { + $StorageStoreBytes(offs, 1, val) + } + function $LoadU64(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU64(offs) + } + default { + val := $StorageLoadU64(offs) + } + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $StorageLoadU64(offs) -> val { + val := $StorageLoadBytes(offs, 8) + } + function $StoreU64(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU64(offs, val) + } + default { + $StorageStoreU64(offs, val) + } + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $StorageStoreU64(offs, val) { + $StorageStoreBytes(offs, 8, val) + } + function $LoadU256(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU256(offs) + } + default { + val := $StorageLoadU256(offs) + } + } + function $MemoryLoadU256(offs) -> val { + val := $MemoryLoadBytes(offs, 32) + } + function $StorageLoadU256(offs) -> val { + val := $StorageLoadBytes(offs, 32) + } + function $StoreU256(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU256(offs, val) + } + default { + $StorageStoreU256(offs, val) + } + } + function $MemoryStoreU256(offs, val) { + $MemoryStoreBytes(offs, 32, val) + } + function $StorageStoreU256(offs, val) { + $StorageStoreBytes(offs, 32, val) + } + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + mstore(add(dst, i), mload(add(src, i))) + } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) + } + } + function $CheckMemorySize(len) -> checked_len { + if gt(len, 0xffffffffffffffff) { $AbortBuiltin() } + checked_len := len + } + function $CopyFromMemoryToMemory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + function $ResizeVector(v_offs, capacity, type_size) -> new_v_offs { + let new_capacity := mul(capacity, 2) + let data_size := add(32, mul(capacity, type_size)) + let new_data_size := add(32, mul(new_capacity, type_size)) + new_v_offs := $Malloc(new_data_size) + $CopyMemory(v_offs, new_v_offs, data_size) + // update capacity at new location + $MemoryStoreU64(add(new_v_offs, 8), new_capacity) + $Free(v_offs, data_size) + } + function $AddU64(x, y) -> r { + if lt(sub(0xffffffffffffffff, x), y) { $AbortBuiltin() } + r := add(x, y) + } + function $LtEq(x, y) -> r { + r := or(lt(x, y), eq(x, y)) + } + function $EqVector(x, y, elem_size) -> r { + let len_x := $MemoryLoadU64(x) + let len_y := $MemoryLoadU64(y) + if $Neq(len_x, len_y) { + r := false + leave + } + let data_size_bytes := mul(elem_size, len_x) + let num_words, overflow_bytes := $ToWordOffs(data_size_bytes) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + if $Neq(mload(add(x, add(i, 32))), mload(add(y, add(i, 32)))) { + r := false + leave + } + } + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let x_overflow := mload(add(x, add(overflow_offs, 32))) + let y_overflow := mload(add(y, add(overflow_offs, 32))) + r := eq(or(mask, x_overflow), or(mask, y_overflow)) + } + function $Neq(x, y) -> r { + r := $LogicalNot(eq(x, y)) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } + } +} +===> Test result of M::test_marshalling_two_bytes1: Succeed(Stopped) (used_gas=66247): [] + +// test of M::test_marshalling_two_u8 +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "test_A2_M_test_marshalling_two_u8" { + code { + mstore(0, memoryguard(160)) + A2_M_test_marshalling_two_u8() + return (0, 0) + function A2_M_test_marshalling_two_u8() { + let i, i_1, v1, v2, v_, $t6, $t7, $t8, $t9, $t10, $t11, $t12, $t13, $t14, $t15, $t16, $t17, $t18, $t19, $t20, $t21, $t22, $t23, $t24, $t25, $t26, $t27, $t28, $t29, $t30, $t31, $t32, $t33, $t34 + let $locals := $Malloc(32) + let $block := 3 + for {} true {} { + switch $block + case 2 { + // label L3 + // $t7 := 31 + $t7 := 31 + // $t8 := <=($t0, $t7) + $t8 := $LtEq(i, $t7) + // if ($t8) goto L0 else goto L2 + switch $t8 + case 0 { $block := 5 } + default { $block := 4 } + } + case 3 { + // $t2 := Vector::empty() + mstore($locals, A1_Vector_empty$u8$()) + // $t6 := 1 + $t6 := 1 + // $t0 := $t6 + i := $t6 + // goto L3 + $block := 2 + } + case 4 { + // label L0 + // $t9 := borrow_local($t2) + $t9 := $MakePtr(false, $locals) + // $t10 := 0 + $t10 := 0 + // Vector::push_back($t9, $t10) + A1_Vector_push_back$u8$($t9, $t10) + // $t11 := 1 + $t11 := 1 + // $t0 := +($t0, $t11) + i := $AddU64(i, $t11) + // goto L3 + $block := 2 + } + case 5 { + // label L2 + // $t12 := borrow_local($t2) + $t12 := $MakePtr(false, $locals) + // $t13 := 42 + $t13 := 42 + // Vector::push_back($t12, $t13) + A1_Vector_push_back$u8$($t12, $t13) + // $t14 := 1 + $t14 := 1 + // $t1 := $t14 + i_1 := $t14 + // goto L7 + $block := 6 + } + case 6 { + // label L7 + // $t15 := 31 + $t15 := 31 + // $t16 := <=($t1, $t15) + $t16 := $LtEq(i_1, $t15) + // if ($t16) goto L4 else goto L6 + switch $t16 + case 0 { $block := 8 } + default { $block := 7 } + } + case 7 { + // label L4 + // $t17 := borrow_local($t2) + $t17 := $MakePtr(false, $locals) + // $t18 := 0 + $t18 := 0 + // Vector::push_back($t17, $t18) + A1_Vector_push_back$u8$($t17, $t18) + // $t19 := 1 + $t19 := 1 + // $t1 := +($t1, $t19) + i_1 := $AddU64(i_1, $t19) + // goto L7 + $block := 6 + } + case 8 { + // label L6 + // $t20 := borrow_local($t2) + $t20 := $MakePtr(false, $locals) + // $t21 := 43 + $t21 := 43 + // Vector::push_back($t20, $t21) + A1_Vector_push_back$u8$($t20, $t21) + // $t22 := copy($t2) + $t22 := mload($locals) + // ($t23, $t24) := M::decode_two_u8($t22) + $t23, $t24 := A2_M_decode_two_u8($t22) + // $t25 := 42 + $t25 := 42 + // $t26 := ==($t23, $t25) + $t26 := $Eq($t23, $t25) + // if ($t26) goto L8 else goto L10 + switch $t26 + case 0 { $block := 10 } + default { $block := 9 } + } + case 9 { + // label L8 + // goto L11 + $block := 11 + } + case 10 { + // label L10 + // $t27 := 101 + $t27 := 101 + // abort($t27) + $Abort($t27) + } + case 11 { + // label L11 + // $t28 := 43 + $t28 := 43 + // $t29 := ==($t24, $t28) + $t29 := $Eq($t24, $t28) + // if ($t29) goto L12 else goto L14 + switch $t29 + case 0 { $block := 13 } + default { $block := 12 } + } + case 12 { + // label L12 + // goto L15 + $block := 14 + } + case 13 { + // label L14 + // $t30 := 102 + $t30 := 102 + // abort($t30) + $Abort($t30) + } + case 14 { + // label L15 + // $t31 := M::encode_two_u8($t23, $t24) + $t31 := A2_M_encode_two_u8($t23, $t24) + // $t32 := move($t2) + $t32 := mload($locals) + // $t33 := ==($t32, $t31) + $t33 := $EqVector($t32, $t31, 1) + // if ($t33) goto L16 else goto L18 + switch $t33 + case 0 { $block := 16 } + default { $block := 15 } + } + case 15 { + // label L16 + // goto L19 + $block := 17 + } + case 16 { + // label L18 + // $t34 := 103 + $t34 := 103 + // abort($t34) + $Abort($t34) + } + case 17 { + // label L19 + // return () + $Free($locals, 32) + leave + } + } + } + + function A2_M_encode_two_u8(v1,v2) -> $result { + $result := mload(0) + let $t2 := add($result, 32) + if gt($t2, 0xffffffffffffffff) { $AbortBuiltin() } + let $t3 := abi_encode_tuple_$uint8_uint8$_$u8_u8$($t2,v1,v2) + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + let $t4 := sub($t3, $t2) + $MemoryStoreU64($result, $t4) + let $t5 := $ClosestGreaterPowerOfTwo($t4) + $MemoryStoreU64(add($result, 8), $t5) + mstore(0, $t3) + } + function A2_M_decode_two_u8(input) -> $result0, $result1 { + let $t1 := add(input, 32) + let $t2 := $MemoryLoadU64(input) + let $t3 := add($t1, $t2) + if gt($t1, 0xffffffffffffffff) { $AbortBuiltin() } + if gt($t3, 0xffffffffffffffff) { $AbortBuiltin() } + $result0, $result1 := abi_decode_tuple_$uint8_uint8$_$u8_u8$_from_memory($t1, $t3) + } + function A1_Vector_push_back$u8$(v_ref, e) { + let v_offs := $LoadU256(v_ref) + let v_ptr := $MakePtr($IsStoragePtr(v_ref), v_offs) + let size := $LoadU64(v_ptr) + let e_ptr := $IndexPtr(v_ptr, add(32, mul(size, 1))) + $StoreU8(e_ptr, e) + size := add(size, 1) + $StoreU64(v_ptr, size) + let capacity := $LoadU64($IndexPtr(v_ptr, 8)) + if and(iszero($IsStoragePtr(v_ptr)), eq(size, capacity)) { + let new_v_offs := $ResizeVector(v_offs, capacity, 1) + $StoreU256(v_ref, new_v_offs) + } + } + function A1_Vector_empty$u8$() -> vector { + vector := $Malloc(34) + $MemoryStoreU64(add(vector, 8), 2) + } + function abi_decode_tuple_$uint8_uint8$_$u8_u8$_from_memory(headStart, dataEnd) -> value_0, value_1 { + if slt(sub(dataEnd, headStart), 64) { $Abort(96) } + { + let offset := 0 + value_0 := abi_decode_uint8_from_memory(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value_1 := abi_decode_uint8_from_memory(add(headStart, offset), dataEnd) + } + } + function abi_decode_uint8_from_memory(offset, end) -> value { + value := mload(offset) + validator_uint8(value) + } + function validator_uint8(value) { + if iszero(eq(value, cleanup_uint8(value))) { $Abort(95) } + } + function cleanup_uint8(value) -> cleaned { + cleaned := and(value, 0xff) + } + function abi_encode_tuple_$uint8_uint8$_$u8_u8$(headStart ,value_0, value_1) -> tail { + tail := add(headStart, 64) + abi_encode_uint8(value_0, add(headStart, 0)) + abi_encode_uint8(value_1, add(headStart, 32)) + } + function abi_encode_uint8(value, pos) { + mstore(pos, cleanup_uint8(value)) + } + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $AbortBuiltin() { + $Abort(sub(0, 1)) + } + function $Malloc(size) -> offs { + offs := mload(0) + // pad to word size + mstore(0, add(offs, shl(5, shr(5, add(size, 31))))) + } + function $Free(offs, size) { + } + function $MakePtr(is_storage, offs) -> ptr { + ptr := or(is_storage, shl(1, offs)) + } + function $IsStoragePtr(ptr) -> b { + b := and(ptr, 0x1) + } + function $OffsetPtr(ptr) -> offs { + offs := shr(1, ptr) + } + function $MaskForSize(size) -> mask { + mask := sub(shl(shl(3, size), 1), 1) + } + function $ExtractBytes(word, start, size) -> bytes { + switch size + case 1 { + // use the faster byte primitive + bytes := byte(start, word) + } + default { + // As we have big endian, we need to right shift the value from + // where the highest byte starts in the word (32 - start), minus + // the size. + let shift_bits := shl(3, sub(sub(32, start), size)) + bytes := and(shr(shift_bits, word), $MaskForSize(size)) + } + } + function $InjectBytes(word, start, size, bytes) -> new_word { + let shift_bits := shl(3, sub(sub(32, start), size)) + // Blend out the bits which we inject + let neg_mask := not(shl(shift_bits, $MaskForSize(size))) + word := and(word, neg_mask) + // Overlay the bits we inject + new_word := or(word, shl(shift_bits, bytes)) + } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } + function $OverflowBytes(byte_offset, size) -> overflow_bytes { + let available_bytes := sub(32, byte_offset) + switch gt(size, available_bytes) + case 0 { + overflow_bytes := 0 + } + default { + overflow_bytes := sub(size, available_bytes) + } + } + function $MemoryLoadBytes(offs, size) -> val { + // Lower bit where the value in the higher bytes ends + let bit_end := shl(3, sub(32, size)) + val := shr(bit_end, mload(offs)) + } + function $MemoryStoreBytes(offs, size, val) { + let bit_end := shl(3, sub(32, size)) + let mask := shl(bit_end, $MaskForSize(size)) + mstore(offs, or(and(mload(offs), not(mask)), shl(bit_end, val))) + } + function $StorageLoadBytes(offs, size) -> val { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + val := $ExtractBytes(sload(key), byte_offs, size) + let overflow_bytes := $OverflowBytes(byte_offs, size) + if $LogicalNot(iszero(overflow_bytes)) { + key := $StorageKey(0, add(word_offs, 1)) + let extra_bytes := $ExtractBytes(sload(key), 0, overflow_bytes) + val := or(shl(shl(3, overflow_bytes), val), extra_bytes) + } + } + function $StorageStoreBytes(offs, size, bytes) { + let word_offs, byte_offs := $ToWordOffs(offs) + let key := $StorageKey(0, word_offs) + let overflow_bytes := $OverflowBytes(byte_offs, size) + switch overflow_bytes + case 0 { + sstore(key, $InjectBytes(sload(key), byte_offs, size, bytes)) + } + default { + // Shift the higher bytes to the right + let used_bytes := sub(size, overflow_bytes) + let higher_bytes := shr(used_bytes, bytes) + let lower_bytes := and(bytes, $MaskForSize(overflow_bytes)) + sstore(key, $InjectBytes(sload(key), byte_offs, used_bytes, higher_bytes)) + key := $StorageKey(0, add(word_offs, 1)) + sstore(key, $InjectBytes(sload(key), 0, overflow_bytes, lower_bytes)) + } + } + function $StorageKey(group, word) -> key { + mstore(32, word) + mstore(64, shl(224, group)) + key := keccak256(32, 36) + } + function $IndexPtr(ptr, offs) -> new_ptr { + new_ptr := $MakePtr($IsStoragePtr(ptr), add($OffsetPtr(ptr), offs)) + } + function $StoreU8(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU8(offs, val) + } + default { + $StorageStoreU8(offs, val) + } + } + function $MemoryStoreU8(offs, val) { + // Shortcut via special instruction + mstore8(offs, val) + } + function $StorageStoreU8(offs, val) { + $StorageStoreBytes(offs, 1, val) + } + function $LoadU64(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU64(offs) + } + default { + val := $StorageLoadU64(offs) + } + } + function $MemoryLoadU64(offs) -> val { + val := $MemoryLoadBytes(offs, 8) + } + function $StorageLoadU64(offs) -> val { + val := $StorageLoadBytes(offs, 8) + } + function $StoreU64(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU64(offs, val) + } + default { + $StorageStoreU64(offs, val) + } + } + function $MemoryStoreU64(offs, val) { + $MemoryStoreBytes(offs, 8, val) + } + function $StorageStoreU64(offs, val) { + $StorageStoreBytes(offs, 8, val) + } + function $LoadU256(ptr) -> val { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + val := $MemoryLoadU256(offs) + } + default { + val := $StorageLoadU256(offs) + } + } + function $MemoryLoadU256(offs) -> val { + val := $MemoryLoadBytes(offs, 32) + } + function $StorageLoadU256(offs) -> val { + val := $StorageLoadBytes(offs, 32) + } + function $StoreU256(ptr, val) { + let offs := $OffsetPtr(ptr) + switch $IsStoragePtr(ptr) + case 0 { + $MemoryStoreU256(offs, val) + } + default { + $StorageStoreU256(offs, val) + } + } + function $MemoryStoreU256(offs, val) { + $MemoryStoreBytes(offs, 32, val) + } + function $StorageStoreU256(offs, val) { + $StorageStoreBytes(offs, 32, val) + } + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + mstore(add(dst, i), mload(add(src, i))) + } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) + } + } + function $ResizeVector(v_offs, capacity, type_size) -> new_v_offs { + let new_capacity := mul(capacity, 2) + let data_size := add(32, mul(capacity, type_size)) + let new_data_size := add(32, mul(new_capacity, type_size)) + new_v_offs := $Malloc(new_data_size) + $CopyMemory(v_offs, new_v_offs, data_size) + // update capacity at new location + $MemoryStoreU64(add(new_v_offs, 8), new_capacity) + $Free(v_offs, data_size) + } + function $AddU64(x, y) -> r { + if lt(sub(0xffffffffffffffff, x), y) { $AbortBuiltin() } + r := add(x, y) + } + function $LtEq(x, y) -> r { + r := or(lt(x, y), eq(x, y)) + } + function $Eq(x, y) -> r { + r := eq(x, y) + } + function $EqVector(x, y, elem_size) -> r { + let len_x := $MemoryLoadU64(x) + let len_y := $MemoryLoadU64(y) + if $Neq(len_x, len_y) { + r := false + leave + } + let data_size_bytes := mul(elem_size, len_x) + let num_words, overflow_bytes := $ToWordOffs(data_size_bytes) + let i := 0 + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { + if $Neq(mload(add(x, add(i, 32))), mload(add(y, add(i, 32)))) { + r := false + leave + } + } + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let x_overflow := mload(add(x, add(overflow_offs, 32))) + let y_overflow := mload(add(y, add(overflow_offs, 32))) + r := eq(or(mask, x_overflow), or(mask, y_overflow)) + } + function $Neq(x, y) -> r { + r := $LogicalNot(eq(x, y)) + } + function $LogicalNot(x) -> r { + r := iszero(x) + } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } + } +} +===> Test result of M::test_marshalling_two_u8: Succeed(Stopped) (used_gas=66157): [] + + + +!! Move-To-Yul Diagnostics: + warning: unused alias + ┌─ tests/TestABINative.move:3:20 + │ +3 │ use Evm::Evm::concat; + │ ^^^^^^ Unused 'use' of alias 'concat'. Consider removing it diff --git a/language/evm/move-to-yul/tests/TestABINative.move b/language/evm/move-to-yul/tests/TestABINative.move new file mode 100644 index 0000000000..7331271ebd --- /dev/null +++ b/language/evm/move-to-yul/tests/TestABINative.move @@ -0,0 +1,126 @@ +#[contract] +module 0x2::M { + use Evm::Evm::concat; + use Std::Vector; + + #[decode] + public native fun decode_two_u8(input: vector): (u8, u8); + + #[encode] + public native fun encode_two_u8(v1: u8, v2: u8): vector; + + #[decode(sig=b"decode_two_bytes1(bytes) returns (bytes1, bytes1)")] + public native fun decode_two_bytes1(input: vector) :(vector, vector); + + #[encode(sig=b"encode_two_bytes1(bytes1, bytes1) returns (bytes)")] + public native fun encode_two_bytes1(input_1: vector, input_2: vector) : vector; + + #[encode_packed(sig=b"encode_packed(uint16, uint16) returns (bytes)")] + public native fun encode_packed(input_1: u64, input_2: u64) : vector; + + #[encode_packed(sig=b"encode_packed_string(string, string) returns (bytes)")] + public native fun encode_packed_string(input_1: vector, input_2: vector) : vector; + + #[evm_test] + fun test_encode_packed_uint16() { + let v1 = 41u64; + let v2 = 42u64; + let v = encode_packed(v1, v2); + assert!(Vector::length(&v) == 4, 101); + } + + #[evm_test] + fun test_encode_packed_string() { + assert!(encode_packed_string(b"", b"") == b"", 100); + assert!(encode_packed_string(b"1", b"2") == b"12", 101); + assert!(encode_packed_string(b"", b"abc") == b"abc", 102); + assert!(encode_packed_string(encode_packed_string(b"a", b"bc"), b"de") == b"abcde", 103); + assert!(encode_packed_string(b"test", b"") == b"test", 104); + } + + #[evm_test] + fun test_decode_two_bytes1() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 43u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + let (v1, v2) = decode_two_bytes1(v); + assert!(Vector::length(&v1) == 1, 101); + assert!(Vector::length(&v2) == 1, 102); + assert!(*Vector::borrow(&v1, 0) == 42, 103); + assert!(*Vector::borrow(&v2, 0) == 43, 104); + } + + #[evm_test] + fun test_marshalling_two_bytes1() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 43u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + let (v1, v2) = decode_two_bytes1(v); + let v_ = encode_two_bytes1(v1, v2); + assert!(v == v_, 101); + } + + #[evm_test] + fun test_decode_two_u8() { + let v = Vector::empty(); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 42u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 43u8); + let (v1, v2) = decode_two_u8(v); + assert!(v1 == 42, 101); + assert!(v2 == 43, 102); + } + + #[evm_test] + fun test_marshalling_two_u8() { + let v = Vector::empty(); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 42u8); + let i = 1; + while (i <= 31) { + Vector::push_back(&mut v, 0); + i = i + 1 + }; + Vector::push_back(&mut v, 43u8); + let (v1, v2) = decode_two_u8(v); + assert!(v1 == 42, 101); + assert!(v2 == 43, 102); + let v_ = encode_two_u8(v1, v2); + assert!(v == v_, 103) + + } + +} diff --git a/language/evm/move-to-yul/tests/TestABINativeError.exp b/language/evm/move-to-yul/tests/TestABINativeError.exp new file mode 100644 index 0000000000..2ff0e0702d --- /dev/null +++ b/language/evm/move-to-yul/tests/TestABINativeError.exp @@ -0,0 +1,71 @@ +/* ======================================= + * Generated by Move-To-Yul compiler v0.0 + * ======================================= */ + + +object "Empty" { + code { + codecopy(0, dataoffset("Empty_deployed"), datasize("Empty_deployed")) + return(0, datasize("Empty_deployed")) + } + object "Empty_deployed" { + code { + mstore(0, memoryguard(160)) + if iszero(lt(calldatasize(), 4)) + { + let selector := $Shr(calldataload(0), 224) + switch selector + default {} + } + $Abort(97) + function $Abort(code) { + mstore(0, code) + revert(24, 8) // TODO: store code as a string? + } + function $Shr(x, y) -> r { + r := shr(y, x) + } + } + } +} + + +!! Succeeded compiling Yul + + +!! Move-To-Yul Diagnostics: + error: decode function must only has one argument of type bytes + ┌─ tests/TestABINativeError.move:8:5 + │ +8 │ public native fun decode_wrong_input_type(input: vector): (u8, u8); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: decode function must only has one argument of type bytes + ┌─ tests/TestABINativeError.move:11:5 + │ +11 │ public native fun decode_wrong_input_type_sig(input: vector): (u8, u8); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encode function must only has one return value of type bytes + ┌─ tests/TestABINativeError.move:14:5 + │ +14 │ public native fun encode_wrong_input_type(input: vector): vector; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encode function must only has one return value of type bytes + ┌─ tests/TestABINativeError.move:20:5 + │ +20 │ public native fun encode_wrong_input_type_packed(input: vector): vector; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encode function must only has one return value of type bytes + ┌─ tests/TestABINativeError.move:23:5 + │ +23 │ public native fun encode_wrong_input_type_packed_sig(input: vector): vector; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encode function must only has one return value of type bytes + ┌─ tests/TestABINativeError.move:17:5 + │ +17 │ public native fun encode_wrong_input_type_sig(input: vector): vector; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/language/evm/move-to-yul/tests/TestABINativeError.move b/language/evm/move-to-yul/tests/TestABINativeError.move new file mode 100644 index 0000000000..584153ef52 --- /dev/null +++ b/language/evm/move-to-yul/tests/TestABINativeError.move @@ -0,0 +1,68 @@ +#[contract] +module 0x2::M { + use Std::Vector; + + // Test type checking on arguments and return values + + #[decode] + public native fun decode_wrong_input_type(input: vector): (u8, u8); + + #[decode(sig=b"decode(uint64[]) returns (uint8, uint8)")] + public native fun decode_wrong_input_type_sig(input: vector): (u8, u8); + + #[encode] + public native fun encode_wrong_input_type(input: vector): vector; + + #[encode(sig=b"encode(uint64[]) returns (uint64[])")] + public native fun encode_wrong_input_type_sig(input: vector): vector; + + #[encode_packed] + public native fun encode_wrong_input_type_packed(input: vector): vector; + + #[encode_packed(sig=b"encode_packed(uint64[]) returns (uint64[])")] + public native fun encode_wrong_input_type_packed_sig(input: vector): vector; + + + #[evm_test] + fun test_decode_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + decode_wrong_input_type(v); + } + + #[evm_test] + fun test_decode_sig_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + decode_wrong_input_type_sig(v); + } + + #[evm_test] + fun test_encode_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + encode_wrong_input_type(v); + } + + #[evm_test] + fun test_encode_sig_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + encode_wrong_input_type_sig(v); + } + + #[evm_test] + fun test_encode_packed_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + encode_wrong_input_type_packed(v); + } + + #[evm_test] + fun test_encode_packed_sig_error() { + let v = Vector::empty(); + Vector::push_back(&mut v, 42); + encode_wrong_input_type_packed_sig(v); + } + +} diff --git a/language/evm/move-to-yul/tests/TestStringLiteral.exp b/language/evm/move-to-yul/tests/TestStringLiteral.exp index 0e5a1568f8..7f77c9af1d 100644 --- a/language/evm/move-to-yul/tests/TestStringLiteral.exp +++ b/language/evm/move-to-yul/tests/TestStringLiteral.exp @@ -743,7 +743,7 @@ object "test_A2_M_test_same_literals" { let size := $MemoryLoadU64(message) mstore(pos, size) pos := add(pos, 32) - $CopyMemoryU8(add(message, 32), pos, size) + $CopyMemory(add(message, 32), pos, size) size := $RoundUp(size) let end := add(pos, size) revert(head, sub(end, head)) @@ -784,6 +784,10 @@ object "test_A2_M_test_same_literals" { function $MaskForSize(size) -> mask { mask := sub(shl(shl(3, size), 1), 1) } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } function $MemoryLoadBytes(offs, size) -> val { // Lower bit where the value in the higher bytes ends let bit_end := shl(3, sub(32, size)) @@ -804,16 +808,18 @@ object "test_A2_M_test_same_literals" { function $MemoryStoreU64(offs, val) { $MemoryStoreBytes(offs, 8, val) } - function $CopyMemoryU8(src, dst, size) { + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) let i := 0 - for { } lt(i, size) { i := add(i, 32) } { + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } function $LogicalNot(x) -> r { diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayDecoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayDecoding.exp index 3f4230c6eb..25a5a76406 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayDecoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayDecoding.exp @@ -1323,7 +1323,7 @@ object "A2_M" { function abi_decode_available_length__uint128_____vec$vec$u128$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1346,7 +1346,7 @@ object "A2_M" { function abi_decode_available_length__uint128___vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1392,7 +1392,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2____vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1413,7 +1413,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1459,7 +1459,7 @@ object "A2_M" { function abi_decode_available_length__uint64_2____vec$vec$u64$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1480,7 +1480,7 @@ object "A2_M" { function abi_decode_available_length__uint64_2__vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1526,7 +1526,7 @@ object "A2_M" { function abi_decode_available_length__uint256_2____vec$vec$A2_U256_U256$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1547,7 +1547,7 @@ object "A2_M" { function abi_decode_available_length__uint256_2__vec$A2_U256_U256$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1583,7 +1583,7 @@ object "A2_M" { function abi_decode_available_length__uint8___2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1606,7 +1606,7 @@ object "A2_M" { function abi_decode_available_length__uint8___vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1635,7 +1635,7 @@ object "A2_M" { function abi_decode_available_length__uint64___2__vec$vec$u64$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1658,7 +1658,7 @@ object "A2_M" { function abi_decode_available_length__uint64___vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1687,7 +1687,7 @@ object "A2_M" { function abi_decode_available_length__uint256___2__vec$vec$A2_U256_U256$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1710,7 +1710,7 @@ object "A2_M" { function abi_decode_available_length__uint256___vec$A2_U256_U256$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1738,7 +1738,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1766,7 +1766,7 @@ object "A2_M" { function abi_decode_available_length__uint64_2__2__vec$vec$u64$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1798,7 +1798,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1831,7 +1831,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2____vec$vec$A2_U256_U256$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1852,7 +1852,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$A2_U256_U256$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1891,7 +1891,7 @@ object "A2_M" { function abi_decode_available_length__uint72___2__vec$vec$u128$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1914,7 +1914,7 @@ object "A2_M" { function abi_decode_available_length__uint72___vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1960,7 +1960,7 @@ object "A2_M" { function abi_decode_available_length__uint16_2____vec$vec$u64$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -1981,7 +1981,7 @@ object "A2_M" { function abi_decode_available_length__uint16_2__vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -2210,6 +2210,15 @@ object "A2_M" { function $LogicalNot(x) -> r { r := iszero(x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayEncoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayEncoding.exp index 1ce5b98e03..bd6bd702cf 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayEncoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherArrayEncoding.exp @@ -228,7 +228,7 @@ object "A2_M" { function abi_decode_available_length__uint128_____vec$vec$u128$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -251,7 +251,7 @@ object "A2_M" { function abi_decode_available_length__uint128___vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -330,7 +330,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2____vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -351,7 +351,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -425,7 +425,7 @@ object "A2_M" { function abi_decode_available_length__uint8___2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -448,7 +448,7 @@ object "A2_M" { function abi_decode_available_length__uint8___vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -525,7 +525,7 @@ object "A2_M" { function abi_decode_available_length__uint256___2__vec$vec$A2_U256_U256$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -548,7 +548,7 @@ object "A2_M" { function abi_decode_available_length__uint256___vec$A2_U256_U256$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -598,7 +598,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -675,7 +675,7 @@ object "A2_M" { function abi_decode_available_length__uint72___2__vec$vec$u128$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -698,7 +698,7 @@ object "A2_M" { function abi_decode_available_length__uint72___vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -769,7 +769,7 @@ object "A2_M" { function abi_decode_available_length__uint64_2__vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -845,6 +845,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesDecoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesDecoding.exp index 81316a4d37..e3eb27a13d 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesDecoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesDecoding.exp @@ -368,7 +368,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -393,7 +393,7 @@ object "A2_M" { function abi_decode_available_length__bytes4(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -418,7 +418,7 @@ object "A2_M" { function abi_decode_available_length__bytes32(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -440,7 +440,7 @@ object "A2_M" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -462,7 +462,7 @@ object "A2_M" { function abi_decode_available_length__bytes5_2____vec$vec$vec$u8$$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -483,7 +483,7 @@ object "A2_M" { function abi_decode_available_length__bytes5_2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -504,7 +504,7 @@ object "A2_M" { function abi_decode_available_length__bytes5(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -678,6 +678,15 @@ object "A2_M" { function $LogicalNot(x) -> r { r := iszero(x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesEncoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesEncoding.exp index 8fdeca299a..c8e640b8a6 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesEncoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherBytesEncoding.exp @@ -123,7 +123,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -144,7 +144,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -156,7 +156,7 @@ object "A2_M" { function abi_encode_bytes5(value, pos) { let size := $MemoryLoadU64(value) if iszero(eq(size, 5)) { $Abort(92) } - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) } function abi_decode_tuple_$bytes4_uint8$_$vec$u8$_u8$(headStart, dataEnd) -> value_0, value_1 { if slt(sub(dataEnd, headStart), 64) { $Abort(96) } @@ -188,7 +188,7 @@ object "A2_M" { function abi_decode_available_length__bytes4(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -247,7 +247,7 @@ object "A2_M" { function abi_decode_available_length__bytes5_2____vec$vec$vec$u8$$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 64)) if gt(srcEnd, end) { $Abort(94) } @@ -268,7 +268,7 @@ object "A2_M" { function abi_decode_available_length__bytes5_2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -289,7 +289,7 @@ object "A2_M" { function abi_decode_available_length__bytes5(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -303,7 +303,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -324,7 +324,7 @@ object "A2_M" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -533,18 +533,6 @@ object "A2_M" { mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } - function $CopyMemoryU8(src, dst, size) { - let i := 0 - for { } lt(i, size) { i := add(i, 32) } { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } - } - } function $CheckMemorySize(len) -> checked_len { if gt(len, 0xffffffffffffffff) { $AbortBuiltin() } checked_len := len @@ -569,6 +557,15 @@ object "A2_M" { function $LogicalNot(x) -> r { r := iszero(x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } function $RoundUp(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherEncodingStorage.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherEncodingStorage.exp index 6366d8fe2c..2ed32bba16 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherEncodingStorage.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherEncodingStorage.exp @@ -348,7 +348,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -578,18 +578,6 @@ object "A2_M" { mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } - function $CopyMemoryU8(src, dst, size) { - let i := 0 - for { } lt(i, size) { i := add(i, 32) } { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } - } - } function $ResizeVector(v_offs, capacity, type_size) -> new_v_offs { let new_capacity := mul(capacity, 2) let data_size := add(32, mul(capacity, type_size)) diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiParaDecoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiParaDecoding.exp index ceb4b60dd2..9404f12470 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiParaDecoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiParaDecoding.exp @@ -302,7 +302,7 @@ object "A2_M" { function abi_decode_available_length__uint8_3__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -353,7 +353,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -405,7 +405,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -603,6 +603,15 @@ object "A2_M" { function $LogicalNot(x) -> r { r := iszero(x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiRetEncoding.exp b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiRetEncoding.exp index c6bf411eeb..1d390c78b1 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiRetEncoding.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/DispatcherMultiRetEncoding.exp @@ -237,7 +237,7 @@ object "A2_M" { function abi_decode_available_length__uint8_3__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -292,7 +292,7 @@ object "A2_M" { function abi_decode_available_length__uint8_2__vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -314,7 +314,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -335,7 +335,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -370,7 +370,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -404,7 +404,7 @@ object "A2_M" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -547,16 +547,18 @@ object "A2_M" { function $StorageLoadU256(offs) -> val { val := $StorageLoadBytes(offs, 32) } - function $CopyMemoryU8(src, dst, size) { + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) let i := 0 - for { } lt(i, size) { i := add(i, 32) } { + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } function $CheckMemorySize(len) -> checked_len { @@ -580,6 +582,15 @@ object "A2_M" { function $LogicalNot(x) -> r { r := iszero(x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } function $RoundUp(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.exp b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.exp index ccb0d65b9e..d4ec570368 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.exp @@ -699,7 +699,7 @@ object "A2_M" { if iszero(extcodesize(contract)) { $Abort(91) } // storage for arguments and returned data let $t1 := mload(0) - mstore($t1, $Shl(0x48050dbe, 224)) + mstore($t1, $Shl(0x557c379b, 224)) let $t2 := abi_encode_tuple__(add($t1, 4)) let $t3 := call(gas(), contract, 0, $t1, sub($t2, $t1), $t1, 0) // set freeMemoryPointer @@ -713,7 +713,7 @@ object "A2_M" { function A2_M_multi_ret(contract, v, vec) -> $result0, $result1 { // storage for arguments and returned data let $t3 := mload(0) - mstore($t3, $Shl(0xe520b193, 224)) + mstore($t3, $Shl(0x7f547216, 224)) let $t4 := abi_encode_tuple_$uint256_uint256__$_$A2_U256_U256_vec$A2_U256_U256$$(add($t3, 4), v, vec) let $t5 := call(gas(), contract, 0, $t3, sub($t4, $t3), $t3, 0) // set freeMemoryPointer @@ -1326,7 +1326,7 @@ object "A2_M" { function abi_decode_available_length__uint256___vec$A2_U256_U256$_from_memory(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -1376,7 +1376,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -1651,18 +1651,6 @@ object "A2_M" { mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } - function $CopyMemoryU8(src, dst, size) { - let i := 0 - for { } lt(i, size) { i := add(i, 32) } { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } - } - } function $CheckMemorySize(len) -> checked_len { if gt(len, 0xffffffffffffffff) { $AbortBuiltin() } checked_len := len @@ -1705,6 +1693,15 @@ object "A2_M" { if gt(lo, 0xffffffffffffffffffffffffffffffff) { $AbortBuiltin() } r := add(shl(128, hi), lo) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } function $RoundUp(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.move b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.move index a89675386a..948357301f 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.move +++ b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCall.move @@ -5,7 +5,7 @@ module 0x2::M { use Evm::Evm::Unit; use Std::Vector; - #[external(sig=b"noPara()")] + #[external] public native fun no_para(contract: address); #[external(sig=b"safeTransferFrom(address,address,uint256,bytes)")] @@ -14,7 +14,7 @@ module 0x2::M { #[external(sig=b"isApprovedForAll(address,address)returns(bool)"), view] public native fun is_approved_for_all(contract: address, account: address, operator: address): bool; - #[external(sig=b"multiRet(uint,uint[])returns(uint[], uint)"), view] + #[external, view] public native fun multi_ret(contract: address, v: U256, vec: vector): (vector, U256); #[external(sig=b"testExternalReturn(uint) returns (uint)")] diff --git a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCallFailure.exp b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCallFailure.exp index 644bef651d..4658e682cc 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/ExternalCallFailure.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/ExternalCallFailure.exp @@ -110,6 +110,12 @@ error: solidity signature is not compatible with the move signature 8 │ public native fun failure_2(add: u128, i:u8); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: external function must have at least one argument + ┌─ tests/test-dispatcher/ExternalCallFailure.move:5:5 + │ +5 │ public native fun failure_1(); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: error happens when parsing the signature ┌─ tests/test-dispatcher/ExternalCallFailure.move:5:5 │ diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/ParsingSoliditySig.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/ParsingSoliditySig.exp index fe6be84030..e81fedd461 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/ParsingSoliditySig.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/ParsingSoliditySig.exp @@ -179,7 +179,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -226,7 +226,7 @@ object "A2_M" { function abi_decode_available_length__bytes32(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -240,7 +240,7 @@ object "A2_M" { function abi_decode_available_length__bytes1(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -254,7 +254,7 @@ object "A2_M" { function abi_decode_available_length__bytes_2__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -277,7 +277,7 @@ object "A2_M" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -291,7 +291,7 @@ object "A2_M" { function abi_decode_available_length__address___vec$address$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -322,7 +322,7 @@ object "A2_M" { function abi_decode_available_length__int120_3____5__vec$vec$vec$u128$$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -345,7 +345,7 @@ object "A2_M" { function abi_decode_available_length__int120_3____vec$vec$u128$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 96)) if gt(srcEnd, end) { $Abort(94) } @@ -366,7 +366,7 @@ object "A2_M" { function abi_decode_available_length__int120_3__vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -494,6 +494,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC1155.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC1155.exp index 0c1ba0efe4..dbf840482d 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC1155.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC1155.exp @@ -176,7 +176,7 @@ object "A2_ERC1155" { function abi_decode_available_length__bytes4(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -245,7 +245,7 @@ object "A2_ERC1155" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -295,7 +295,7 @@ object "A2_ERC1155" { function abi_decode_available_length__uint256___vec$A2_U256_U256$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -365,7 +365,7 @@ object "A2_ERC1155" { function abi_decode_available_length__address___vec$address$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -440,6 +440,15 @@ object "A2_ERC1155" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC721.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC721.exp index ec61375be7..5c745b4922 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC721.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/evm-examples/ERC721.exp @@ -282,7 +282,7 @@ object "A2_ERC721" { function abi_decode_available_length__bytes4(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -334,7 +334,7 @@ object "A2_ERC721" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalDataLocation.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalDataLocation.exp index 26195cb4c9..b7eb00f505 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalDataLocation.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalDataLocation.exp @@ -120,7 +120,7 @@ object "A2_M" { function abi_decode_available_length__uint8___vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -176,6 +176,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalTypeList.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalTypeList.exp index 5645a1d5b3..aa2724a573 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalTypeList.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IllegalTypeList.exp @@ -424,7 +424,7 @@ object "A2_M" { function abi_decode_available_length__uint8___vec$u8$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -508,6 +508,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IncompatibileType.exp b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IncompatibileType.exp index 7e59244158..9e14e19815 100644 --- a/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IncompatibileType.exp +++ b/language/evm/move-to-yul/tests/test-dispatcher/signature-parsing-test/baseline/parsing_failure/IncompatibileType.exp @@ -351,7 +351,7 @@ object "A2_M" { function abi_decode_available_length__uint64___vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -380,7 +380,7 @@ object "A2_M" { function abi_decode_available_length__uint128___vec$u128$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -476,6 +476,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } } } } diff --git a/language/evm/move-to-yul/tests/test-events/CallEmit.exp b/language/evm/move-to-yul/tests/test-events/CallEmit.exp index 1557825f34..ab8e443702 100644 --- a/language/evm/move-to-yul/tests/test-events/CallEmit.exp +++ b/language/evm/move-to-yul/tests/test-events/CallEmit.exp @@ -276,6 +276,7 @@ object "A2_M" { let $t5 := mload(0) let $t6 := abi_encode_tuple_$address_address_uint256$_$address_address_A2_U256_U256$($t5, $t2,$t3,$t4) log1($t5, sub($t6, $t5), $t1) + mstore(0, $t6) } } function A2_Evm_emit$A2_M_Event_8$(e) { @@ -287,6 +288,7 @@ object "A2_M" { let $t5 := mload(0) let $t6 := abi_encode_tuple_$bytes__$_$vec$vec$u8$$$($t5, $t2) log2($t5, sub($t6, $t5), $t1,$t4) + mstore(0, $t6) } } function A2_Evm_emit$A2_M_Event_7$(e) { @@ -297,6 +299,7 @@ object "A2_M" { let $t4 := mload(0) let $t5 := abi_encode_tuple__($t4) log2($t4, sub($t5, $t4), $t1,$t3) + mstore(0, $t5) } } function A2_Evm_emit$A2_M_Event_6$(e) { @@ -310,6 +313,7 @@ object "A2_M" { let $t7 := mload(0) let $t8 := abi_encode_tuple_$string$_$vec$u8$$($t7, $t4) log3($t7, sub($t8, $t7), $t1,$t3,$t6) + mstore(0, $t8) } } function A2_Evm_emit$A2_M_Event_5$(e) { @@ -322,6 +326,7 @@ object "A2_M" { let $t6 := mload(0) let $t7 := abi_encode_tuple__($t6) log3($t6, sub($t7, $t6), $t1,$t3,$t5) + mstore(0, $t7) } } function A2_Evm_emit$A2_M_Event_4$(e) { @@ -333,6 +338,7 @@ object "A2_M" { let $t5 := mload(0) let $t6 := abi_encode_tuple__($t5) log4($t5, sub($t6, $t5), $t1,$t2,$t3,$t4) + mstore(0, $t6) } } function A2_Evm_emit$A2_M_Event_3$(e) { @@ -344,6 +350,7 @@ object "A2_M" { let $t5 := mload(0) let $t6 := abi_encode_tuple_$address$_$address$($t5, $t3) log3($t5, sub($t6, $t5), $t1,$t2,$t4) + mstore(0, $t6) } } function A2_Evm_emit$A2_M_Event_2$(e) { @@ -356,6 +363,7 @@ object "A2_M" { let $t6 := mload(0) let $t7 := abi_encode_tuple_$uint8_address_uint16_bytes$_$u8_address_u64_vec$u8$$($t6, $t2,$t3,$t4,$t5) log1($t6, sub($t7, $t6), $t1) + mstore(0, $t7) } } function A2_Evm_emit$A2_M_Event_1$(e) { @@ -367,6 +375,7 @@ object "A2_M" { let $t5 := mload(0) let $t6 := abi_encode_tuple_$uint8_address_uint256$_$u8_address_A2_U256_U256$($t5, $t2,$t3,$t4) log1($t5, sub($t6, $t5), $t1) + mstore(0, $t6) } } function abi_encode_tuple_$uint8_address_uint256$_$u8_address_A2_U256_U256$(headStart ,value_0, value_1, value_2) -> tail { @@ -405,7 +414,7 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -425,6 +434,7 @@ object "A2_M" { function packed_hashed__$string$_$vec$u8$$(value_0) -> hash { let pos := mload(0) let end := abi_encode_tuple_packed_$string$_$vec$u8$$_not_padded_inplace(pos ,value_0) + mstore(0, end) hash := keccak256(pos, sub(end, pos)) } function abi_encode_tuple_packed_$string$_$vec$u8$$_not_padded_inplace(pos ,value_0) -> end { @@ -433,12 +443,13 @@ object "A2_M" { } function abi_encode_string_not_padded_inplace(value, pos) -> end{ let size := $MemoryLoadU64(value) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) end := add(pos, size) } function packed_hashed__$bytes$_$vec$u8$$(value_0) -> hash { let pos := mload(0) let end := abi_encode_tuple_packed_$bytes$_$vec$u8$$_not_padded_inplace(pos ,value_0) + mstore(0, end) hash := keccak256(pos, sub(end, pos)) } function abi_encode_tuple_packed_$bytes$_$vec$u8$$_not_padded_inplace(pos ,value_0) -> end { @@ -447,7 +458,7 @@ object "A2_M" { } function abi_encode_bytes_not_padded_inplace(value, pos) -> end{ let size := $MemoryLoadU64(value) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) end := add(pos, size) } function abi_encode_tuple_$string$_$vec$u8$$(headStart ,value_0) -> tail { @@ -459,13 +470,14 @@ object "A2_M" { let size := $MemoryLoadU64(value) mstore(pos, size) pos := add(pos, 0x20) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } function packed_hashed__$uint16_3_$_$vec$u64$$(value_0) -> hash { let pos := mload(0) let end := abi_encode_tuple_packed_$uint16_3_$_$vec$u64$$_not_padded_inplace(pos ,value_0) + mstore(0, end) hash := keccak256(pos, sub(end, pos)) } function abi_encode_tuple_packed_$uint16_3_$_$vec$u64$$_not_padded_inplace(pos ,value_0) -> end { @@ -491,6 +503,7 @@ object "A2_M" { function packed_hashed__$bytes__$_$vec$vec$u8$$$(value_0) -> hash { let pos := mload(0) let end := abi_encode_tuple_packed_$bytes__$_$vec$vec$u8$$$_not_padded_inplace(pos ,value_0) + mstore(0, end) hash := keccak256(pos, sub(end, pos)) } function abi_encode_tuple_packed_$bytes__$_$vec$vec$u8$$$_not_padded_inplace(pos ,value_0) -> end { @@ -513,7 +526,7 @@ object "A2_M" { } function abi_encode_bytes_inplace(value, pos) -> end{ let size := $MemoryLoadU64(value) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -546,6 +559,7 @@ object "A2_M" { function packed_hashed__$string_3_$_$vec$vec$u8$$$(value_0) -> hash { let pos := mload(0) let end := abi_encode_tuple_packed_$string_3_$_$vec$vec$u8$$$_not_padded_inplace(pos ,value_0) + mstore(0, end) hash := keccak256(pos, sub(end, pos)) } function abi_encode_tuple_packed_$string_3_$_$vec$vec$u8$$$_not_padded_inplace(pos ,value_0) -> end { @@ -569,7 +583,7 @@ object "A2_M" { } function abi_encode_string_inplace(value, pos) -> end{ let size := $MemoryLoadU64(value) - $CopyMemoryU8(add(value, 0x20), pos, size) + $CopyMemory(add(value, 0x20), pos, size) size := $RoundUp(size) end := add(pos, size) } @@ -630,7 +644,7 @@ object "A2_M" { function abi_decode_available_length__string_3__vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -653,7 +667,7 @@ object "A2_M" { function abi_decode_available_length__string(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -667,7 +681,7 @@ object "A2_M" { function abi_decode_available_length__bytes___vec$vec$u8$$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -690,7 +704,7 @@ object "A2_M" { function abi_decode_available_length__bytes(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -729,7 +743,7 @@ object "A2_M" { function abi_decode_available_length__uint16_3__vec$u64$(offset, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) let srcEnd := add(offset, mul(length, 32)) if gt(srcEnd, end) { $Abort(94) } @@ -785,7 +799,7 @@ object "A2_M" { function abi_decode_available_length__bytes32(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -799,7 +813,7 @@ object "A2_M" { function abi_decode_available_length__bytes2(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -813,7 +827,7 @@ object "A2_M" { function abi_decode_available_length__bytes1(src, length, size, end) -> array { array := $Malloc($CheckMemorySize(size)) $MemoryStoreU64(array, length) - $MemoryStoreU64(add(array, 8), length) + $MemoryStoreU64(add(array, 8), $ClosestGreaterPowerOfTwo(length)) let dst := add(array, 32) if gt(add(src, sub(size, 32)), end) { $Abort(93) } $CopyFromCallDataToMemory(src, dst, length) @@ -875,6 +889,10 @@ object "A2_M" { function $MaskForSize(size) -> mask { mask := sub(shl(shl(3, size), 1), 1) } + function $ToWordOffs(offs) -> word_offs, byte_offset { + word_offs := shr(5, offs) + byte_offset := and(offs, 0x1F) + } function $MemoryLoadBytes(offs, size) -> val { // Lower bit where the value in the higher bytes ends let bit_end := shl(3, sub(32, size)) @@ -904,16 +922,18 @@ object "A2_M" { function $MemoryStoreU256(offs, val) { $MemoryStoreBytes(offs, 32, val) } - function $CopyMemoryU8(src, dst, size) { + function $CopyMemory(src, dst, size) { + let num_words, overflow_bytes := $ToWordOffs(size) let i := 0 - for { } lt(i, size) { i := add(i, 32) } { + for { } lt(i, mul(num_words, 32)) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } - if gt(i, size) - { - for {let j := i} lt(j, size) {j := add(j, 1)} { - mstore8(add(dst, j), 0) - } + if overflow_bytes { + let mask := $MaskForSize(sub(32, overflow_bytes)) + let overflow_offs := mul(num_words, 32) + let dst_word := and(mload(add(dst, overflow_offs)), mask) + let src_word := and(mload(add(src, overflow_offs)), not(mask)) + mstore(add(dst, overflow_offs), or(dst_word, src_word)) } } function $CheckMemorySize(len) -> checked_len { @@ -927,6 +947,15 @@ object "A2_M" { function $Shr(x, y) -> r { r := shr(y, x) } + function $ClosestGreaterPowerOfTwo(x) -> r { + r := or(r, shr(1, x)) + r := or(r, shr(2, r)) + r := or(r, shr(4, r)) + r := or(r, shr(8, r)) + r := or(r, shr(16, r)) + r := or(r, shr(32, r)) + r := add(x, 1) + } function $RoundUp(value) -> result { result := and(add(value, 31), not(31)) }