mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #1468 from ethereum/memcpy-assembly
Implement memcpy without the identity precompile
This commit is contained in:
		
						commit
						1c3605362d
					
				| @ -3,6 +3,7 @@ | |||||||
| Features: | Features: | ||||||
|  * Type checker: Warn when ``msg.value`` is used in non-payable function. |  * 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: 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. |  * Optimizer: Some dead code elimination. | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  | |||||||
| @ -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>
 | ||||||
|  | |||||||
| @ -202,6 +202,8 @@ void CompilerContext::appendInlineAssembly( | |||||||
| 			return false; | 			return false; | ||||||
| 		unsigned stackDepth = _localVariables.end() - it; | 		unsigned stackDepth = _localVariables.end() - it; | ||||||
| 		int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; | 		int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; | ||||||
|  | 		if (_context == assembly::CodeGenerator::IdentifierContext::LValue) | ||||||
|  | 			stackDiff -= 1; | ||||||
| 		if (stackDiff < 1 || stackDiff > 16) | 		if (stackDiff < 1 || stackDiff > 16) | ||||||
| 			BOOST_THROW_EXCEPTION( | 			BOOST_THROW_EXCEPTION( | ||||||
| 				CompilerError() << | 				CompilerError() << | ||||||
| @ -217,7 +219,7 @@ void CompilerContext::appendInlineAssembly( | |||||||
| 		return true; | 		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( | FunctionDefinition const& CompilerContext::resolveVirtualFunction( | ||||||
|  | |||||||
| @ -298,22 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) | |||||||
| 	m_context << Instruction::SWAP1 << Instruction::POP; | 	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() | void CompilerUtils::memoryCopy() | ||||||
| { | { | ||||||
| 	// Stack here: size target source
 | 	// 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.appendInlineAssembly(R"( | ||||||
| 	m_context << Instruction::DUP3 << Instruction::SWAP1; | 		{ | ||||||
| 	m_context << u256(0) << u256(identityContractAddress); | 		// copy 32 bytes at once
 | ||||||
| 	// compute gas costs
 | 		start32: | ||||||
| 	m_context << u256(32) << Instruction::DUP5 << u256(31) << Instruction::ADD; | 		jumpi(end32, lt(len, 32)) | ||||||
| 	static unsigned c_identityGas = 15; | 		mstore(dst, mload(src)) | ||||||
| 	static unsigned c_identityWordGas = 3; | 		dst := add(dst, 32) | ||||||
| 	m_context << Instruction::DIV << u256(c_identityWordGas) << Instruction::MUL; | 		src := add(src, 32) | ||||||
| 	m_context << u256(c_identityGas) << Instruction::ADD; | 		len := sub(len, 32) | ||||||
| 	m_context << Instruction::CALL; | 		jump(start32) | ||||||
| 	m_context << Instruction::ISZERO; | 		end32: | ||||||
| 	m_context.appendConditionalJumpTo(m_context.errorTag()); | 
 | ||||||
|  | 		// 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) | ||||||
|  | |||||||
| @ -112,6 +112,14 @@ 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 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(); | 	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)
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user