Skip to content

Commit

Permalink
Memory and storage copying.
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t committed Dec 16, 2020
1 parent cb1bdc7 commit a18a55e
Show file tree
Hide file tree
Showing 22 changed files with 532 additions and 193 deletions.
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! { calldata_to_mem(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! { calldata_to_mem([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 := calldata_to_mem(add(start, calldataload(head_ptr)), add(32, mul(calldataload(add(start, calldataload(head_ptr))), 1))) }"
)
}

Expand Down
12 changes: 7 additions & 5 deletions compiler/src/yul/mappers/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,21 @@ pub fn assign(
let value = operations::mem_to_val(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::mem_to_mem(typ, value);
statement! { [target] := [ptr] }
}
(Location::Storage { .. }, Location::Storage { .. }) => {
unimplemented!("storage copying")
operations::sto_to_sto(typ, target, value)
}
(Location::Storage { .. }, Location::Value) => {
let target = expr_as_ident(target)?;
let value = operations::sto_to_val(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)
Expand Down
57 changes: 30 additions & 27 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 @@ -64,7 +65,8 @@ fn move_expression(

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::Memory, Location::Value) => Ok(operations::mem_to_val(typ, val)),
(Location::Memory, Location::Memory) => Ok(operations::mem_to_mem(typ, val)),
(Location::Storage { .. }, Location::Memory) => Ok(operations::sto_to_mem(typ, val)),
_ => Err(CompileError::str(format!(
"invalid expression move: {:?} {:?}",
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 @@ -250,8 +253,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 @@ -261,7 +264,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 @@ -430,7 +433,7 @@ mod tests {

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

Expand Down
34 changes: 25 additions & 9 deletions compiler/src/yul/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ pub fn sto_to_val<T: FeSized>(typ: T, sptr: yul::Expression) -> yul::Expression
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,
Expand Down Expand Up @@ -50,7 +42,31 @@ pub fn mem_to_sto<T: FeSized>(
mptr: yul::Expression,
) -> yul::Statement {
let size = literal_expression! { (typ.size()) };
statement! { mcopy([mptr], [sptr], [size]) }
statement! { mem_to_sto([mptr], [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! { sto_to_mem([sptr], [size]) }
}

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

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

/// Loads a value in memory.
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(),
calldata_to_mem(),
mem_to_sto(),
sto_to_mem(),
mem_to_mem(),
sto_to_sto(),
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 calldata_to_mem() -> yul::Statement {
function_definition! {
function ccopy(cptr, size) -> mptr {
function calldata_to_mem(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 mem_to_sto() -> yul::Statement {
function_definition! {
function mcopy(mptr, sptr, size) {
(for {(let i := 0)} (lt(i, size)) {(i := add(i, 1))}
function mem_to_sto(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))
})
}
}
}

/// Copy storage to a newly allocated segment of memory.
pub fn scopy() -> yul::Statement {
pub fn sto_to_mem() -> yul::Statement {
function_definition! {
function scopy(sptr, size) -> mptr {
function sto_to_mem(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 sto_to_sto() -> yul::Statement {
function_definition! {
function sto_to_sto(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 mem_to_mem() -> yul::Statement {
function_definition! {
function mem_to_mem(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
3 changes: 2 additions & 1 deletion compiler/tests/compile_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use std::fs;
case("return_call_to_fn_with_param_type_mismatch.fe", "TypeError"),
case("return_addition_with_mixed_types.fe", "TypeError"),
case("return_lt_mixed_types.fe", "TypeError"),
case("indexed_event.fe", "MoreThanThreeIndexedParams")
case("indexed_event.fe", "MoreThanThreeIndexedParams"),
case("needs_mem_copy.fe", "CannotMove")
)]
fn test_compile_errors(fixture_file: &str, expected_error: &str) {
let src = fs::read_to_string(format!("tests/fixtures/compile_errors/{}", fixture_file))
Expand Down
Loading

0 comments on commit a18a55e

Please sign in to comment.