Split memcopy into three functions.

This commit is contained in:
chriseth 2016-12-11 17:51:17 +01:00
parent 4184525d4a
commit bfa4f45116
3 changed files with 78 additions and 53 deletions

View File

@ -335,9 +335,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
if (baseSize > 1) if (baseSize > 1)
m_context << u256(baseSize) << Instruction::MUL; m_context << u256(baseSize) << Instruction::MUL;
// stack: <target> <source> <size> // stack: <target> <source> <size>
//@TODO do not use ::CALL if less than 32 bytes?
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4; m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
utils.memoryCopy(); // We can resort to copying full 32 bytes only if
// - the length is known to be a multiple of 32 or
// - we will pad to full 32 bytes later anyway.
if (((baseSize % 32) == 0) || _padToWordBoundaries)
utils.memoryCopy32();
else
utils.memoryCopy();
m_context << Instruction::SWAP1 << Instruction::POP; m_context << Instruction::SWAP1 << Instruction::POP;
// stack: <target> <size> // stack: <target> <size>

View File

@ -298,61 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
m_context << Instruction::SWAP1 << Instruction::POP; m_context << Instruction::SWAP1 << Instruction::POP;
} }
void CompilerUtils::memoryCopy(bool _useIdentityPrecompile) void CompilerUtils::memoryCopyPrecompile()
{ {
//@TODO do not use ::CALL if less than 32 bytes?
// Stack here: size target source // Stack here: size target source
if (!_useIdentityPrecompile) m_context.appendInlineAssembly(R"(
{ {
m_context.appendInlineAssembly(R"( let words := div(add(len, 31), 32)
{ let cost := add(15, mul(3, words))
// expects three locals: src, dst, len jumpi(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len)))
}
)",
{ "len", "dst", "src" },
map<string, string> {
{ "$identityContractAddress", toString(identityContractAddress) }
}
);
m_context << Instruction::POP << Instruction::POP << Instruction::POP;
}
// copy 32 bytes at once void CompilerUtils::memoryCopy32()
start32: {
jumpi(end32, lt(len, 32)) // Stack here: size target source
mstore(dst, mload(src))
dst := add(dst, 32)
src := add(src, 32)
len := sub(len, 32)
jump(start32)
end32:
// copy the remainder (0 < len < 32) m_context.appendInlineAssembly(R"(
let mask := sub(exp(256, sub(32, len)), 1) {
let srcpart := and(mload(src), not(mask)) jumpi(end, eq(len, 0))
let dstpart := and(mload(dst), mask) start:
mstore(dst, or(srcpart, dstpart)) mstore(dst, mload(src))
} jumpi(end, iszero(gt(len, 32)))
)", dst := add(dst, 32)
{ "len", "dst", "src" } src := add(src, 32)
); len := sub(len, 32)
m_context << Instruction::POP; jump(start)
m_context << Instruction::POP; end:
m_context << Instruction::POP; }
return; )",
} { "len", "dst", "src" }
else );
{ m_context << Instruction::POP << Instruction::POP << Instruction::POP;
m_context.appendInlineAssembly(R"( }
{
let words := div(add(len, 31), 32) void CompilerUtils::memoryCopy()
let cost := add(15, mul(3, words)) {
jump(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len))) // Stack here: size target source
}
)", m_context.appendInlineAssembly(R"(
{ "len", "dst", "src" }, {
map<string, string> { // copy 32 bytes at once
{ "$identityContractAddress", toString(identityContractAddress) } start32:
} jumpi(end32, lt(len, 32))
); mstore(dst, mload(src))
m_context << Instruction::POP; dst := add(dst, 32)
m_context << Instruction::POP; src := add(src, 32)
m_context << Instruction::POP; len := sub(len, 32)
return; jump(start32)
} end32:
// copy the remainder (0 < len < 32)
let mask := sub(exp(256, sub(32, len)), 1)
let srcpart := and(mload(src), not(mask))
let dstpart := and(mload(dst), mask)
mstore(dst, or(srcpart, dstpart))
}
)",
{ "len", "dst", "src" }
);
m_context << Instruction::POP << Instruction::POP << Instruction::POP;
} }
void CompilerUtils::splitExternalFunctionType(bool _leftAligned) void CompilerUtils::splitExternalFunctionType(bool _leftAligned)

View File

@ -112,7 +112,15 @@ public:
/// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Uses a CALL to the identity contract to perform a memory-to-memory copy.
/// Stack pre: <size> <target> <source> /// Stack pre: <size> <target> <source>
/// Stack post: /// Stack post:
void memoryCopy(bool _useIdentityPrecompile = false); void memoryCopyPrecompile();
/// Copies full 32 byte words in memory (regions cannot overlap), i.e. may copy more than length.
/// Stack pre: <size> <target> <source>
/// Stack post:
void memoryCopy32();
/// Copies data in memory (regions cannot overlap).
/// Stack pre: <size> <target> <source>
/// Stack post:
void memoryCopy();
/// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true) /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
/// external function type <address><function identifier> into two stack slots: /// external function type <address><function identifier> into two stack slots: