Skip to content

Commit

Permalink
Merge pull request #155 from g-r-a-n-t/data-operations
Browse files Browse the repository at this point in the history
Memory and storage copying
  • Loading branch information
g-r-a-n-t authored Dec 17, 2020
2 parents 220185f + 9090fb7 commit 8e3189b
Show file tree
Hide file tree
Showing 23 changed files with 584 additions and 222 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! { 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))
})
}
}
}

/// 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

0 comments on commit 8e3189b

Please sign in to comment.