diff options
author | chriseth <c@ethdev.com> | 2016-12-12 00:51:17 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2016-12-12 00:57:15 +0800 |
commit | bfa4f451160bff14d79bf6e25d969b1f586a8b00 (patch) | |
tree | dd16eb515fd3c1594a044d1ec7235782ef6ffa36 | |
parent | 4184525d4ad7aff3acb2d521c3cdc21054e36eff (diff) | |
download | dexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.tar.gz dexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.tar.zst dexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.zip |
Split memcopy into three functions.
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 9 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 114 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 10 |
3 files changed, 79 insertions, 54 deletions
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 2c982982..352c7177 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -335,9 +335,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord if (baseSize > 1) m_context << u256(baseSize) << Instruction::MUL; // stack: <target> <source> <size> - //@TODO do not use ::CALL if less than 32 bytes? 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; // stack: <target> <size> diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 8b94bd0d..da2e78e8 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -298,61 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) 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 + m_context.appendInlineAssembly(R"( + { + let words := div(add(len, 31), 32) + let cost := add(15, mul(3, words)) + 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; +} + +void CompilerUtils::memoryCopy32() +{ // Stack here: size target source - if (!_useIdentityPrecompile) - { - m_context.appendInlineAssembly(R"( - { - // expects three locals: src, dst, len - - // copy 32 bytes at once - start32: - jumpi(end32, lt(len, 32)) - 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) - 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; - m_context << Instruction::POP; - m_context << Instruction::POP; - return; - } - else - { - m_context.appendInlineAssembly(R"( - { - let words := div(add(len, 31), 32) - let cost := add(15, mul(3, words)) - jump(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len))) - } - )", - { "len", "dst", "src" }, - map<string, string> { - { "$identityContractAddress", toString(identityContractAddress) } - } - ); - m_context << Instruction::POP; - m_context << Instruction::POP; - m_context << Instruction::POP; - return; - } + m_context.appendInlineAssembly(R"( + { + jumpi(end, eq(len, 0)) + start: + mstore(dst, mload(src)) + jumpi(end, iszero(gt(len, 32))) + dst := add(dst, 32) + src := add(src, 32) + len := sub(len, 32) + jump(start) + end: + } + )", + { "len", "dst", "src" } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; +} + +void CompilerUtils::memoryCopy() +{ + // Stack here: size target source + + m_context.appendInlineAssembly(R"( + { + // copy 32 bytes at once + start32: + jumpi(end32, lt(len, 32)) + 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) + 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) diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 52b5b0d6..ad53efea 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -112,7 +112,15 @@ public: /// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Stack pre: <size> <target> <source> /// 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) /// external function type <address><function identifier> into two stack slots: |