aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYoichi Hirai <i@yoichihirai.com>2016-12-13 05:39:47 +0800
committerGitHub <noreply@github.com>2016-12-13 05:39:47 +0800
commit1c3605362d6018f5ab72cc84c7511fb49c15d126 (patch)
tree0dd967a78fc37b9efc49fb7f71578a0fb52432f0
parentc111d6e27e7a951d49e587d0984fc8c24898c18f (diff)
parent81d7e0233aa24a2d38103b2bf746cef5eb12a893 (diff)
downloaddexon-solidity-1c3605362d6018f5ab72cc84c7511fb49c15d126.tar.gz
dexon-solidity-1c3605362d6018f5ab72cc84c7511fb49c15d126.tar.zst
dexon-solidity-1c3605362d6018f5ab72cc84c7511fb49c15d126.zip
Merge pull request #1468 from ethereum/memcpy-assembly
Implement memcpy without the identity precompile
-rw-r--r--Changelog.md1
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp9
-rw-r--r--libsolidity/codegen/CompilerContext.cpp4
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp77
-rw-r--r--libsolidity/codegen/CompilerUtils.h8
5 files changed, 83 insertions, 16 deletions
diff --git a/Changelog.md b/Changelog.md
index 1eb90c22..c3b3f5fa 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,6 +3,7 @@
Features:
* Type checker: Warn when ``msg.value`` is used in non-payable function.
* Code generator: Inject the Swarm hash of a metadata file into the bytecode.
+ * Code generator: Replace expensive memcpy precompile by simple assembly loop.
* Optimizer: Some dead code elimination.
Bugfixes:
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/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 2de5a3ec..c14ab845 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -202,6 +202,8 @@ void CompilerContext::appendInlineAssembly(
return false;
unsigned stackDepth = _localVariables.end() - it;
int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
+ if (_context == assembly::CodeGenerator::IdentifierContext::LValue)
+ stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
@@ -217,7 +219,7 @@ void CompilerContext::appendInlineAssembly(
return true;
};
- solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "");
+ solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block.");
}
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 7c159ff7..da2e78e8 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -298,22 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
m_context << Instruction::SWAP1 << Instruction::POP;
}
+void CompilerUtils::memoryCopyPrecompile()
+{
+ // 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
+
+ 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
- // stack for call: outsize target size source value contract gas
- //@TODO do not use ::CALL if less than 32 bytes?
- m_context << Instruction::DUP3 << Instruction::SWAP1;
- m_context << u256(0) << u256(identityContractAddress);
- // compute gas costs
- m_context << u256(32) << Instruction::DUP5 << u256(31) << Instruction::ADD;
- static unsigned c_identityGas = 15;
- static unsigned c_identityWordGas = 3;
- m_context << Instruction::DIV << u256(c_identityWordGas) << Instruction::MUL;
- m_context << u256(c_identityGas) << Instruction::ADD;
- m_context << Instruction::CALL;
- m_context << Instruction::ISZERO;
- m_context.appendConditionalJumpTo(m_context.errorTag());
+
+ 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 0a5d8e1c..ad53efea 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -112,6 +112,14 @@ public:
/// Uses a CALL to the identity contract to perform a memory-to-memory copy.
/// Stack pre: <size> <target> <source>
/// Stack post:
+ 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)