mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Avoid sorting FunctionDefinitions by AST ID during codegen
Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
		
							parent
							
								
									cc7a14a61d
								
							
						
					
					
						commit
						e3b36f736d
					
				| @ -12,6 +12,7 @@ Compiler Features: | |||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  * AST: Fix wrong initial ID for Yul nodes in the AST. |  * AST: Fix wrong initial ID for Yul nodes in the AST. | ||||||
|  |  * Code Generator: Fix output from via-IR code generator being dependent on which files were discovered by import callback. In some cases, a different AST ID assignment would alter the order of functions in internal dispatch, resulting in superficially different but semantically equivalent bytecode. | ||||||
|  * NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface. |  * NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface. | ||||||
|  * SMTChecker: Fix encoding error that causes loops to unroll after completion. |  * SMTChecker: Fix encoding error that causes loops to unroll after completion. | ||||||
|  * SMTChecker: Fix inconsistency on constant condition checks when ``while`` or ``for`` loops are unrolled before the condition check. |  * SMTChecker: Fix inconsistency on constant condition checks when ``while`` or ``for`` loops are unrolled before the condition check. | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ | |||||||
| #include <libsolutil/StringUtils.h> | #include <libsolutil/StringUtils.h> | ||||||
| 
 | 
 | ||||||
| #include <range/v3/view/map.hpp> | #include <range/v3/view/map.hpp> | ||||||
|  | #include <range/v3/algorithm/find.hpp> | ||||||
| 
 | 
 | ||||||
| using namespace solidity; | using namespace solidity; | ||||||
| using namespace solidity::util; | using namespace solidity::util; | ||||||
| @ -41,7 +42,7 @@ std::string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefini | |||||||
| 	std::string name = IRNames::function(_function); | 	std::string name = IRNames::function(_function); | ||||||
| 
 | 
 | ||||||
| 	if (!m_functions.contains(name)) | 	if (!m_functions.contains(name)) | ||||||
| 		m_functionGenerationQueue.insert(&_function); | 		m_functionGenerationQueue.push_back(&_function); | ||||||
| 
 | 
 | ||||||
| 	return name; | 	return name; | ||||||
| } | } | ||||||
| @ -50,8 +51,8 @@ FunctionDefinition const* IRGenerationContext::dequeueFunctionForCodeGeneration( | |||||||
| { | { | ||||||
| 	solAssert(!m_functionGenerationQueue.empty(), ""); | 	solAssert(!m_functionGenerationQueue.empty(), ""); | ||||||
| 
 | 
 | ||||||
| 	FunctionDefinition const* result = *m_functionGenerationQueue.begin(); | 	FunctionDefinition const* result = m_functionGenerationQueue.front(); | ||||||
| 	m_functionGenerationQueue.erase(m_functionGenerationQueue.begin()); | 	m_functionGenerationQueue.pop_front(); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -132,7 +133,7 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern | |||||||
| { | { | ||||||
| 	solAssert(internalDispatchClean(), ""); | 	solAssert(internalDispatchClean(), ""); | ||||||
| 
 | 
 | ||||||
| 	for (DispatchSet const& functions: _internalDispatch | ranges::views::values) | 	for (DispatchQueue const& functions: _internalDispatch | ranges::views::values) | ||||||
| 		for (auto function: functions) | 		for (auto function: functions) | ||||||
| 			enqueueFunctionForCodeGeneration(*function); | 			enqueueFunctionForCodeGeneration(*function); | ||||||
| 
 | 
 | ||||||
| @ -149,16 +150,15 @@ InternalDispatchMap IRGenerationContext::consumeInternalDispatchMap() | |||||||
| void IRGenerationContext::addToInternalDispatch(FunctionDefinition const& _function) | void IRGenerationContext::addToInternalDispatch(FunctionDefinition const& _function) | ||||||
| { | { | ||||||
| 	FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal); | 	FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal); | ||||||
| 	solAssert(functionType, ""); | 	solAssert(functionType); | ||||||
| 
 | 
 | ||||||
| 	YulArity arity = YulArity::fromType(*functionType); | 	YulArity arity = YulArity::fromType(*functionType); | ||||||
| 
 | 	DispatchQueue& dispatchQueue = m_internalDispatchMap[arity]; | ||||||
| 	if (m_internalDispatchMap.count(arity) != 0 && m_internalDispatchMap[arity].count(&_function) != 0) | 	if (ranges::find(dispatchQueue, &_function) == ranges::end(dispatchQueue)) | ||||||
| 		// Note that m_internalDispatchMap[arity] is a set with a custom comparator, which looks at function IDs not definitions
 | 	{ | ||||||
| 		solAssert(*m_internalDispatchMap[arity].find(&_function) == &_function, "Different definitions with the same function ID"); | 		dispatchQueue.push_back(&_function); | ||||||
| 
 | 		enqueueFunctionForCodeGeneration(_function); | ||||||
| 	m_internalDispatchMap[arity].insert(&_function); | 	} | ||||||
| 	enqueueFunctionForCodeGeneration(_function); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,7 +37,6 @@ | |||||||
| #include <set> | #include <set> | ||||||
| #include <string> | #include <string> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <vector> |  | ||||||
| 
 | 
 | ||||||
| namespace solidity::frontend | namespace solidity::frontend | ||||||
| { | { | ||||||
| @ -45,20 +44,8 @@ namespace solidity::frontend | |||||||
| class YulUtilFunctions; | class YulUtilFunctions; | ||||||
| class ABIFunctions; | class ABIFunctions; | ||||||
| 
 | 
 | ||||||
| struct AscendingFunctionIDCompare | using DispatchQueue = std::deque<FunctionDefinition const*>; | ||||||
| { | using InternalDispatchMap = std::map<YulArity, DispatchQueue>; | ||||||
| 	bool operator()(FunctionDefinition const* _f1, FunctionDefinition const* _f2) const |  | ||||||
| 	{ |  | ||||||
| 		// NULLs always first.
 |  | ||||||
| 		if (_f1 != nullptr && _f2 != nullptr) |  | ||||||
| 			return _f1->id() < _f2->id(); |  | ||||||
| 		else |  | ||||||
| 			return _f1 == nullptr; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| using DispatchSet = std::set<FunctionDefinition const*, AscendingFunctionIDCompare>; |  | ||||||
| using InternalDispatchMap = std::map<YulArity, DispatchSet>; |  | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Class that contains contextual information during IR generation. |  * Class that contains contextual information during IR generation. | ||||||
| @ -197,10 +184,9 @@ private: | |||||||
| 	/// were discovered by the IR generator during AST traversal.
 | 	/// were discovered by the IR generator during AST traversal.
 | ||||||
| 	/// Note that the queue gets filled in a lazy way - new definitions can be added while the
 | 	/// Note that the queue gets filled in a lazy way - new definitions can be added while the
 | ||||||
| 	/// collected ones get removed and traversed.
 | 	/// collected ones get removed and traversed.
 | ||||||
| 	/// The order and duplicates are irrelevant here (hence std::set rather than std::queue) as
 | 	/// The order and duplicates are relevant here
 | ||||||
| 	/// long as the order of Yul functions in the generated code is deterministic and the same on
 | 	/// (see: IRGenerationContext::[enqueue|dequeue]FunctionForCodeGeneration)
 | ||||||
| 	/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
 | 	DispatchQueue m_functionGenerationQueue; | ||||||
| 	DispatchSet m_functionGenerationQueue; |  | ||||||
| 
 | 
 | ||||||
| 	/// Collection of functions that need to be callable via internal dispatch.
 | 	/// Collection of functions that need to be callable via internal dispatch.
 | ||||||
| 	/// Note that having a key with an empty set of functions is a valid situation. It means that
 | 	/// Note that having a key with an empty set of functions is a valid situation. It means that
 | ||||||
|  | |||||||
| @ -279,6 +279,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin | |||||||
| 			templ("out", suffixedVariableNameList("out_", 0, arity.out)); | 			templ("out", suffixedVariableNameList("out_", 0, arity.out)); | ||||||
| 
 | 
 | ||||||
| 			std::vector<std::map<std::string, std::string>> cases; | 			std::vector<std::map<std::string, std::string>> cases; | ||||||
|  | 			std::set<int64_t> caseValues; | ||||||
| 			for (FunctionDefinition const* function: internalDispatchMap.at(arity)) | 			for (FunctionDefinition const* function: internalDispatchMap.at(arity)) | ||||||
| 			{ | 			{ | ||||||
| 				solAssert(function, ""); | 				solAssert(function, ""); | ||||||
| @ -289,12 +290,14 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin | |||||||
| 				solAssert(!function->isConstructor(), ""); | 				solAssert(!function->isConstructor(), ""); | ||||||
| 				// 0 is reserved for uninitialized function pointers
 | 				// 0 is reserved for uninitialized function pointers
 | ||||||
| 				solAssert(function->id() != 0, "Unexpected function ID: 0"); | 				solAssert(function->id() != 0, "Unexpected function ID: 0"); | ||||||
|  | 				solAssert(caseValues.count(function->id()) == 0, "Duplicate function ID"); | ||||||
| 				solAssert(m_context.functionCollector().contains(IRNames::function(*function)), ""); | 				solAssert(m_context.functionCollector().contains(IRNames::function(*function)), ""); | ||||||
| 
 | 
 | ||||||
| 				cases.emplace_back(std::map<std::string, std::string>{ | 				cases.emplace_back(std::map<std::string, std::string>{ | ||||||
| 					{"funID", std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(function))}, | 					{"funID", std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(function))}, | ||||||
| 					{"name", IRNames::function(*function)} | 					{"name", IRNames::function(*function)} | ||||||
| 				}); | 				}); | ||||||
|  | 				caseValues.insert(function->id()); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			templ("cases", std::move(cases)); | 			templ("cases", std::move(cases)); | ||||||
|  | |||||||
| @ -63,6 +63,6 @@ contract C { | |||||||
| // gas legacy: 411269 | // gas legacy: 411269 | ||||||
| // gas legacyOptimized: 317754 | // gas legacyOptimized: 317754 | ||||||
| // test_uint256() -> | // test_uint256() -> | ||||||
| // gas irOptimized: 509677 | // gas irOptimized: 509479 | ||||||
| // gas legacy: 577469 | // gas legacy: 577469 | ||||||
| // gas legacyOptimized: 441003 | // gas legacyOptimized: 441003 | ||||||
|  | |||||||
| @ -64,6 +64,6 @@ contract C { | |||||||
| // gas legacy: 411269 | // gas legacy: 411269 | ||||||
| // gas legacyOptimized: 317754 | // gas legacyOptimized: 317754 | ||||||
| // test_uint256() -> | // test_uint256() -> | ||||||
| // gas irOptimized: 509677 | // gas irOptimized: 509479 | ||||||
| // gas legacy: 577469 | // gas legacy: 577469 | ||||||
| // gas legacyOptimized: 441003 | // gas legacyOptimized: 441003 | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ contract test { | |||||||
| // EVMVersion: >=constantinople | // EVMVersion: >=constantinople | ||||||
| // ---- | // ---- | ||||||
| // constructor() | // constructor() | ||||||
| // gas irOptimized: 406679 | // gas irOptimized: 406691 | ||||||
| // gas legacy: 737652 | // gas legacy: 737652 | ||||||
| // gas legacyOptimized: 527036 | // gas legacyOptimized: 527036 | ||||||
| // encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0 | // encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0 | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ contract test { | |||||||
| } | } | ||||||
| // ---- | // ---- | ||||||
| // constructor() | // constructor() | ||||||
| // gas irOptimized: 1722598 | // gas irOptimized: 1722610 | ||||||
| // gas legacy: 2210160 | // gas legacy: 2210160 | ||||||
| // gas legacyOptimized: 1734152 | // gas legacyOptimized: 1734152 | ||||||
| // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 | // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ contract test { | |||||||
| } | } | ||||||
| // ---- | // ---- | ||||||
| // constructor() | // constructor() | ||||||
| // gas irOptimized: 634316 | // gas irOptimized: 634328 | ||||||
| // gas legacy: 1065857 | // gas legacy: 1065857 | ||||||
| // gas legacyOptimized: 725423 | // gas legacyOptimized: 725423 | ||||||
| // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 | // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 | ||||||
|  | |||||||
| @ -0,0 +1,9 @@ | |||||||
|  | contract C { | ||||||
|  |     function a() internal {} | ||||||
|  |     function f() public { | ||||||
|  |         function() ptr1 = a; | ||||||
|  |         function() ptr2 = a; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // f() | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user