aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2016-12-12 00:51:17 +0800
committerchriseth <c@ethdev.com>2016-12-12 00:57:15 +0800
commitbfa4f451160bff14d79bf6e25d969b1f586a8b00 (patch)
treedd16eb515fd3c1594a044d1ec7235782ef6ffa36
parent4184525d4ad7aff3acb2d521c3cdc21054e36eff (diff)
downloaddexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.tar.gz
dexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.tar.zst
dexon-solidity-bfa4f451160bff14d79bf6e25d969b1f586a8b00.zip
Split memcopy into three functions.
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp9
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp114
-rw-r--r--libsolidity/codegen/CompilerUtils.h10
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: