Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Memory and storage copying #155

Merged
merged 1 commit into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions compiler/src/yul/abi/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ fn decode_array(
let total_size = literal_expression! { (total_size) };

match location {
AbiDecodeLocation::Calldata => expression! { ccopy(head_ptr, [total_size]) },
AbiDecodeLocation::Calldata => {
expression! { ccopym(head_ptr, [total_size]) }
}
AbiDecodeLocation::Memory => expression! { head_ptr },
}
}
Expand All @@ -206,7 +208,7 @@ fn decode_array(
let data_size = literal_expression! { (data_size) };
let total_size = expression! { add(32, (mul([array_size], [data_size]))) };

expression! { ccopy([data_ptr], [total_size]) }
expression! { ccopym([data_ptr], [total_size]) }
}
AbiDecodeLocation::Memory => {
expression! { add(start, (mload(head_ptr))) }
Expand Down Expand Up @@ -251,7 +253,7 @@ mod tests {
fn test_decode_string_calldata() {
assert_eq!(
decode(FeString { max_size: 100 }, AbiDecodeLocation::Calldata).to_string(),
"function abi_decode_string100_calldata(start, head_ptr) -> val { val := ccopy(add(start, calldataload(head_ptr)), add(32, mul(calldataload(add(start, calldataload(head_ptr))), 1))) }"
"function abi_decode_string100_calldata(start, head_ptr) -> val { val := ccopym(add(start, calldataload(head_ptr)), add(32, mul(calldataload(add(start, calldataload(head_ptr))), 1))) }"
)
}

Expand Down
22 changes: 12 additions & 10 deletions compiler/src/yul/mappers/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,34 @@ pub fn assign(
target_attributes.final_location(),
) {
(Location::Memory, Location::Storage { .. }) => {
operations::mem_to_sto(typ, target, value)
operations::mcopys(typ, target, value)
}
(Location::Memory, Location::Value) => {
let target = expr_as_ident(target)?;
let value = operations::mem_to_val(typ, value);
let value = operations::mload(typ, value);
statement! { [target] := [value] }
}
(Location::Memory, Location::Memory) => unimplemented!("memory copying"),
(Location::Memory, Location::Memory) => {
let target = expr_as_ident(target)?;
let ptr = operations::mcopym(typ, value);
statement! { [target] := [ptr] }
}
(Location::Storage { .. }, Location::Storage { .. }) => {
unimplemented!("storage copying")
operations::scopys(typ, target, value)
}
(Location::Storage { .. }, Location::Value) => {
let target = expr_as_ident(target)?;
let value = operations::sto_to_val(typ, value);
let value = operations::sload(typ, value);
statement! { [target] := [value] }
}
(Location::Storage { .. }, Location::Memory) => {
let target = expr_as_ident(target)?;
let mptr = operations::sto_to_mem(typ, value);
statement! { [target] := [mptr] }
unreachable!("raw sto to mem assign")
}
(Location::Value, Location::Memory) => {
operations::val_to_mem(typ, target, value)
operations::mstore(typ, target, value)
}
(Location::Value, Location::Storage { .. }) => {
operations::val_to_sto(typ, target, value)
operations::sstore(typ, target, value)
}
(Location::Value, Location::Value) => {
let target = expr_as_ident(target)?;
Expand Down
61 changes: 32 additions & 29 deletions compiler/src/yul/mappers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::yul::operations;
use crate::yul::utils;
use fe_parser::ast as fe;
use fe_parser::span::Spanned;
use fe_semantics::builtins;
use fe_semantics::namespace::types::{
FixedSize,
Type,
Expand Down Expand Up @@ -63,9 +64,10 @@ fn move_expression(
FixedSize::try_from(typ).map_err(|_| CompileError::static_str("invalid attributes"))?;

match (from.clone(), to.clone()) {
(Location::Storage { .. }, Location::Value) => Ok(operations::sto_to_val(typ, val)),
(Location::Memory { .. }, Location::Value) => Ok(operations::mem_to_val(typ, val)),
(Location::Storage { .. }, Location::Memory) => Ok(operations::sto_to_mem(typ, val)),
(Location::Storage { .. }, Location::Value) => Ok(operations::sload(typ, val)),
(Location::Memory, Location::Value) => Ok(operations::mload(typ, val)),
(Location::Memory, Location::Memory) => Ok(operations::mcopym(typ, val)),
(Location::Storage { .. }, Location::Memory) => Ok(operations::scopym(typ, val)),
_ => Err(CompileError::str(format!(
"invalid expression move: {:?} {:?}",
from, to
Expand All @@ -90,29 +92,30 @@ pub fn expr_call(
context: &Context,
exp: &Spanned<fe::Expr>,
) -> Result<yul::Expression, CompileError> {
if let (Some(call_type), fe::Expr::Call { args, .. }) = (context.get_call(exp), &exp.node) {
let yul_args: Vec<yul::Expression> = args
.node
.iter()
.map(|val| call_arg(context, val))
.collect::<Result<_, _>>()?;

return match call_type {
CallType::SelfFunction { name } => {
let func_name = utils::func_name(name);

Ok(expression! { [func_name]([yul_args...]) })
}
CallType::TypeConstructor => {
if let Some(first_arg) = yul_args.first() {
Ok(first_arg.to_owned())
} else {
Err(CompileError::static_str(
"type constructor expected a single parameter",
))
if let fe::Expr::Call { args, func } = &exp.node {
if let Some(call_type) = context.get_call(func) {
let yul_args: Vec<yul::Expression> = args
.node
.iter()
.map(|val| call_arg(context, val))
.collect::<Result<_, _>>()?;

return match call_type {
CallType::TypeConstructor { .. } => Ok(yul_args[0].to_owned()),
CallType::SelfAttribute { func_name } => {
let func_name = utils::func_name(func_name);

Ok(expression! { [func_name]([yul_args...]) })
}
}
};
CallType::ValueAttribute { .. } => {
if let fe::Expr::Attribute { value, .. } = &func.node {
expr(context, value)
} else {
unreachable!()
}
}
};
}
}

unreachable!()
Expand Down Expand Up @@ -296,8 +299,8 @@ fn expr_attribute(
) -> Result<yul::Expression, CompileError> {
if let fe::Expr::Attribute { value, attr } = &exp.node {
return match expr_name_str(value)? {
"msg" => expr_attribute_msg(attr),
"self" => expr_attribute_self(context, exp),
builtins::MSG => expr_attribute_msg(attr),
builtins::SELF => expr_attribute_self(context, exp),
_ => Err(CompileError::static_str("invalid attributes")),
};
}
Expand All @@ -307,7 +310,7 @@ fn expr_attribute(

fn expr_attribute_msg(attr: &Spanned<&str>) -> Result<yul::Expression, CompileError> {
match attr.node {
"sender" => Ok(expression! { caller() }),
builtins::SENDER => Ok(expression! { caller() }),
_ => Err(CompileError::static_str("invalid msg attribute name")),
}
}
Expand Down Expand Up @@ -476,7 +479,7 @@ mod tests {

assert_eq!(
result,
"scopy(dualkeccak256(0, mloadn(add($bar_array, mul($index, 20)), 20)), 160)"
"scopym(dualkeccak256(0, mloadn(add($bar_array, mul($index, 20)), 20)), 160)"
);
}

Expand Down
52 changes: 28 additions & 24 deletions compiler/src/yul/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,57 @@ use yultsur::*;
/// Loads a value from storage.
///
/// The returned expression evaluates to a 256 bit value.
pub fn sto_to_val<T: FeSized>(typ: T, sptr: yul::Expression) -> yul::Expression {
pub fn sload<T: FeSized>(typ: T, sptr: yul::Expression) -> yul::Expression {
let size = literal_expression! { (typ.size()) };
expression! { sloadn([sptr], [size]) }
}

/// Copies a segment of storage into memory.
///
/// The returned expression evaluates to a memory pointer.
pub fn sto_to_mem<T: FeSized>(typ: T, sptr: yul::Expression) -> yul::Expression {
let size = literal_expression! { (typ.size()) };
expression! { scopy([sptr], [size]) }
}

/// Stores a 256 bit value in storage.
pub fn val_to_sto<T: FeSized>(
typ: T,
sptr: yul::Expression,
value: yul::Expression,
) -> yul::Statement {
pub fn sstore<T: FeSized>(typ: T, sptr: yul::Expression, value: yul::Expression) -> yul::Statement {
let size = literal_expression! { (typ.size()) };
statement! { sstoren([sptr], [value], [size]) }
}

/// Stores a 256 bit value in memory.
pub fn val_to_mem<T: FeSized>(
typ: T,
mptr: yul::Expression,
value: yul::Expression,
) -> yul::Statement {
pub fn mstore<T: FeSized>(typ: T, mptr: yul::Expression, value: yul::Expression) -> yul::Statement {
let size = literal_expression! { (typ.size()) };
statement! { mstoren([mptr], [value], [size]) }
}

/// Copies a segment of memory into storage.
pub fn mem_to_sto<T: FeSized>(
pub fn mcopys<T: FeSized>(typ: T, sptr: yul::Expression, mptr: yul::Expression) -> yul::Statement {
let size = literal_expression! { (typ.size()) };
statement! { mcopys([mptr], [sptr], [size]) }
}

/// Copies a segment of storage into memory.
///
/// The returned expression evaluates to a memory pointer.
pub fn scopym<T: FeSized>(typ: T, sptr: yul::Expression) -> yul::Expression {
let size = literal_expression! { (typ.size()) };
expression! { scopym([sptr], [size]) }
}

/// Copies a segment of storage to another segment of storage.
pub fn scopys<T: FeSized>(
typ: T,
sptr: yul::Expression,
mptr: yul::Expression,
dest_ptr: yul::Expression,
origin_ptr: yul::Expression,
) -> yul::Statement {
let size = literal_expression! { (typ.size()) };
statement! { mcopy([mptr], [sptr], [size]) }
statement! { scopys([origin_ptr], [dest_ptr], [size]) }
}

/// Copies a segment of memory to another segment of memory.
pub fn mcopym<T: FeSized>(typ: T, ptr: yul::Expression) -> yul::Expression {
let size = literal_expression! { (typ.size()) };
expression! { mcopym([ptr], [size]) }
}

/// Loads a value in memory.
///
/// The returned expression evaluates to a 256 bit value.
pub fn mem_to_val<T: FeSized>(typ: T, mptr: yul::Expression) -> yul::Expression {
pub fn mload<T: FeSized>(typ: T, mptr: yul::Expression) -> yul::Expression {
let size = literal_expression! { (typ.size()) };
expression! { mloadn([mptr], [size]) }
}
Expand Down
101 changes: 84 additions & 17 deletions compiler/src/yul/runtime/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ pub fn std() -> Vec<yul::Statement> {
alloc(),
alloc_mstoren(),
free(),
ccopy(),
mcopy(),
scopy(),
ccopym(),
mcopys(),
scopym(),
mcopym(),
scopys(),
mloadn(),
sloadn(),
cloadn(),
Expand Down Expand Up @@ -62,39 +64,104 @@ pub fn free() -> yul::Statement {
}

/// Copy calldata to memory a newly allocated segment of memory.
pub fn ccopy() -> yul::Statement {
pub fn ccopym() -> yul::Statement {
function_definition! {
function ccopy(cptr, size) -> mptr {
function ccopym(cptr, size) -> mptr {
(mptr := alloc(size))
(calldatacopy(mptr, cptr, size))
}
}
}

/// Copy memory to a given segment of storage.
pub fn mcopy() -> yul::Statement {
pub fn mcopys() -> yul::Statement {
function_definition! {
function mcopy(mptr, sptr, size) {
(for {(let i := 0)} (lt(i, size)) {(i := add(i, 1))}
function mcopys(mptr, sptr, size) {
(let offset := 0)
(for { } (lt((add(offset, 32)), size)) { }
{
(let _mptr := add(mptr, i))
(let _sptr := add(sptr, i))
(sstoren(_sptr, (mloadn(_mptr, 1)), 1))
(let _mptr := add(mptr, offset))
(let _sptr := add(sptr, offset))
(sstore(_sptr, (mload(_mptr))))
(offset := add(offset, 32))
})

(let rem := sub(size, offset))
(if (gt(rem, 0)) {
(let _mptr := add(mptr, offset))
(let _sptr := add(sptr, offset))
(sstoren(_sptr, (mloadn(_mptr, rem)), rem))
g-r-a-n-t marked this conversation as resolved.
Show resolved Hide resolved
})
}
}
}

/// Copy storage to a newly allocated segment of memory.
pub fn scopy() -> yul::Statement {
pub fn scopym() -> yul::Statement {
function_definition! {
function scopy(sptr, size) -> mptr {
function scopym(sptr, size) -> mptr {
(mptr := alloc(size))
(for {(let i := 0)} (lt(i, size)) {(i := add(i, 1))}
(let offset := 0)
(for { } (lt((add(offset, 32)), size)) { }
{
(let _mptr := add(mptr, offset))
(let _sptr := add(sptr, offset))
(mstore(_mptr, (sload(_sptr))))
(offset := add(offset, 32))
})

(let rem := sub(size, offset))
(if (gt(rem, 0)) {
(let _mptr := add(mptr, offset))
(let _sptr := add(sptr, offset))
(mstoren(_mptr, (sloadn(_sptr, rem)), rem))
})
}
}
}

/// Copies a segment of storage to another segment of storage.
pub fn scopys() -> yul::Statement {
function_definition! {
function scopys(ptr1, ptr2, size) {
(let offset := 0)
(for { } (lt((add(offset, 32)), size)) { }
{
(let _mptr := add(mptr, i))
(let _sptr := add(sptr, i))
(mstoren(_mptr, (sloadn(_sptr, 1)), 1))
(let _ptr1 := add(ptr1, offset))
(let _ptr2 := add(ptr2, offset))
(sstore(_ptr2, (sload(_ptr1))))
(offset := add(offset, 32))
})

(let rem := sub(size, offset))
(if (gt(rem, 0)) {
(let _ptr1 := add(ptr1, offset))
(let _ptr2 := add(ptr2, offset))
(sstoren(_ptr2, (sloadn(_ptr1, rem)), rem))
})
}
}
}

/// Copies a segment of memory to another segment of memory.
pub fn mcopym() -> yul::Statement {
function_definition! {
function mcopym(ptr1, size) -> ptr2 {
(ptr2 := alloc(size))
(let offset := 0)
(for { } (lt((add(offset, 32)), size)) { }
{
(let _ptr1 := add(ptr1, offset))
(let _ptr2 := add(ptr2, offset))
(mstore(_ptr2, (mload(_ptr1))))
(offset := add(offset, 32))
})

(let rem := sub(size, offset))
(if (gt(rem, 0)) {
(let _ptr1 := add(ptr1, offset))
(let _ptr2 := add(ptr2, offset))
(mstoren(_ptr2, (mloadn(_ptr1, rem)), rem))
})
}
}
Expand Down
Loading