mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Simple peephole optimizer that is activated even if not requested.
This commit is contained in:
		
							parent
							
								
									22b4d1b29a
								
							
						
					
					
						commit
						0335ed4cb4
					
				| @ -20,13 +20,17 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include "Assembly.h" | ||||
| #include <fstream> | ||||
| 
 | ||||
| #include <libevmasm/CommonSubexpressionEliminator.h> | ||||
| #include <libevmasm/ControlFlowGraph.h> | ||||
| #include <libevmasm/PeepholeOptimiser.h> | ||||
| #include <libevmasm/BlockDeduplicator.h> | ||||
| #include <libevmasm/ConstantOptimiser.h> | ||||
| #include <libevmasm/GasMeter.h> | ||||
| 
 | ||||
| #include <fstream> | ||||
| #include <json/json.h> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev; | ||||
| using namespace dev::eth; | ||||
| @ -314,16 +318,15 @@ void Assembly::injectStart(AssemblyItem const& _i) | ||||
| 
 | ||||
| Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs) | ||||
| { | ||||
| 	if (_enable) | ||||
| 		optimiseInternal(_isCreation, _runs); | ||||
| 	optimiseInternal(_enable, _isCreation, _runs); | ||||
| 	return *this; | ||||
| } | ||||
| 
 | ||||
| map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs) | ||||
| map<u256, u256> Assembly::optimiseInternal(bool _enable, bool _isCreation, size_t _runs) | ||||
| { | ||||
| 	for (size_t subId = 0; subId < m_subs.size(); ++subId) | ||||
| 	{ | ||||
| 		map<u256, u256> subTagReplacements = m_subs[subId]->optimiseInternal(false, _runs); | ||||
| 		map<u256, u256> subTagReplacements = m_subs[subId]->optimiseInternal(_enable, false, _runs); | ||||
| 		BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId); | ||||
| 	} | ||||
| 
 | ||||
| @ -333,6 +336,13 @@ map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs) | ||||
| 	{ | ||||
| 		count = 0; | ||||
| 
 | ||||
| 		PeepholeOptimiser peepOpt(m_items); | ||||
| 		if (peepOpt.optimise()) | ||||
| 			count++; | ||||
| 
 | ||||
| 		if (!_enable) | ||||
| 			continue; | ||||
| 
 | ||||
| 		// This only modifies PushTags, we have to run again to actually remove code.
 | ||||
| 		BlockDeduplicator dedup(m_items); | ||||
| 		if (dedup.deduplicate()) | ||||
| @ -399,12 +409,13 @@ map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	total += ConstantOptimisationMethod::optimiseConstants( | ||||
| 		_isCreation, | ||||
| 		_isCreation ? 1 : _runs, | ||||
| 		*this, | ||||
| 		m_items | ||||
| 	); | ||||
| 	if (_enable) | ||||
| 		total += ConstantOptimisationMethod::optimiseConstants( | ||||
| 			_isCreation, | ||||
| 			_isCreation ? 1 : _runs, | ||||
| 			*this, | ||||
| 			m_items | ||||
| 		); | ||||
| 
 | ||||
| 	return tagReplacements; | ||||
| } | ||||
|  | ||||
| @ -101,6 +101,7 @@ public: | ||||
| 	/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
 | ||||
| 	/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
 | ||||
| 	/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
 | ||||
| 	/// If @a _enable is not set, will perform some simple peephole optimizations.
 | ||||
| 	Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200); | ||||
| 	Json::Value stream( | ||||
| 		std::ostream& _out, | ||||
| @ -112,7 +113,7 @@ public: | ||||
| protected: | ||||
| 	/// Does the same operations as @a optimise, but should only be applied to a sub and
 | ||||
| 	/// returns the replaced tags.
 | ||||
| 	std::map<u256, u256> optimiseInternal(bool _isCreation, size_t _runs); | ||||
| 	std::map<u256, u256> optimiseInternal(bool _enable, bool _isCreation, size_t _runs); | ||||
| 
 | ||||
| 	std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const; | ||||
| 	void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); } | ||||
|  | ||||
							
								
								
									
										146
									
								
								libevmasm/PeepholeOptimiser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								libevmasm/PeepholeOptimiser.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| /*
 | ||||
| 	This file is part of cpp-ethereum. | ||||
| 
 | ||||
| 	cpp-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	cpp-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /**
 | ||||
|  * @file PeepholeOptimiser.h | ||||
|  * Performs local optimising code changes to assembly. | ||||
|  */ | ||||
| 
 | ||||
| #include "PeepholeOptimiser.h" | ||||
| 
 | ||||
| #include <libevmasm/AssemblyItem.h> | ||||
| #include <libevmasm/SemanticInformation.h> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev::eth; | ||||
| using namespace dev; | ||||
| 
 | ||||
| // TODO: Extend this to use the tools from ExpressionClasses.cpp
 | ||||
| 
 | ||||
| struct Identity | ||||
| { | ||||
| 	static size_t windowSize() { return 1; } | ||||
| 	static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) | ||||
| 	{ | ||||
| 		*_out = *_in; | ||||
| 		return true; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct PushPop | ||||
| { | ||||
| 	static size_t windowSize() { return 2; } | ||||
| 	static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>) | ||||
| 	{ | ||||
| 		auto t = _in[0].type(); | ||||
| 		if (_in[1] == Instruction::POP && ( | ||||
| 			SemanticInformation::isDupInstruction(_in[0]) || | ||||
| 			t == Push || t == PushString || t == PushTag || t == PushSub || | ||||
| 			t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress | ||||
| 		)) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct DoubleSwap | ||||
| { | ||||
| 	static size_t windowSize() { return 2; } | ||||
| 	static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>) | ||||
| 	{ | ||||
| 		if (_in[0] == _in[1] && SemanticInformation::isSwapInstruction(_in[0])) | ||||
| 			return true; | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct JumpToNext | ||||
| { | ||||
| 	static size_t windowSize() { return 3; } | ||||
| 	static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) | ||||
| 	{ | ||||
| 		if ( | ||||
| 			_in[0].type() == PushTag && | ||||
| 			(_in[1] == Instruction::JUMP || _in[1] == Instruction::JUMPI) && | ||||
| 			_in[2].type() == Tag && | ||||
| 			_in[0].data() == _in[2].data() | ||||
| 		) | ||||
| 		{ | ||||
| 			*_out = _in[2]; | ||||
| 			return true; | ||||
| 		} | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct TagConjunctions | ||||
| { | ||||
| 	static size_t windowSize() { return 3; } | ||||
| 	static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) | ||||
| 	{ | ||||
| 		if ( | ||||
| 			_in[0].type() == PushTag && | ||||
| 			_in[2] == Instruction::AND && | ||||
| 			_in[1].type() == Push && | ||||
| 			(_in[1].data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF) | ||||
| 		) | ||||
| 		{ | ||||
| 			*_out = _in[0]; | ||||
| 			return true; | ||||
| 		} | ||||
| 		else | ||||
| 			return false; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct OptimiserState | ||||
| { | ||||
| 	AssemblyItems const& items; | ||||
| 	size_t i; | ||||
| 	std::back_insert_iterator<AssemblyItems> out; | ||||
| }; | ||||
| 
 | ||||
| void applyMethods(OptimiserState&) | ||||
| { | ||||
| 	assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity."); | ||||
| } | ||||
| 
 | ||||
| template <typename Method, typename... OtherMethods> | ||||
| void applyMethods(OptimiserState& _state, Method, OtherMethods... _other) | ||||
| { | ||||
| 	if (_state.i + Method::windowSize() <= _state.items.size() && Method::apply(_state.items.begin() + _state.i, _state.out)) | ||||
| 		_state.i += Method::windowSize(); | ||||
| 	else | ||||
| 		applyMethods(_state, _other...); | ||||
| } | ||||
| 
 | ||||
| bool PeepholeOptimiser::optimise() | ||||
| { | ||||
| 	OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; | ||||
| 	while (state.i < m_items.size()) | ||||
| 		applyMethods(state, PushPop(), DoubleSwap(), JumpToNext(), TagConjunctions(), Identity()); | ||||
| 	if (m_optimisedItems.size() < m_items.size()) | ||||
| 	{ | ||||
| 		m_items = std::move(m_optimisedItems); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 		return false; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										53
									
								
								libevmasm/PeepholeOptimiser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								libevmasm/PeepholeOptimiser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| /*
 | ||||
| 	This file is part of cpp-ethereum. | ||||
| 
 | ||||
| 	cpp-ethereum is free software: you can redistribute it and/or modify | ||||
| 	it under the terms of the GNU General Public License as published by | ||||
| 	the Free Software Foundation, either version 3 of the License, or | ||||
| 	(at your option) any later version. | ||||
| 
 | ||||
| 	cpp-ethereum is distributed in the hope that it will be useful, | ||||
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| 	GNU General Public License for more details. | ||||
| 
 | ||||
| 	You should have received a copy of the GNU General Public License | ||||
| 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /**
 | ||||
|  * @file PeepholeOptimiser.h | ||||
|  * Performs local optimising code changes to assembly. | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <cstddef> | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| namespace eth | ||||
| { | ||||
| class AssemblyItem; | ||||
| using AssemblyItems = std::vector<AssemblyItem>; | ||||
| 
 | ||||
| class PeepholeOptimisationMethod | ||||
| { | ||||
| public: | ||||
| 	virtual size_t windowSize() const; | ||||
| 	virtual bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out); | ||||
| }; | ||||
| 
 | ||||
| class PeepholeOptimiser | ||||
| { | ||||
| public: | ||||
| 	explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {} | ||||
| 
 | ||||
| 	bool optimise(); | ||||
| 
 | ||||
| private: | ||||
| 	AssemblyItems& m_items; | ||||
| 	AssemblyItems m_optimisedItems; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -41,8 +41,7 @@ void Compiler::compileContract( | ||||
| 	ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize); | ||||
| 	m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts); | ||||
| 
 | ||||
| 	if (m_optimize) | ||||
| 		m_context.optimise(m_optimizeRuns); | ||||
| 	m_context.optimise(m_optimize, m_optimizeRuns); | ||||
| 
 | ||||
| 	if (_contract.isLibrary()) | ||||
| 	{ | ||||
| @ -60,8 +59,7 @@ void Compiler::compileClone( | ||||
| 	ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize); | ||||
| 	m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts); | ||||
| 
 | ||||
| 	if (m_optimize) | ||||
| 		m_context.optimise(m_optimizeRuns); | ||||
| 	m_context.optimise(m_optimize, m_optimizeRuns); | ||||
| } | ||||
| 
 | ||||
| eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const | ||||
|  | ||||
| @ -155,7 +155,7 @@ public: | ||||
| 	/// Prepends "PUSH <compiler version number> POP"
 | ||||
| 	void injectVersionStampIntoSub(size_t _subIndex); | ||||
| 
 | ||||
| 	void optimise(unsigned _runs = 200) { m_asm->optimise(true, true, _runs); } | ||||
| 	void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } | ||||
| 
 | ||||
| 	/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
 | ||||
| 	CompilerContext* runtimeContext() { return m_runtimeContext; } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user