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 10, 2020
1 parent cb1bdc7 commit 1d52991
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 36 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
8 changes: 6 additions & 2 deletions compiler/src/yul/mappers/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ 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)?;
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/yul/mappers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,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 Down Expand Up @@ -430,7 +431,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
36 changes: 36 additions & 0 deletions compiler/tests/evm_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -836,3 +836,39 @@ fn erc20_token() {
);
});
}

#[test]
fn data_copying_stress() {
with_executor(&|mut executor| {
let harness = deploy_contract(&mut executor, "data_copying_stress.fe", "Foo", vec![]);

harness.test_function(
&mut executor,
"set_my_vals",
vec![
string_token("my string"),
string_token("my other string"),
uint_token(26),
uint_token(42),
],
None,
);

harness.test_function(&mut executor, "emit_my_event", vec![], None);

harness.test_function(&mut executor, "set_to_my_other_vals", vec![], None);

harness.test_function(&mut executor, "emit_my_event", vec![], None);

harness.events_emitted(
executor,
vec![
("MyEvent", vec![string_token("my string"), uint_token(26)]),
(
"MyEvent",
vec![string_token("my other string"), uint_token(42)],
),
],
);
});
}
31 changes: 31 additions & 0 deletions compiler/tests/fixtures/data_copying_stress.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
contract Foo:
my_string: string42
my_other_string: string42

my_u256: u256
my_other_u256: u256

event MyEvent:
my_string: string42
my_u256: u256

pub def set_my_vals(
my_string: string42,
my_other_string: string42,
my_u256: u256,
my_other_u256: u256
):
self.my_string = my_string
self.my_other_string = my_other_string
self.my_u256 = my_u256
self.my_other_u256 = my_other_u256

pub def set_to_my_other_vals():
self.my_string = self.my_other_string
self.my_u256 = self.my_other_u256

pub def emit_my_event():
self.emit_my_event_internal(self.my_string, self.my_u256)

def emit_my_event_internal(some_string: string42, some_u256: u256):
emit MyEvent(some_string, some_u256)
17 changes: 14 additions & 3 deletions semantics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,27 @@ impl ExpressionAttributes {
/// Base types are moved to the stack and reference types are moved to a
/// segment of memory.
pub fn with_default_move(mut self) -> Result<Self, SemanticError> {
let move_location = match self.typ {
let default_location = match self.typ {
Type::Base(_) => Location::Value,
Type::Array(_) => Location::Memory,
Type::Tuple(_) => Location::Memory,
Type::String(_) => Location::Memory,
Type::Map(_) => return Err(SemanticError::cannot_move()),
};

if self.location != move_location {
self.move_location = Some(move_location)
match (&self.location, default_location) {
(Location::Storage { .. }, Location::Memory) => {
self.move_location = Some(Location::Memory)
}
(Location::Memory, Location::Memory) => {
// we copy the segment of memory
self.move_location = Some(Location::Memory)
}
(Location::Storage { .. }, Location::Value) => {
self.move_location = Some(Location::Value)
}
(Location::Memory, Location::Value) => self.move_location = Some(Location::Value),
_ => {}
}

Ok(self)
Expand Down

0 comments on commit 1d52991

Please sign in to comment.