mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #13376 from ethereum/make-hardcoded-parts-of-optimizer-sequence-configurable
This commit is contained in:
		
						commit
						ea78c8fd31
					
				| @ -4,6 +4,7 @@ Language Features: | ||||
| 
 | ||||
| 
 | ||||
| Compiler Features: | ||||
| * Yul Optimizer: Allow replacing the previously hard-coded cleanup sequence by specifying custom steps after a colon delimiter (``:``) in the sequence string. | ||||
| 
 | ||||
| 
 | ||||
| Bugfixes: | ||||
|  | ||||
| @ -281,60 +281,85 @@ The following transformation steps are the main components: | ||||
| - Redundant Assign Eliminator | ||||
| - Full Inliner | ||||
| 
 | ||||
| .. _optimizer-steps: | ||||
| 
 | ||||
| Optimizer Steps | ||||
| --------------- | ||||
| 
 | ||||
| This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information | ||||
| on the individual steps and their sequence below. | ||||
| 
 | ||||
| - :ref:`block-flattener`. | ||||
| - :ref:`circular-reference-pruner`. | ||||
| - :ref:`common-subexpression-eliminator`. | ||||
| - :ref:`conditional-simplifier`. | ||||
| - :ref:`conditional-unsimplifier`. | ||||
| - :ref:`control-flow-simplifier`. | ||||
| - :ref:`dead-code-eliminator`. | ||||
| - :ref:`equal-store-eliminator`. | ||||
| - :ref:`equivalent-function-combiner`. | ||||
| - :ref:`expression-joiner`. | ||||
| - :ref:`expression-simplifier`. | ||||
| - :ref:`expression-splitter`. | ||||
| - :ref:`for-loop-condition-into-body`. | ||||
| - :ref:`for-loop-condition-out-of-body`. | ||||
| - :ref:`for-loop-init-rewriter`. | ||||
| - :ref:`expression-inliner`. | ||||
| - :ref:`full-inliner`. | ||||
| - :ref:`function-grouper`. | ||||
| - :ref:`function-hoister`. | ||||
| - :ref:`function-specializer`. | ||||
| - :ref:`literal-rematerialiser`. | ||||
| - :ref:`load-resolver`. | ||||
| - :ref:`loop-invariant-code-motion`. | ||||
| - :ref:`redundant-assign-eliminator`. | ||||
| - :ref:`reasoning-based-simplifier`. | ||||
| - :ref:`rematerialiser`. | ||||
| - :ref:`SSA-reverser`. | ||||
| - :ref:`SSA-transform`. | ||||
| - :ref:`structural-simplifier`. | ||||
| - :ref:`unused-function-parameter-pruner`. | ||||
| - :ref:`unused-pruner`. | ||||
| - :ref:`var-decl-initializer`. | ||||
| ============ =============================== | ||||
| Abbreviation Full name | ||||
| ============ =============================== | ||||
| ``f``        :ref:`block-flattener` | ||||
| ``l``        :ref:`circular-reference-pruner` | ||||
| ``c``        :ref:`common-subexpression-eliminator` | ||||
| ``C``        :ref:`conditional-simplifier` | ||||
| ``U``        :ref:`conditional-unsimplifier` | ||||
| ``n``        :ref:`control-flow-simplifier` | ||||
| ``D``        :ref:`dead-code-eliminator` | ||||
| ``E``        :ref:`equal-store-eliminator` | ||||
| ``v``        :ref:`equivalent-function-combiner` | ||||
| ``e``        :ref:`expression-inliner` | ||||
| ``j``        :ref:`expression-joiner` | ||||
| ``s``        :ref:`expression-simplifier` | ||||
| ``x``        :ref:`expression-splitter` | ||||
| ``I``        :ref:`for-loop-condition-into-body` | ||||
| ``O``        :ref:`for-loop-condition-out-of-body` | ||||
| ``o``        :ref:`for-loop-init-rewriter` | ||||
| ``i``        :ref:`full-inliner` | ||||
| ``g``        :ref:`function-grouper` | ||||
| ``h``        :ref:`function-hoister` | ||||
| ``F``        :ref:`function-specializer` | ||||
| ``T``        :ref:`literal-rematerialiser` | ||||
| ``L``        :ref:`load-resolver` | ||||
| ``M``        :ref:`loop-invariant-code-motion` | ||||
| ``r``        :ref:`redundant-assign-eliminator` | ||||
| ``R``        :ref:`reasoning-based-simplifier` - highly experimental | ||||
| ``m``        :ref:`rematerialiser` | ||||
| ``V``        :ref:`SSA-reverser` | ||||
| ``a``        :ref:`SSA-transform` | ||||
| ``t``        :ref:`structural-simplifier` | ||||
| ``p``        :ref:`unused-function-parameter-pruner` | ||||
| ``u``        :ref:`unused-pruner` | ||||
| ``d``        :ref:`var-decl-initializer` | ||||
| ============ =============================== | ||||
| 
 | ||||
| Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. | ||||
| For this reason the Yul optimizer always applies them before applying any steps supplied by the user. | ||||
| 
 | ||||
| The ReasoningBasedSimplifier is an optimizer step that is currently not enabled | ||||
| in the default set of steps. It uses an SMT solver to simplify arithmetic expressions | ||||
| and boolean conditions. It has not received thorough testing or validation yet and can produce | ||||
| non-reproducible results, so please use with care! | ||||
| 
 | ||||
| Selecting Optimizations | ||||
| ----------------------- | ||||
| 
 | ||||
| By default the optimizer applies its predefined sequence of optimization steps to | ||||
| the generated assembly. You can override this sequence and supply your own using | ||||
| the ``--yul-optimizations`` option: | ||||
| By default the optimizer applies its predefined sequence of optimization steps to the generated assembly. | ||||
| You can override this sequence and supply your own using the ``--yul-optimizations`` option: | ||||
| 
 | ||||
| .. code-block:: bash | ||||
| 
 | ||||
|     solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' | ||||
|     solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul:fDnTOc' | ||||
| 
 | ||||
| The order of steps is significant and affects the quality of the output. | ||||
| Moreover, applying a step may uncover new optimization opportunities for others that were already applied, | ||||
| so repeating steps is often beneficial. | ||||
| 
 | ||||
| The sequence inside ``[...]`` will be applied multiple times in a loop until the Yul code | ||||
| remains unchanged or until the maximum number of rounds (currently 12) has been reached. | ||||
| Brackets (``[]``) may be used multiple times in a sequence, but can not be nested. | ||||
| 
 | ||||
| Available abbreviations are listed in the :ref:`Yul optimizer docs <optimization-step-sequence>`. | ||||
| An important thing to note, is that there are some hardcoded steps that are always run before and after the | ||||
| user-supplied sequence, or the default sequence if one was not supplied by the user. | ||||
| 
 | ||||
| The cleanup sequence delimiter ``:`` is optional, and is used to supply a custom cleanup sequence | ||||
| in order to replace the default one. If omitted, the optimizer will simply apply the default cleanup | ||||
| sequence. In addition, the delimiter may be placed at the beginning of the user-supplied sequence, | ||||
| which will result in the optimization sequence being empty, whereas conversely, if placed at the end of | ||||
| the sequence, will be treated as an empty cleanup sequence. | ||||
| 
 | ||||
| Preprocessing | ||||
| ------------- | ||||
|  | ||||
							
								
								
									
										61
									
								
								docs/yul.rst
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								docs/yul.rst
									
									
									
									
									
								
							| @ -1245,65 +1245,8 @@ In Solidity mode, the Yul optimizer is activated together with the regular optim | ||||
| Optimization Step Sequence | ||||
| -------------------------- | ||||
| 
 | ||||
| By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly. | ||||
| You can override this sequence and supply your own using the ``--yul-optimizations`` option: | ||||
| 
 | ||||
| .. code-block:: sh | ||||
| 
 | ||||
|     solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' | ||||
| 
 | ||||
| The order of steps is significant and affects the quality of the output. | ||||
| Moreover, applying a step may uncover new optimization opportunities for others that were already | ||||
| applied so repeating steps is often beneficial. | ||||
| By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly | ||||
| apply that part until it no longer improves the size of the resulting assembly. | ||||
| You can use brackets multiple times in a single sequence but they cannot be nested. | ||||
| 
 | ||||
| The following optimization steps are available: | ||||
| 
 | ||||
| ============ =============================== | ||||
| Abbreviation Full name | ||||
| ============ =============================== | ||||
| ``f``        ``BlockFlattener`` | ||||
| ``l``        ``CircularReferencesPruner`` | ||||
| ``c``        ``CommonSubexpressionEliminator`` | ||||
| ``C``        ``ConditionalSimplifier`` | ||||
| ``U``        ``ConditionalUnsimplifier`` | ||||
| ``n``        ``ControlFlowSimplifier`` | ||||
| ``D``        ``DeadCodeEliminator`` | ||||
| ``v``        ``EquivalentFunctionCombiner`` | ||||
| ``e``        ``ExpressionInliner`` | ||||
| ``j``        ``ExpressionJoiner`` | ||||
| ``s``        ``ExpressionSimplifier`` | ||||
| ``x``        ``ExpressionSplitter`` | ||||
| ``I``        ``ForLoopConditionIntoBody`` | ||||
| ``O``        ``ForLoopConditionOutOfBody`` | ||||
| ``o``        ``ForLoopInitRewriter`` | ||||
| ``i``        ``FullInliner`` | ||||
| ``g``        ``FunctionGrouper`` | ||||
| ``h``        ``FunctionHoister`` | ||||
| ``F``        ``FunctionSpecializer`` | ||||
| ``T``        ``LiteralRematerialiser`` | ||||
| ``L``        ``LoadResolver`` | ||||
| ``M``        ``LoopInvariantCodeMotion`` | ||||
| ``r``        ``RedundantAssignEliminator`` | ||||
| ``R``        ``ReasoningBasedSimplifier`` - highly experimental | ||||
| ``m``        ``Rematerialiser`` | ||||
| ``V``        ``SSAReverser`` | ||||
| ``a``        ``SSATransform`` | ||||
| ``t``        ``StructuralSimplifier`` | ||||
| ``u``        ``UnusedPruner`` | ||||
| ``p``        ``UnusedFunctionParameterPruner`` | ||||
| ``d``        ``VarDeclInitializer`` | ||||
| ============ =============================== | ||||
| 
 | ||||
| Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. | ||||
| For this reason the Yul optimizer always applies them before applying any steps supplied by the user. | ||||
| 
 | ||||
| The ReasoningBasedSimplifier is an optimizer step that is currently not enabled | ||||
| in the default set of steps. It uses an SMT solver to simplify arithmetic expressions | ||||
| and boolean conditions. It has not received thorough testing or validation yet and can produce | ||||
| non-reproducible results, so please use with care! | ||||
| Detailed information regrading the optimization sequence as well a list of abbreviations is | ||||
| available in the :ref:`optimizer docs <optimizer-steps>`. | ||||
| 
 | ||||
| .. _erc20yul: | ||||
| 
 | ||||
|  | ||||
| @ -547,6 +547,7 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ | ||||
| 		_object, | ||||
| 		_optimiserSettings.optimizeStackAllocation, | ||||
| 		_optimiserSettings.yulOptimiserSteps, | ||||
| 		_optimiserSettings.yulOptimiserCleanupSteps, | ||||
| 		isCreation? nullopt : make_optional(_optimiserSettings.expectedExecutionsPerDeployment), | ||||
| 		_externalIdentifiers | ||||
| 	); | ||||
|  | ||||
| @ -1539,7 +1539,7 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con | ||||
| 		{ | ||||
| 			details["yulDetails"] = Json::objectValue; | ||||
| 			details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; | ||||
| 			details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps; | ||||
| 			details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps; | ||||
| 		} | ||||
| 
 | ||||
| 		meta["settings"]["optimizer"]["details"] = std::move(details); | ||||
|  | ||||
| @ -59,6 +59,8 @@ struct OptimiserSettings | ||||
| 		"]" | ||||
| 		"jmul[jul] VcTOcul jmul";      // Make source short and pretty
 | ||||
| 
 | ||||
| 	static char constexpr DefaultYulOptimiserCleanupSteps[] = "fDnTOc"; | ||||
| 
 | ||||
| 	/// No optimisations at all - not recommended.
 | ||||
| 	static OptimiserSettings none() | ||||
| 	{ | ||||
| @ -146,6 +148,10 @@ struct OptimiserSettings | ||||
| 	/// them just by setting this to an empty string. Set @a runYulOptimiser to false if you want
 | ||||
| 	/// no optimisations.
 | ||||
| 	std::string yulOptimiserSteps = DefaultYulOptimiserSteps; | ||||
| 	/// Sequence of clean-up optimisation steps after yulOptimiserSteps is run. Note that if the string
 | ||||
| 	/// is left empty, there will still be hard-coded optimisation steps that will run regardless.
 | ||||
| 	/// Set @a runYulOptimiser to false if you want no optimisations.
 | ||||
| 	std::string yulOptimiserCleanupSteps = DefaultYulOptimiserCleanupSteps; | ||||
| 	/// This specifies 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 gas usage.
 | ||||
| 	size_t expectedExecutionsPerDeployment = 200; | ||||
|  | ||||
| @ -472,7 +472,7 @@ std::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std | ||||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _setting) | ||||
| std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, string& _optimiserSetting, string& _cleanupSetting) | ||||
| { | ||||
| 	if (_details.isMember(_name)) | ||||
| 	{ | ||||
| @ -490,7 +490,14 @@ std::optional<Json::Value> checkOptimizerDetailSteps(Json::Value const& _details | ||||
| 				); | ||||
| 			} | ||||
| 
 | ||||
| 			_setting = _details[_name].asString(); | ||||
| 			string const fullSequence = _details[_name].asString(); | ||||
| 			auto const delimiterPos = fullSequence.find(":"); | ||||
| 			_optimiserSetting = fullSequence.substr(0, delimiterPos); | ||||
| 
 | ||||
| 			if (delimiterPos != string::npos) | ||||
| 				_cleanupSetting = fullSequence.substr(delimiterPos + 1); | ||||
| 			else | ||||
| 				solAssert(_cleanupSetting == OptimiserSettings::DefaultYulOptimiserCleanupSteps); | ||||
| 		} | ||||
| 		else | ||||
| 			return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be a string"); | ||||
| @ -616,7 +623,7 @@ std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value | ||||
| 				return *result; | ||||
| 			if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) | ||||
| 				return *error; | ||||
| 			if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps)) | ||||
| 			if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps)) | ||||
| 				return *error; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -209,6 +209,7 @@ void YulStack::optimize(Object& _object, bool _isCreation) | ||||
| 		_object, | ||||
| 		m_optimiserSettings.optimizeStackAllocation, | ||||
| 		m_optimiserSettings.yulOptimiserSteps, | ||||
| 		m_optimiserSettings.yulOptimiserCleanupSteps, | ||||
| 		_isCreation ? nullopt : make_optional(m_optimiserSettings.expectedExecutionsPerDeployment), | ||||
| 		{} | ||||
| 	); | ||||
|  | ||||
| @ -93,6 +93,7 @@ void OptimiserSuite::run( | ||||
| 	Object& _object, | ||||
| 	bool _optimizeStackAllocation, | ||||
| 	string_view _optimisationSequence, | ||||
| 	string_view _optimisationCleanupSequence, | ||||
| 	optional<size_t> _expectedExecutionsPerDeployment, | ||||
| 	set<YulString> const& _externallyUsedIdentifiers | ||||
| ) | ||||
| @ -139,7 +140,13 @@ void OptimiserSuite::run( | ||||
| 			_optimizeStackAllocation, | ||||
| 			stackCompressorMaxIterations | ||||
| 		); | ||||
| 	suite.runSequence("fDnTOc g", ast); | ||||
| 
 | ||||
| 	// Run the user-supplied clean up sequence
 | ||||
| 	suite.runSequence(_optimisationCleanupSequence, ast); | ||||
| 	// Hard-coded FunctionGrouper step is used to bring the AST into a canonical form required by the StackCompressor
 | ||||
| 	// and StackLimitEvader. This is hard-coded as the last step, as some previously executed steps may break the
 | ||||
| 	// aforementioned form, thus causing the StackCompressor/StackLimitEvader to throw.
 | ||||
| 	suite.runSequence("g", ast); | ||||
| 
 | ||||
| 	if (evmDialect) | ||||
| 	{ | ||||
| @ -296,6 +303,7 @@ map<char, string> const& OptimiserSuite::stepAbbreviationToNameMap() | ||||
| void OptimiserSuite::validateSequence(string_view _stepAbbreviations) | ||||
| { | ||||
| 	int8_t nestingLevel = 0; | ||||
| 	int8_t colonDelimiters = 0; | ||||
| 	for (char abbreviation: _stepAbbreviations) | ||||
| 		switch (abbreviation) | ||||
| 		{ | ||||
| @ -310,6 +318,11 @@ void OptimiserSuite::validateSequence(string_view _stepAbbreviations) | ||||
| 			nestingLevel--; | ||||
| 			assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets"); | ||||
| 			break; | ||||
| 		case ':': | ||||
| 			++colonDelimiters; | ||||
| 			assertThrow(nestingLevel == 0, OptimizerException, "Cleanup sequence delimiter cannot be placed inside the brackets"); | ||||
| 			assertThrow(colonDelimiters <= 1, OptimizerException, "Too many cleanup sequence delimiters"); | ||||
| 			break; | ||||
| 		default: | ||||
| 		{ | ||||
| 			yulAssert( | ||||
|  | ||||
| @ -51,7 +51,7 @@ public: | ||||
| 
 | ||||
| 	/// Special characters that do not represent optimiser steps but are allowed in abbreviation sequences.
 | ||||
| 	/// Some of them (like whitespace) are ignored, others (like brackets) are a part of the syntax.
 | ||||
| 	static constexpr char NonStepAbbreviations[] = " \n[]"; | ||||
| 	static constexpr char NonStepAbbreviations[] = " \n[]:"; | ||||
| 
 | ||||
| 	enum class Debug | ||||
| 	{ | ||||
| @ -68,6 +68,7 @@ public: | ||||
| 		Object& _object, | ||||
| 		bool _optimizeStackAllocation, | ||||
| 		std::string_view _optimisationSequence, | ||||
| 		std::string_view _optimisationCleanupSequence, | ||||
| 		std::optional<size_t> _expectedExecutionsPerDeployment, | ||||
| 		std::set<YulString> const& _externallyUsedIdentifiers = {} | ||||
| 	); | ||||
|  | ||||
| @ -266,7 +266,16 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const | ||||
| 		settings.expectedExecutionsPerDeployment = optimizer.expectedExecutionsPerDeployment.value(); | ||||
| 
 | ||||
| 	if (optimizer.yulSteps.has_value()) | ||||
| 		settings.yulOptimiserSteps = optimizer.yulSteps.value(); | ||||
| 	{ | ||||
| 		string const fullSequence = optimizer.yulSteps.value(); | ||||
| 		auto const delimiterPos = fullSequence.find(":"); | ||||
| 		settings.yulOptimiserSteps = fullSequence.substr(0, delimiterPos); | ||||
| 
 | ||||
| 		if (delimiterPos != string::npos) | ||||
| 			settings.yulOptimiserCleanupSteps = fullSequence.substr(delimiterPos + 1); | ||||
| 		else | ||||
| 			solAssert(settings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps); | ||||
| 	} | ||||
| 
 | ||||
| 	return settings; | ||||
| } | ||||
|  | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_invalid_nested_delimiter/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": "dhfoDgvulfnTUtnIf\n[    xar:rscLM\n]\njmuljuljul VcTOcul jmul" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| { | ||||
|     "errors": | ||||
|     [ | ||||
|         { | ||||
|             "component": "general", | ||||
|             "formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup sequence delimiter cannot be placed inside the brackets", | ||||
|             "message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Cleanup sequence delimiter cannot be placed inside the brackets", | ||||
|             "severity": "error", | ||||
|             "type": "JSONError" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_mutliple_delimiters/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": "dhfoDgvu:lfnTUtnIf\n[    xarrscLM\n]\njmuljulj:ul VcTOcul jmul" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| { | ||||
|     "errors": | ||||
|     [ | ||||
|         { | ||||
|             "component": "general", | ||||
|             "formattedMessage": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many cleanup sequence delimiters", | ||||
|             "message": "Invalid optimizer step sequence in \"settings.optimizer.details.optimizerSteps\": Too many cleanup sequence delimiters", | ||||
|             "severity": "error", | ||||
|             "type": "JSONError" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": "dhfoDgvulfnTUtnIf\n[    xarrscLM\n]\njmuljuljul VcTOcul jmul:fDnTOc" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| { | ||||
|     "sources": | ||||
|     { | ||||
|         "A": | ||||
|         { | ||||
|             "id": 0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": "dhfoDgvulfnTUtnIf\n[    xarrscLM\n]\njmuljuljul VcTOcul jmul:" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| { | ||||
|     "sources": | ||||
|     { | ||||
|         "A": | ||||
|         { | ||||
|             "id": 0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": ":dhfoDgvulfnTUtnIf\n[    xarrscLM\n]\njmuljuljul VcTOcul jmul" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| { | ||||
|     "sources": | ||||
|     { | ||||
|         "A": | ||||
|         { | ||||
|             "id": 0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --pretty-json --json-indent 4 | ||||
| @ -0,0 +1,6 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| 
 | ||||
| contract C { | ||||
|     function f() public pure {} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     "language": "Solidity", | ||||
|     "sources": { | ||||
|         "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} | ||||
|     }, | ||||
|     "settings": { | ||||
|         "optimizer": { | ||||
|             "details": { | ||||
|                 "yul": true, | ||||
|                 "yulDetails": { | ||||
|                     "optimizerSteps": ":" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| { | ||||
|     "sources": | ||||
|     { | ||||
|         "A": | ||||
|         { | ||||
|             "id": 0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --ir-optimized --optimize --yul-optimizations iDu | ||||
| @ -0,0 +1,12 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| pragma abicoder v2; | ||||
| 
 | ||||
| contract C { | ||||
|     constructor() payable { | ||||
|         assembly ("memory-safe") { | ||||
|             let a := 0 | ||||
|             revert(0, a) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										24
									
								
								test/cmdlineTests/yul_optimizer_steps_short_sequence/output
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								test/cmdlineTests/yul_optimizer_steps_short_sequence/output
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| Optimized IR: | ||||
| /// @use-src 0:"yul_optimizer_steps_short_sequence/input.sol" | ||||
| object "C_8" { | ||||
|     code { | ||||
|         { | ||||
|             /// @src 0:80:221  "contract C {..." | ||||
|             mstore(64, memoryguard(0x80)) | ||||
|             /// @src 0:129:213  "assembly (\"memory-safe\") {..." | ||||
|             let usr$a := 0 | ||||
|             revert(usr$a, usr$a) | ||||
|         } | ||||
|     } | ||||
|     /// @use-src 0:"yul_optimizer_steps_short_sequence/input.sol" | ||||
|     object "C_8_deployed" { | ||||
|         code { | ||||
|             { | ||||
|                 /// @src 0:80:221  "contract C {..." | ||||
|                 mstore(64, memoryguard(0x80)) | ||||
|                 revert(0, 0) | ||||
|             } | ||||
|         } | ||||
|         data ".metadata" hex"<BYTECODE REMOVED>" | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --ir-optimized --optimize --yul-optimizations iDu: | ||||
| @ -0,0 +1,13 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| pragma abicoder v2; | ||||
| 
 | ||||
| contract C { | ||||
|     constructor() payable { | ||||
|         assembly ("memory-safe") { | ||||
|             let a := 0 | ||||
|             // Without the cleanup sequence this will not be simplified to ``revert(a, a)``. | ||||
|             revert(0, a) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| Optimized IR: | ||||
| /// @use-src 0:"yul_optimizer_steps_with_empty_cleanup_sequence/input.sol" | ||||
| object "C_8" { | ||||
|     code { | ||||
|         { | ||||
|             /// @src 0:80:314  "contract C {..." | ||||
|             mstore(64, memoryguard(0x80)) | ||||
|             /// @src 0:129:306  "assembly (\"memory-safe\") {..." | ||||
|             let usr$a := 0 | ||||
|             revert(0, usr$a) | ||||
|         } | ||||
|     } | ||||
|     /// @use-src 0:"yul_optimizer_steps_with_empty_cleanup_sequence/input.sol" | ||||
|     object "C_8_deployed" { | ||||
|         code { | ||||
|             { | ||||
|                 /// @src 0:80:314  "contract C {..." | ||||
|                 mstore(64, memoryguard(0x80)) | ||||
|                 revert(0, 0) | ||||
|             } | ||||
|         } | ||||
|         data ".metadata" hex"<BYTECODE REMOVED>" | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1 @@ | ||||
| --ir-optimized --optimize --yul-optimizations :iDu | ||||
| @ -0,0 +1,13 @@ | ||||
| // SPDX-License-Identifier: GPL-3.0 | ||||
| pragma solidity >=0.0; | ||||
| pragma abicoder v2; | ||||
| 
 | ||||
| contract C { | ||||
|     constructor() payable { | ||||
|         assembly ("memory-safe") { | ||||
|             let a := 0 | ||||
|             // Without the cleanup sequence this will not be simplified to ``revert(a, a)``. | ||||
|             revert(0, a) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| Optimized IR: | ||||
| /// @use-src 0:"yul_optimizer_steps_with_empty_optimization_sequence/input.sol" | ||||
| object "C_8" { | ||||
|     code { | ||||
|         { | ||||
|             /// @src 0:80:314  "contract C {..." | ||||
|             mstore(64, memoryguard(0x80)) | ||||
|             /// @src 0:129:306  "assembly (\"memory-safe\") {..." | ||||
|             let usr$a := 0 | ||||
|             revert(0, usr$a) | ||||
|         } | ||||
|     } | ||||
|     /// @use-src 0:"yul_optimizer_steps_with_empty_optimization_sequence/input.sol" | ||||
|     object "C_8_deployed" { | ||||
|         code { | ||||
|             { | ||||
|                 /// @src 0:80:314  "contract C {..." | ||||
|                 mstore(64, memoryguard(0x80)) | ||||
|                 revert(0, 0) | ||||
|             } | ||||
|         } | ||||
|         data ".metadata" hex"<BYTECODE REMOVED>" | ||||
|     } | ||||
| } | ||||
| @ -400,6 +400,53 @@ BOOST_AUTO_TEST_CASE(metadata_revert_strings) | ||||
| 	BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip"); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(metadata_optimiser_sequence) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
| 		pragma solidity >=0.0; | ||||
| 		contract C { | ||||
| 		} | ||||
| 	)"; | ||||
| 
 | ||||
| 	vector<tuple<string, string>> sequences = | ||||
| 	{ | ||||
| 		// {"<optimizer sequence>", "<optimizer cleanup sequence>"}
 | ||||
| 		{"", ""}, | ||||
| 		{"", "fDn"}, | ||||
| 		{"dhfoDgvulfnTUtnIf", "" }, | ||||
| 		{"dhfoDgvulfnTUtnIf", "fDn"} | ||||
| 	}; | ||||
| 
 | ||||
| 	auto check = [sourceCode](string const& _optimizerSequence, string const& _optimizerCleanupSequence) | ||||
| 	{ | ||||
| 		OptimiserSettings optimizerSettings = OptimiserSettings::minimal(); | ||||
| 		optimizerSettings.runYulOptimiser = true; | ||||
| 		optimizerSettings.yulOptimiserSteps = _optimizerSequence; | ||||
| 		optimizerSettings.yulOptimiserCleanupSteps = _optimizerCleanupSequence; | ||||
| 		CompilerStack compilerStack; | ||||
| 		compilerStack.setSources({{"", std::string(sourceCode)}}); | ||||
| 		compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); | ||||
| 		compilerStack.setOptimiserSettings(optimizerSettings); | ||||
| 
 | ||||
| 		BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); | ||||
| 
 | ||||
| 		std::string const& serialisedMetadata = compilerStack.metadata("C"); | ||||
| 		Json::Value metadata; | ||||
| 		BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); | ||||
| 		BOOST_CHECK(solidity::test::isValidMetadata(metadata)); | ||||
| 		BOOST_CHECK(metadata["settings"]["optimizer"].isMember("details")); | ||||
| 		BOOST_CHECK(metadata["settings"]["optimizer"]["details"].isMember("yulDetails")); | ||||
| 		BOOST_CHECK(metadata["settings"]["optimizer"]["details"]["yulDetails"].isMember("optimizerSteps")); | ||||
| 
 | ||||
| 		string const metadataOptimizerSteps = metadata["settings"]["optimizer"]["details"]["yulDetails"]["optimizerSteps"].asString(); | ||||
| 		string const expectedMetadataOptimiserSteps = _optimizerSequence + ":" + _optimizerCleanupSequence; | ||||
| 		BOOST_CHECK_EQUAL(metadataOptimizerSteps, expectedMetadataOptimiserSteps); | ||||
| 	}; | ||||
| 
 | ||||
| 	for (auto const& [sequence, cleanupSequence] : sequences) | ||||
| 		check(sequence, cleanupSequence); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(metadata_license_missing) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
|  | ||||
| @ -1247,7 +1247,10 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different) | ||||
| 		(set<string>{"stackAllocation", "optimizerSteps"}) | ||||
| 	); | ||||
| 	BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true); | ||||
| 	BOOST_CHECK(optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == OptimiserSettings::DefaultYulOptimiserSteps); | ||||
| 	BOOST_CHECK( | ||||
| 		optimizer["details"]["yulDetails"]["optimizerSteps"].asString() == | ||||
| 		OptimiserSettings::DefaultYulOptimiserSteps + ":"s + OptimiserSettings::DefaultYulOptimiserCleanupSteps | ||||
|  	); | ||||
| 	BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 9); | ||||
| 	BOOST_CHECK(optimizer["runs"].asUInt() == 600); | ||||
| } | ||||
|  | ||||
| @ -339,6 +339,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( | ||||
| 				*m_object, | ||||
| 				true, | ||||
| 				frontend::OptimiserSettings::DefaultYulOptimiserSteps, | ||||
| 				frontend::OptimiserSettings::DefaultYulOptimiserCleanupSteps, | ||||
| 				frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment | ||||
| 			); | ||||
| 		}}, | ||||
|  | ||||
| @ -426,6 +426,58 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) | ||||
| 		} | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(default_optimiser_sequence) | ||||
| { | ||||
| 	CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--optimize"}); | ||||
| 	BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, OptimiserSettings::DefaultYulOptimiserSteps); | ||||
| 	BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, OptimiserSettings::DefaultYulOptimiserCleanupSteps); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(valid_optimiser_sequences) | ||||
| { | ||||
| 	vector<string> validSequenceInputs { | ||||
| 		":",                        // Empty optimization sequence and empty cleanup sequence
 | ||||
| 		":fDn",                     // Empty optimization sequence and specified cleanup sequence
 | ||||
| 		"dhfoDgvulfnTUtnIf:",       // Specified optimization sequence and empty cleanup sequence
 | ||||
| 		"dhfoDgvulfnTUtnIf:fDn",    // Specified optimization sequence and cleanup sequence
 | ||||
| 		"dhfo[Dgvulfn]TUtnIf:f[D]n" // Specified and nested optimization and cleanup sequence
 | ||||
| 	}; | ||||
| 
 | ||||
| 	vector<tuple<string, string>> expectedParsedSequences { | ||||
| 		{"", ""}, | ||||
| 		{"", "fDn"}, | ||||
| 		{"dhfoDgvulfnTUtnIf", ""}, | ||||
| 		{"dhfoDgvulfnTUtnIf", "fDn"}, | ||||
| 		{"dhfo[Dgvulfn]TUtnIf", "f[D]n"} | ||||
| 	}; | ||||
| 
 | ||||
| 	BOOST_CHECK_EQUAL(validSequenceInputs.size(), expectedParsedSequences.size()); | ||||
| 
 | ||||
| 	for (size_t i = 0; i < validSequenceInputs.size(); ++i) | ||||
| 	{ | ||||
| 		CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--optimize", "--yul-optimizations=" + validSequenceInputs[i]}); | ||||
| 		auto const& [expectedYulOptimiserSteps, expectedYulCleanupSteps] = expectedParsedSequences[i]; | ||||
| 		BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, expectedYulOptimiserSteps); | ||||
| 		BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, expectedYulCleanupSteps); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(invalid_nested_cleanup_sequence_delimiter) | ||||
| { | ||||
| 	vector<string> commandLine {"solc", "contract.sol", "--optimize", "--yul-optimizations=dhfoDgvulfnTUt[nIf:fd]N"}; | ||||
| 	string expectedMessage = "Invalid optimizer step sequence in --yul-optimizations: Cleanup sequence delimiter cannot be placed inside the brackets"; | ||||
| 	auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; }; | ||||
| 	BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(too_many_cleanup_sequence_delimiters) | ||||
| { | ||||
| 	vector<string> commandLine {"solc", "contract.sol", "--optimize", "--yul-optimizations=dhfoDgvulfnTU:tnIf:fdN"}; | ||||
| 	string expectedMessage = "Invalid optimizer step sequence in --yul-optimizations: Too many cleanup sequence delimiters"; | ||||
| 	auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; }; | ||||
| 	BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE_END() | ||||
| 
 | ||||
| } // namespace solidity::frontend::test
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user