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: | ||||
|  * 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: | ||||
|  | ||||
| @ -335,8 +335,13 @@ 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; | ||||
| 		// 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; | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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)
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user