mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #12091 from ethereum/applyControlFlowSideEffectsUserDefined
Use side effects of user-defined functions in other optimizer steps.
This commit is contained in:
		
						commit
						a7b137829f
					
				| @ -12,6 +12,7 @@ Compiler Features: | |||||||
|  * SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``. |  * SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``. | ||||||
|  * Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``. |  * Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``. | ||||||
|  * Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code. |  * Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code. | ||||||
|  |  * Yul Optimizer: Take control-flow side-effects of user-defined functions into account in various optimizer steps. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ | |||||||
| #include <libyul/optimiser/Semantics.h> | #include <libyul/optimiser/Semantics.h> | ||||||
| #include <libyul/AST.h> | #include <libyul/AST.h> | ||||||
| #include <libyul/optimiser/NameCollector.h> | #include <libyul/optimiser/NameCollector.h> | ||||||
|  | #include <libyul/ControlFlowSideEffectsCollector.h> | ||||||
| #include <libsolutil/CommonData.h> | #include <libsolutil/CommonData.h> | ||||||
| 
 | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| @ -26,6 +27,12 @@ using namespace solidity; | |||||||
| using namespace solidity::yul; | using namespace solidity::yul; | ||||||
| using namespace solidity::util; | using namespace solidity::util; | ||||||
| 
 | 
 | ||||||
|  | void ConditionalSimplifier::run(OptimiserStepContext& _context, Block& _ast) | ||||||
|  | { | ||||||
|  | 	ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast); | ||||||
|  | 	ConditionalSimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ConditionalSimplifier::operator()(Switch& _switch) | void ConditionalSimplifier::operator()(Switch& _switch) | ||||||
| { | { | ||||||
| 	visit(*_switch.expression); | 	visit(*_switch.expression); | ||||||
| @ -65,7 +72,7 @@ void ConditionalSimplifier::operator()(Block& _block) | |||||||
| 				if ( | 				if ( | ||||||
| 					holds_alternative<Identifier>(*_if.condition) && | 					holds_alternative<Identifier>(*_if.condition) && | ||||||
| 					!_if.body.statements.empty() && | 					!_if.body.statements.empty() && | ||||||
| 					TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != | 					TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) != | ||||||
| 						TerminationFinder::ControlFlow::FlowOut | 						TerminationFinder::ControlFlow::FlowOut | ||||||
| 				) | 				) | ||||||
| 				{ | 				{ | ||||||
|  | |||||||
| @ -44,7 +44,6 @@ namespace solidity::yul | |||||||
|  * |  * | ||||||
|  * Future features: |  * Future features: | ||||||
|  *  - allow replacements by "1" |  *  - allow replacements by "1" | ||||||
|  *  - take termination of user-defined functions into account |  | ||||||
|  * |  * | ||||||
|  * Works best with SSA form and if dead code removal has run before. |  * Works best with SSA form and if dead code removal has run before. | ||||||
|  * |  * | ||||||
| @ -54,20 +53,21 @@ class ConditionalSimplifier: public ASTModifier | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	static constexpr char const* name{"ConditionalSimplifier"}; | 	static constexpr char const* name{"ConditionalSimplifier"}; | ||||||
| 	static void run(OptimiserStepContext& _context, Block& _ast) | 	static void run(OptimiserStepContext& _context, Block& _ast); | ||||||
| 	{ |  | ||||||
| 		ConditionalSimplifier{_context.dialect}(_ast); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	using ASTModifier::operator(); | 	using ASTModifier::operator(); | ||||||
| 	void operator()(Switch& _switch) override; | 	void operator()(Switch& _switch) override; | ||||||
| 	void operator()(Block& _block) override; | 	void operator()(Block& _block) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	explicit ConditionalSimplifier(Dialect const& _dialect): | 	explicit ConditionalSimplifier( | ||||||
| 		m_dialect(_dialect) | 		Dialect const& _dialect, | ||||||
|  | 		std::map<YulString, ControlFlowSideEffects> const& _sideEffects | ||||||
|  | 	): | ||||||
|  | 		m_dialect(_dialect), m_functionSideEffects(_sideEffects) | ||||||
| 	{} | 	{} | ||||||
| 	Dialect const& m_dialect; | 	Dialect const& m_dialect; | ||||||
|  | 	std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ | |||||||
| #include <libyul/AST.h> | #include <libyul/AST.h> | ||||||
| #include <libyul/Utilities.h> | #include <libyul/Utilities.h> | ||||||
| #include <libyul/optimiser/NameCollector.h> | #include <libyul/optimiser/NameCollector.h> | ||||||
|  | #include <libyul/ControlFlowSideEffectsCollector.h> | ||||||
| #include <libsolutil/CommonData.h> | #include <libsolutil/CommonData.h> | ||||||
| 
 | 
 | ||||||
| using namespace std; | using namespace std; | ||||||
| @ -27,6 +28,12 @@ using namespace solidity; | |||||||
| using namespace solidity::yul; | using namespace solidity::yul; | ||||||
| using namespace solidity::util; | using namespace solidity::util; | ||||||
| 
 | 
 | ||||||
|  | void ConditionalUnsimplifier::run(OptimiserStepContext& _context, Block& _ast) | ||||||
|  | { | ||||||
|  | 	ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast); | ||||||
|  | 	ConditionalUnsimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ConditionalUnsimplifier::operator()(Switch& _switch) | void ConditionalUnsimplifier::operator()(Switch& _switch) | ||||||
| { | { | ||||||
| 	visit(*_switch.expression); | 	visit(*_switch.expression); | ||||||
| @ -78,7 +85,7 @@ void ConditionalUnsimplifier::operator()(Block& _block) | |||||||
| 					YulString condition = std::get<Identifier>(*_if.condition).name; | 					YulString condition = std::get<Identifier>(*_if.condition).name; | ||||||
| 					if ( | 					if ( | ||||||
| 						holds_alternative<Assignment>(_stmt2) && | 						holds_alternative<Assignment>(_stmt2) && | ||||||
| 						TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != | 						TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) != | ||||||
| 							TerminationFinder::ControlFlow::FlowOut | 							TerminationFinder::ControlFlow::FlowOut | ||||||
| 					) | 					) | ||||||
| 					{ | 					{ | ||||||
|  | |||||||
| @ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	static constexpr char const* name{"ConditionalUnsimplifier"}; | 	static constexpr char const* name{"ConditionalUnsimplifier"}; | ||||||
| 	static void run(OptimiserStepContext& _context, Block& _ast) | 	static void run(OptimiserStepContext& _context, Block& _ast); | ||||||
| 	{ |  | ||||||
| 		ConditionalUnsimplifier{_context.dialect}(_ast); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	using ASTModifier::operator(); | 	using ASTModifier::operator(); | ||||||
| 	void operator()(Switch& _switch) override; | 	void operator()(Switch& _switch) override; | ||||||
| 	void operator()(Block& _block) override; | 	void operator()(Block& _block) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	explicit ConditionalUnsimplifier(Dialect const& _dialect): | 	explicit ConditionalUnsimplifier( | ||||||
| 		m_dialect(_dialect) | 		Dialect const& _dialect, | ||||||
|  | 		std::map<YulString, ControlFlowSideEffects> const& _sideEffects | ||||||
|  | 	): | ||||||
|  | 		m_dialect(_dialect), m_functionSideEffects(_sideEffects) | ||||||
| 	{} | 	{} | ||||||
| 	Dialect const& m_dialect; | 	Dialect const& m_dialect; | ||||||
|  | 	std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ | |||||||
| #include <libyul/optimiser/DeadCodeEliminator.h> | #include <libyul/optimiser/DeadCodeEliminator.h> | ||||||
| #include <libyul/optimiser/Semantics.h> | #include <libyul/optimiser/Semantics.h> | ||||||
| #include <libyul/optimiser/OptimiserStep.h> | #include <libyul/optimiser/OptimiserStep.h> | ||||||
|  | #include <libyul/ControlFlowSideEffectsCollector.h> | ||||||
| #include <libyul/AST.h> | #include <libyul/AST.h> | ||||||
| 
 | 
 | ||||||
| #include <libevmasm/SemanticInformation.h> | #include <libevmasm/SemanticInformation.h> | ||||||
| @ -36,7 +37,11 @@ using namespace solidity::yul; | |||||||
| 
 | 
 | ||||||
| void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast) | void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast) | ||||||
| { | { | ||||||
| 	DeadCodeEliminator{_context.dialect}(_ast); | 	ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast); | ||||||
|  | 	DeadCodeEliminator{ | ||||||
|  | 		_context.dialect, | ||||||
|  | 		sideEffects.functionSideEffects() | ||||||
|  | 	}(_ast); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DeadCodeEliminator::operator()(ForLoop& _for) | void DeadCodeEliminator::operator()(ForLoop& _for) | ||||||
| @ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block) | |||||||
| { | { | ||||||
| 	TerminationFinder::ControlFlow controlFlowChange; | 	TerminationFinder::ControlFlow controlFlowChange; | ||||||
| 	size_t index; | 	size_t index; | ||||||
| 	tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements); | 	tie(controlFlowChange, index) = TerminationFinder{m_dialect, &m_functionSideEffects}.firstUnconditionalControlFlowChange(_block.statements); | ||||||
| 
 | 
 | ||||||
| 	// Erase everything after the terminating statement that is not a function definition.
 | 	// Erase everything after the terminating statement that is not a function definition.
 | ||||||
| 	if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max()) | 	if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max()) | ||||||
|  | |||||||
| @ -31,12 +31,15 @@ namespace solidity::yul | |||||||
| { | { | ||||||
| struct Dialect; | struct Dialect; | ||||||
| struct OptimiserStepContext; | struct OptimiserStepContext; | ||||||
|  | struct ControlFlowSideEffects; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Optimisation stage that removes unreachable code |  * Optimisation stage that removes unreachable code | ||||||
|  * |  * | ||||||
|  * Unreachable code is any code within a block which is preceded by a |  * Unreachable code is any code within a block which is preceded by a | ||||||
|  * leave, return, invalid, break, continue, selfdestruct or revert. |  * leave, return, invalid, break, continue, selfdestruct or revert or | ||||||
|  |  * a call to a user-defined function that never returns (either due to | ||||||
|  |  * recursion or a call to return / revert / stop). | ||||||
|  * |  * | ||||||
|  * Function definitions are retained as they might be called by earlier |  * Function definitions are retained as they might be called by earlier | ||||||
|  * code and thus are considered reachable. |  * code and thus are considered reachable. | ||||||
| @ -57,9 +60,13 @@ public: | |||||||
| 	void operator()(Block& _block) override; | 	void operator()(Block& _block) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {} | 	DeadCodeEliminator( | ||||||
|  | 		Dialect const& _dialect, | ||||||
|  | 		std::map<YulString, ControlFlowSideEffects> const& _sideEffects | ||||||
|  | 	): m_dialect(_dialect), m_functionSideEffects(_sideEffects) {} | ||||||
| 
 | 
 | ||||||
| 	Dialect const& m_dialect; | 	Dialect const& m_dialect; | ||||||
|  | 	std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition | |||||||
| TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement) | TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement) | ||||||
| { | { | ||||||
| 	if ( | 	if ( | ||||||
|  | 		holds_alternative<VariableDeclaration>(_statement) && | ||||||
|  | 		std::get<VariableDeclaration>(_statement).value && | ||||||
|  | 		containsNonContinuingFunctionCall(*std::get<VariableDeclaration>(_statement).value) | ||||||
|  | 	) | ||||||
|  | 		return ControlFlow::Terminate; | ||||||
|  | 	else if ( | ||||||
|  | 		holds_alternative<Assignment>(_statement) && | ||||||
|  | 		containsNonContinuingFunctionCall(*std::get<Assignment>(_statement).value) | ||||||
|  | 	) | ||||||
|  | 		return ControlFlow::Terminate; | ||||||
|  | 	else if ( | ||||||
| 		holds_alternative<ExpressionStatement>(_statement) && | 		holds_alternative<ExpressionStatement>(_statement) && | ||||||
| 		isTerminatingBuiltin(std::get<ExpressionStatement>(_statement)) | 		containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression) | ||||||
| 	) | 	) | ||||||
| 		return ControlFlow::Terminate; | 		return ControlFlow::Terminate; | ||||||
| 	else if (holds_alternative<Break>(_statement)) | 	else if (holds_alternative<Break>(_statement)) | ||||||
| @ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons | |||||||
| 		return ControlFlow::FlowOut; | 		return ControlFlow::FlowOut; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt) | bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr) | ||||||
| { | { | ||||||
| 	if (holds_alternative<FunctionCall>(_exprStmnt.expression)) | 	if (auto functionCall = std::get_if<FunctionCall>(&_expr)) | ||||||
| 		if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name)) | 	{ | ||||||
| 			return evmasm::SemanticInformation::terminatesControlFlow(*instruction); | 		for (auto const& arg: functionCall->arguments) | ||||||
|  | 			if (containsNonContinuingFunctionCall(arg)) | ||||||
|  | 				return true; | ||||||
|  | 
 | ||||||
|  | 		if (auto builtin = m_dialect.builtin(functionCall->functionName.name)) | ||||||
|  | 			return !builtin->controlFlowSideEffects.canContinue; | ||||||
|  | 		else if (m_functionSideEffects && m_functionSideEffects->count(functionCall->functionName.name)) | ||||||
|  | 			return !m_functionSideEffects->at(functionCall->functionName.name).canContinue; | ||||||
|  | 	} | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  | |||||||
| @ -205,22 +205,31 @@ private: | |||||||
| 	std::set<YulString> m_variableReferences; | 	std::set<YulString> m_variableReferences; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct ControlFlowSideEffects; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Helper class to find "irregular" control flow. |  * Helper class to find "irregular" control flow. | ||||||
|  * This includes termination, break and continue. |  * This includes termination, break, continue and leave. | ||||||
|  |  * In general, it is applied only to "simple" statements. The control-flow | ||||||
|  |  * of loops, switches and if statements is always "FlowOut" with the assumption | ||||||
|  |  * that the caller will descend into them. | ||||||
|  */ |  */ | ||||||
| class TerminationFinder | class TerminationFinder | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	// TODO check all uses of TerminationFinder!
 | 	/// "Terminate" here means that there is no continuing control-flow.
 | ||||||
|  | 	/// If this is applied to a function that can revert or stop, but can also
 | ||||||
|  | 	/// exit regularly, the property is set to "FlowOut".
 | ||||||
| 	enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave }; | 	enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave }; | ||||||
| 
 | 
 | ||||||
| 	TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {} | 	TerminationFinder( | ||||||
|  | 		Dialect const& _dialect, | ||||||
|  | 		std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr | ||||||
|  | 	): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} | ||||||
| 
 | 
 | ||||||
| 	/// @returns the index of the first statement in the provided sequence
 | 	/// @returns the index of the first statement in the provided sequence
 | ||||||
| 	/// that is an unconditional ``break``, ``continue``, ``leave`` or a
 | 	/// that is an unconditional ``break``, ``continue``, ``leave`` or a
 | ||||||
| 	/// call to a terminating builtin function.
 | 	/// call to a terminating function.
 | ||||||
| 	/// If control flow can continue at the end of the list,
 | 	/// If control flow can continue at the end of the list,
 | ||||||
| 	/// returns `FlowOut` and ``size_t(-1)``.
 | 	/// returns `FlowOut` and ``size_t(-1)``.
 | ||||||
| 	/// The function might return ``FlowOut`` even though control
 | 	/// The function might return ``FlowOut`` even though control
 | ||||||
| @ -233,13 +242,14 @@ public: | |||||||
| 	/// This function could return FlowOut even if control flow never continues.
 | 	/// This function could return FlowOut even if control flow never continues.
 | ||||||
| 	ControlFlow controlFlowKind(Statement const& _statement); | 	ControlFlow controlFlowKind(Statement const& _statement); | ||||||
| 
 | 
 | ||||||
| 	/// @returns true if the expression statement is a direct
 | 	/// @returns true if the expression contains a
 | ||||||
| 	/// call to a builtin terminating function like
 | 	/// call to a terminating function, i.e. a function that does not have
 | ||||||
| 	/// ``stop``, ``revert`` or ``return``.
 | 	/// a regular "flow out" control-flow (it might also be recursive).
 | ||||||
| 	bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt); | 	bool containsNonContinuingFunctionCall(Expression const& _expr); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	Dialect const& m_dialect; | 	Dialect const& m_dialect; | ||||||
|  | 	std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,38 @@ | |||||||
|  | { | ||||||
|  |     function recursive() { recursive() } | ||||||
|  |     function terminating() { stop() } | ||||||
|  |     function maybeReverting() { if calldataload(0) { revert(0, 0) } } | ||||||
|  | 
 | ||||||
|  |     let a := calldataload(7) | ||||||
|  |     if a { recursive() } | ||||||
|  | 
 | ||||||
|  |     a := calldataload(a) | ||||||
|  |     if a { maybeReverting() } | ||||||
|  | 
 | ||||||
|  |     a := calldataload(a) | ||||||
|  |     if a { terminating() } | ||||||
|  | 
 | ||||||
|  |     sstore(0, a) | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: conditionalSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     function recursive() | ||||||
|  | //     { recursive() } | ||||||
|  | //     function terminating() | ||||||
|  | //     { stop() } | ||||||
|  | //     function maybeReverting() | ||||||
|  | //     { | ||||||
|  | //         if calldataload(0) { revert(0, 0) } | ||||||
|  | //     } | ||||||
|  | //     let a := calldataload(7) | ||||||
|  | //     if a { recursive() } | ||||||
|  | //     a := 0 | ||||||
|  | //     a := calldataload(a) | ||||||
|  | //     if a { maybeReverting() } | ||||||
|  | //     a := calldataload(a) | ||||||
|  | //     if a { terminating() } | ||||||
|  | //     a := 0 | ||||||
|  | //     sstore(0, a) | ||||||
|  | // } | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | { | ||||||
|  |     function recursive() { recursive() } | ||||||
|  |     function terminating() { stop() } | ||||||
|  |     function maybeReverting() { if calldataload(0) { revert(0, 0) } } | ||||||
|  | 
 | ||||||
|  |     let a := calldataload(7) | ||||||
|  |     if a { recursive() } | ||||||
|  |     a := 0 | ||||||
|  | 
 | ||||||
|  |     a := calldataload(a) | ||||||
|  |     if a { maybeReverting() } | ||||||
|  | 
 | ||||||
|  |     a := calldataload(a) | ||||||
|  |     if a { terminating() } | ||||||
|  |     a := 0 | ||||||
|  | 
 | ||||||
|  |     sstore(0, a) | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: conditionalUnsimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     function recursive() | ||||||
|  | //     { recursive() } | ||||||
|  | //     function terminating() | ||||||
|  | //     { stop() } | ||||||
|  | //     function maybeReverting() | ||||||
|  | //     { | ||||||
|  | //         if calldataload(0) { revert(0, 0) } | ||||||
|  | //     } | ||||||
|  | //     let a := calldataload(7) | ||||||
|  | //     if a { recursive() } | ||||||
|  | //     a := calldataload(a) | ||||||
|  | //     if a { maybeReverting() } | ||||||
|  | //     a := calldataload(a) | ||||||
|  | //     if a { terminating() } | ||||||
|  | //     sstore(0, a) | ||||||
|  | // } | ||||||
| @ -0,0 +1,44 @@ | |||||||
|  | { | ||||||
|  |     switch calldataload(0) | ||||||
|  |     case 0 { | ||||||
|  |         recursive() | ||||||
|  |         sstore(0, 1) | ||||||
|  |     } | ||||||
|  |     case 1 { | ||||||
|  |         terminating() | ||||||
|  |         sstore(0, 7) | ||||||
|  |     } | ||||||
|  |     case 2 { | ||||||
|  |         reverting() | ||||||
|  |         sstore(0, 7) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     function recursive() | ||||||
|  |     { | ||||||
|  |         recursive() | ||||||
|  |     } | ||||||
|  |     function terminating() | ||||||
|  |     { | ||||||
|  |         return(0, 0) | ||||||
|  |     } | ||||||
|  |     function reverting() | ||||||
|  |     { | ||||||
|  |         revert(0, 0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: deadCodeEliminator | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     switch calldataload(0) | ||||||
|  | //     case 0 { recursive() } | ||||||
|  | //     case 1 { terminating() } | ||||||
|  | //     case 2 { reverting() } | ||||||
|  | //     function recursive() | ||||||
|  | //     { recursive() } | ||||||
|  | //     function terminating() | ||||||
|  | //     { return(0, 0) } | ||||||
|  | //     function reverting() | ||||||
|  | //     { revert(0, 0) } | ||||||
|  | // } | ||||||
| @ -5,9 +5,7 @@ | |||||||
| 
 | 
 | ||||||
|     function fun() |     function fun() | ||||||
|     { |     { | ||||||
|         return(1, 1) |         sstore(0, 1) | ||||||
| 
 |  | ||||||
|         pop(sub(10, 5)) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pop(add(1, 1)) |     pop(add(1, 1)) | ||||||
| @ -19,5 +17,5 @@ | |||||||
| //     fun() | //     fun() | ||||||
| //     revert(0, 0) | //     revert(0, 0) | ||||||
| //     function fun() | //     function fun() | ||||||
| //     { return(1, 1) } | //     { sstore(0, 1) } | ||||||
| // } | // } | ||||||
|  | |||||||
| @ -1,11 +1,11 @@ | |||||||
| { | { | ||||||
| 	// This function name can be shortened, the other cannot. | 	// This function name can be shortened, the other cannot. | ||||||
| 	function nonmstore_(x) { | 	function nonmstore_(x) { | ||||||
| 		nonmstore_(x) | 		if calldataload(0) { nonmstore_(x) } | ||||||
| 		sstore(10, calldataload(2)) | 		sstore(10, calldataload(2)) | ||||||
| 	} | 	} | ||||||
| 	function mstore_(x) -> y { | 	function mstore_(x) -> y { | ||||||
| 		let t3_3_ := mstore_(x) | 		if calldataload(0) { let t3_3_ := mstore_(x) } | ||||||
| 		y := 8 | 		y := 8 | ||||||
| 		sstore(y, calldataload(y)) | 		sstore(y, calldataload(y)) | ||||||
| 	} | 	} | ||||||
| @ -22,12 +22,12 @@ | |||||||
| //     } | //     } | ||||||
| //     function nonmstore(x) | //     function nonmstore(x) | ||||||
| //     { | //     { | ||||||
| //         nonmstore(x) | //         if calldataload(0) { nonmstore(x) } | ||||||
| //         sstore(10, calldataload(2)) | //         sstore(10, calldataload(2)) | ||||||
| //     } | //     } | ||||||
| //     function mstore_(x) -> y | //     function mstore_(x) -> y | ||||||
| //     { | //     { | ||||||
| //         pop(mstore_(x)) | //         if calldataload(0) { pop(mstore_(x)) } | ||||||
| //         y := 8 | //         y := 8 | ||||||
| //         sstore(y, calldataload(y)) | //         sstore(y, calldataload(y)) | ||||||
| //     } | //     } | ||||||
|  | |||||||
| @ -5,7 +5,9 @@ | |||||||
|     sstore(1, l) |     sstore(1, l) | ||||||
|     function f(a, b, c) -> x, y, z |     function f(a, b, c) -> x, y, z | ||||||
|     { |     { | ||||||
|         x, y, z := f(1, 2, 3) |         if calldataload(0) { | ||||||
|  |             x, y, z := f(1, 2, 3) | ||||||
|  |         } | ||||||
|         x := add(x, 1) |         x := add(x, 1) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -21,9 +23,13 @@ | |||||||
| //     } | //     } | ||||||
| //     function f() -> x, y, z | //     function f() -> x, y, z | ||||||
| //     { | //     { | ||||||
| //         let x_1, y_1, z_1 := f() | //         if calldataload(0) | ||||||
| //         y := y_1 | //         { | ||||||
| //         z := z_1 | //             let x_1, y_1, z_1 := f() | ||||||
| //         x := add(x_1, 1) | //             x := x_1 | ||||||
|  | //             y := y_1 | ||||||
|  | //             z := z_1 | ||||||
|  | //         } | ||||||
|  | //         x := add(x, 1) | ||||||
| //     } | //     } | ||||||
| // } | // } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user