mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #5292 from ethereum/redundantAssignEliminator
[Yul] Redundant assign eliminator.
This commit is contained in:
		
						commit
						8d01db7c2d
					
				
							
								
								
									
										193
									
								
								libyul/optimiser/RedundantAssignEliminator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								libyul/optimiser/RedundantAssignEliminator.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | ||||
| /*
 | ||||
| 	This file is part of solidity. | ||||
| 
 | ||||
| 	solidity 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. | ||||
| 
 | ||||
| 	solidity 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 solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /**
 | ||||
|  * Optimiser component that removes assignments to variables that are not used | ||||
|  * until they go out of scope or are re-assigned. | ||||
|  */ | ||||
| 
 | ||||
| #include <libyul/optimiser/RedundantAssignEliminator.h> | ||||
| 
 | ||||
| #include <libyul/optimiser/Semantics.h> | ||||
| 
 | ||||
| #include <libsolidity/inlineasm/AsmData.h> | ||||
| 
 | ||||
| #include <libdevcore/CommonData.h> | ||||
| 
 | ||||
| #include <boost/range/algorithm_ext/erase.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace dev; | ||||
| using namespace dev::yul; | ||||
| using namespace dev::solidity; | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(Identifier const& _identifier) | ||||
| { | ||||
| 	changeUndecidedTo(_identifier.name, State::Used); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(VariableDeclaration const& _variableDeclaration) | ||||
| { | ||||
| 	ASTWalker::operator()(_variableDeclaration); | ||||
| 
 | ||||
| 	for (auto const& var: _variableDeclaration.variables) | ||||
| 		m_declaredVariables.insert(var.name); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(Assignment const& _assignment) | ||||
| { | ||||
| 	visit(*_assignment.value); | ||||
| 	for (auto const& var: _assignment.variableNames) | ||||
| 		changeUndecidedTo(var.name, State::Unused); | ||||
| 
 | ||||
| 	if (_assignment.variableNames.size() == 1) | ||||
| 		// Default-construct it in "Undecided" state if it does not yet exist.
 | ||||
| 		m_assignments[_assignment.variableNames.front().name][&_assignment]; | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(If const& _if) | ||||
| { | ||||
| 	visit(*_if.condition); | ||||
| 
 | ||||
| 	RedundantAssignEliminator branch{*this}; | ||||
| 	branch(_if.body); | ||||
| 
 | ||||
| 	join(branch); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(Switch const& _switch) | ||||
| { | ||||
| 	visit(*_switch.expression); | ||||
| 
 | ||||
| 	bool hasDefault = false; | ||||
| 	vector<RedundantAssignEliminator> branches; | ||||
| 	for (auto const& c: _switch.cases) | ||||
| 	{ | ||||
| 		if (!c.value) | ||||
| 			hasDefault = true; | ||||
| 		branches.emplace_back(*this); | ||||
| 		branches.back()(c.body); | ||||
| 	} | ||||
| 
 | ||||
| 	if (hasDefault) | ||||
| 	{ | ||||
| 		*this = std::move(branches.back()); | ||||
| 		branches.pop_back(); | ||||
| 	} | ||||
| 	for (auto& branch: branches) | ||||
| 		join(branch); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition) | ||||
| { | ||||
| 	(*this)(_functionDefinition.body); | ||||
| 
 | ||||
| 	for (auto const& param: _functionDefinition.parameters) | ||||
| 		changeUndecidedTo(param.name, State::Unused); | ||||
| 	for (auto const& retParam: _functionDefinition.returnVariables) | ||||
| 		changeUndecidedTo(retParam.name, State::Used); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(ForLoop const& _forLoop) | ||||
| { | ||||
| 	// This will set all variables that are declared in this
 | ||||
| 	// block to "unused" when it is destroyed.
 | ||||
| 	BlockScope scope(*this); | ||||
| 
 | ||||
| 	// We need to visit the statements directly because of the
 | ||||
| 	// scoping rules.
 | ||||
| 	walkVector(_forLoop.pre.statements); | ||||
| 
 | ||||
| 	// We just run the loop twice to account for the
 | ||||
| 	// back edge.
 | ||||
| 	// There need not be more runs because we only have three different states.
 | ||||
| 
 | ||||
| 	visit(*_forLoop.condition); | ||||
| 
 | ||||
| 	RedundantAssignEliminator zeroRuns{*this}; | ||||
| 
 | ||||
| 	(*this)(_forLoop.body); | ||||
| 	(*this)(_forLoop.post); | ||||
| 
 | ||||
| 	visit(*_forLoop.condition); | ||||
| 
 | ||||
| 	RedundantAssignEliminator oneRun{*this}; | ||||
| 
 | ||||
| 	(*this)(_forLoop.body); | ||||
| 	(*this)(_forLoop.post); | ||||
| 
 | ||||
| 	visit(*_forLoop.condition); | ||||
| 
 | ||||
| 	// Order does not matter because "max" is commutative and associative.
 | ||||
| 	join(oneRun); | ||||
| 	join(zeroRuns); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::operator()(Block const& _block) | ||||
| { | ||||
| 	// This will set all variables that are declared in this
 | ||||
| 	// block to "unused" when it is destroyed.
 | ||||
| 	BlockScope scope(*this); | ||||
| 
 | ||||
| 	ASTWalker::operator()(_block); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::run(Block& _ast) | ||||
| { | ||||
| 	RedundantAssignEliminator rae; | ||||
| 	rae(_ast); | ||||
| 
 | ||||
| 	std::set<Assignment const*> assignmentsToRemove; | ||||
| 	for (auto const& variables: rae.m_assignments) | ||||
| 		for (auto const& assignment: variables.second) | ||||
| 		{ | ||||
| 			assertThrow(assignment.second != State::Undecided, OptimizerException, ""); | ||||
| 			if (assignment.second == State::Unused && MovableChecker{*assignment.first->value}.movable()) | ||||
| 				assignmentsToRemove.insert(assignment.first); | ||||
| 		} | ||||
| 
 | ||||
| 	AssignmentRemover remover{assignmentsToRemove}; | ||||
| 	remover(_ast); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::join(RedundantAssignEliminator& _other) | ||||
| { | ||||
| 	for (auto& var: _other.m_assignments) | ||||
| 		if (m_assignments.count(var.first)) | ||||
| 		{ | ||||
| 			map<Assignment const*, State>& assignmentsHere = m_assignments[var.first]; | ||||
| 			for (auto& assignment: var.second) | ||||
| 				assignmentsHere[assignment.first].join(assignment.second); | ||||
| 		} | ||||
| 		else | ||||
| 			m_assignments[var.first] = std::move(var.second); | ||||
| } | ||||
| 
 | ||||
| void RedundantAssignEliminator::changeUndecidedTo(string const& _variable, RedundantAssignEliminator::State _newState) | ||||
| { | ||||
| 	for (auto& assignment: m_assignments[_variable]) | ||||
| 		if (assignment.second == State{State::Undecided}) | ||||
| 			assignment.second = _newState; | ||||
| } | ||||
| 
 | ||||
| void AssignmentRemover::operator()(Block& _block) | ||||
| { | ||||
| 	boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool { | ||||
| 		return _statement.type() == typeid(Assignment) && m_toRemove.count(&boost::get<Assignment>(_statement)); | ||||
| 	}); | ||||
| 
 | ||||
| 	ASTModifier::operator()(_block); | ||||
| } | ||||
							
								
								
									
										185
									
								
								libyul/optimiser/RedundantAssignEliminator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								libyul/optimiser/RedundantAssignEliminator.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| /*
 | ||||
| 	This file is part of solidity. | ||||
| 
 | ||||
| 	solidity 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. | ||||
| 
 | ||||
| 	solidity 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 solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| /**
 | ||||
|  * Optimiser component that removes assignments to variables that are not used | ||||
|  * until they go out of scope or are re-assigned. | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <libyul/ASTDataForward.h> | ||||
| 
 | ||||
| #include <libyul/optimiser/ASTWalker.h> | ||||
| 
 | ||||
| #include <map> | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| namespace yul | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Optimiser component that removes assignments to variables that are not used | ||||
|  * until they go out of scope or are re-assigned. This component | ||||
|  * respects the control-flow and takes it into account for removal. | ||||
|  * | ||||
|  * Example: | ||||
|  * | ||||
|  * { | ||||
|  *   let a | ||||
|  *   a := 1 | ||||
|  *   a := 2 | ||||
|  *   b := 2 | ||||
|  *   if calldataload(0) | ||||
|  *   { | ||||
|  *     b := mload(a) | ||||
|  *   } | ||||
|  *   a := b | ||||
|  * } | ||||
|  * | ||||
|  * In the example, "a := 1" can be removed because the value from this assignment | ||||
|  * is not used in any control-flow branch (it is replaced right away). | ||||
|  * The assignment "a := 2" is also overwritten by "a := b" at the end, | ||||
|  * but there is a control-flow path (through the condition body) which uses | ||||
|  * the value from "a := 2" and thus, this assignment cannot be removed. | ||||
|  * | ||||
|  * Detailed rules: | ||||
|  * | ||||
|  * The AST is traversed twice: in an information gathering step and in the | ||||
|  * actual removal step. During information gathering, we maintain a | ||||
|  * mapping from assignment statements to the three states | ||||
|  * "unused", "undecided" and "used". | ||||
|  * When an assignment is visited, it is added to the mapping in the "undecided" state | ||||
|  * (see remark about for loops below) and every other assignment to the same variable | ||||
|  * that is still in the "undecided" state is changed to "unused". | ||||
|  * When a variable is referenced, the state of any assignment to that variable still | ||||
|  * in the "undecided" state is changed to "used". | ||||
|  * At points where control flow splits, a copy | ||||
|  * of the mapping is handed over to each branch. At points where control flow | ||||
|  * joins, the two mappings coming from the two branches are combined in the following way: | ||||
|  * Statements that are only in one mapping or have the same state are used unchanged. | ||||
|  * Conflicting values are resolved in the following way: | ||||
|  * "unused", "undecided" -> "undecided" | ||||
|  * "unused", "used" -> "used" | ||||
|  * "undecided, "used" -> "used". | ||||
|  * | ||||
|  * For for-loops, the condition, body and post-part are visited twice, taking | ||||
|  * the joining control-flow at the condition into account. | ||||
|  * In other words, we create three control flow paths: Zero runs of the loop, | ||||
|  * one run and two runs and then combine them at the end. | ||||
|  * Running at most twice is enough because there are only three different states. | ||||
|  * | ||||
|  * For switch statements that have a "default"-case, there is no control-flow | ||||
|  * part that skips the switch. | ||||
|  * | ||||
|  * When a variable goes out of scope, all statements still in the "undecided" | ||||
|  * state are changed to "unused", unless the variable is the return | ||||
|  * parameter of a function - there, the state changes to "used". | ||||
|  * | ||||
|  * In the second traversal, all assignments that are in the "unused" state are removed. | ||||
|  * | ||||
|  * | ||||
|  * This step is usually run right after the SSA transform to complete | ||||
|  * the generation of the pseudo-SSA. | ||||
|  * | ||||
|  * Prerequisite: Disambiguator. | ||||
|  */ | ||||
| class RedundantAssignEliminator: public ASTWalker | ||||
| { | ||||
| public: | ||||
| 	RedundantAssignEliminator(RedundantAssignEliminator const&) = default; | ||||
| 	RedundantAssignEliminator& operator=(RedundantAssignEliminator const&) = default; | ||||
| 	RedundantAssignEliminator(RedundantAssignEliminator&&) = default; | ||||
| 	RedundantAssignEliminator& operator=(RedundantAssignEliminator&&) = default; | ||||
| 
 | ||||
| 	void operator()(Identifier const& _identifier) override; | ||||
| 	void operator()(VariableDeclaration const& _variableDeclaration) override; | ||||
| 	void operator()(Assignment const& _assignment) override; | ||||
| 	void operator()(If const& _if) override; | ||||
| 	void operator()(Switch const& _switch) override; | ||||
| 	void operator()(FunctionDefinition const&) override; | ||||
| 	void operator()(ForLoop const&) override; | ||||
| 	void operator()(Block const& _block) override; | ||||
| 
 | ||||
| 	static void run(Block& _ast); | ||||
| 
 | ||||
| private: | ||||
| 	RedundantAssignEliminator() {} | ||||
| 
 | ||||
| 	class State | ||||
| 	{ | ||||
| 	public: | ||||
| 		enum Value { Unused, Undecided, Used }; | ||||
| 		State(Value _value = Undecided): m_value(_value) {} | ||||
| 		bool operator==(State _other) const { return m_value == _other.m_value; } | ||||
| 		bool operator!=(State _other) const { return !operator==(_other); } | ||||
| 		void join(State _other) | ||||
| 		{ | ||||
| 			// Using "max" works here because of the order of the values in the enum.
 | ||||
| 			m_value = Value(std::max(int(_other.m_value), int(m_value))); | ||||
| 		} | ||||
| 	private: | ||||
| 		Value m_value = Undecided; | ||||
| 	}; | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * Takes care about storing the list of declared variables and | ||||
| 	 * sets them to "unused" when it is destroyed. | ||||
| 	 */ | ||||
| 	class BlockScope | ||||
| 	{ | ||||
| 	public: | ||||
| 		explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae) | ||||
| 		{ | ||||
| 			swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); | ||||
| 		} | ||||
| 		~BlockScope() | ||||
| 		{ | ||||
| 			for (auto const& var: m_rae.m_declaredVariables) | ||||
| 				m_rae.changeUndecidedTo(var, State::Unused); | ||||
| 			swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		RedundantAssignEliminator& m_rae; | ||||
| 		std::set<std::string> m_outerDeclaredVariables; | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Joins the assignment mapping with @a _other according to the rules laid out
 | ||||
| 	/// above.
 | ||||
| 	/// Will destroy @a _other.
 | ||||
| 	void join(RedundantAssignEliminator& _other); | ||||
| 	void changeUndecidedTo(std::string const& _variable, State _newState); | ||||
| 
 | ||||
| 	std::set<std::string> m_declaredVariables; | ||||
| 	std::map<std::string, std::map<Assignment const*, State>> m_assignments; | ||||
| }; | ||||
| 
 | ||||
| class AssignmentRemover: public ASTModifier | ||||
| { | ||||
| public: | ||||
| 	explicit AssignmentRemover(std::set<Assignment const*> const& _toRemove): | ||||
| 		m_toRemove(_toRemove) | ||||
| 	{} | ||||
| 	void operator()(Block& _block) override; | ||||
| 
 | ||||
| private: | ||||
| 	std::set<Assignment const*> const& m_toRemove; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } | ||||
| @ -36,6 +36,7 @@ | ||||
| #include <libyul/optimiser/UnusedPruner.h> | ||||
| #include <libyul/optimiser/ExpressionJoiner.h> | ||||
| #include <libyul/optimiser/SSATransform.h> | ||||
| #include <libyul/optimiser/RedundantAssignEliminator.h> | ||||
| 
 | ||||
| #include <libsolidity/parsing/Scanner.h> | ||||
| #include <libsolidity/inlineasm/AsmPrinter.h> | ||||
| @ -178,6 +179,18 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con | ||||
| 		NameDispenser nameDispenser(*m_ast); | ||||
| 		SSATransform::run(*m_ast, nameDispenser); | ||||
| 	} | ||||
| 	else if (m_optimizerStep == "redundantAssignEliminator") | ||||
| 	{ | ||||
| 		disambiguate(); | ||||
| 		RedundantAssignEliminator::run(*m_ast); | ||||
| 	} | ||||
| 	else if (m_optimizerStep == "ssaPlusCleanup") | ||||
| 	{ | ||||
| 		disambiguate(); | ||||
| 		NameDispenser nameDispenser(*m_ast); | ||||
| 		SSATransform::run(*m_ast, nameDispenser); | ||||
| 		RedundantAssignEliminator::run(*m_ast); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; | ||||
|  | ||||
| @ -0,0 +1,26 @@ | ||||
| { | ||||
|     for { | ||||
|         let a := 2 | ||||
|         // Should not be removed, even though you might think | ||||
|         // it goes out of scope | ||||
|         a := 3 | ||||
|     } a { a := add(a, 1) } | ||||
|     { | ||||
|         a := 7 | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     for { | ||||
| //         let a := 2 | ||||
| //         a := 3 | ||||
| //     } | ||||
| //     a | ||||
| //     { | ||||
| //         a := add(a, 1) | ||||
| //     } | ||||
| //     { | ||||
| //         a := 7 | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,31 @@ | ||||
| { | ||||
|     let x | ||||
|     let y | ||||
|     // Cannot be removed, because we might skip the loop | ||||
|     x := 1 | ||||
|     for { } calldataload(0) { } | ||||
|     { | ||||
|         // Cannot be removed | ||||
|         x := 2 | ||||
|         // Can be removed | ||||
|         y := 3 | ||||
|     } | ||||
|     y := 8 | ||||
|     mstore(x, 0) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     let y | ||||
| //     x := 1 | ||||
| //     for { | ||||
| //     } | ||||
| //     calldataload(0) | ||||
| //     { | ||||
| //     } | ||||
| //     { | ||||
| //         x := 2 | ||||
| //     } | ||||
| //     mstore(x, 0) | ||||
| // } | ||||
| @ -0,0 +1,27 @@ | ||||
| { | ||||
|     let x | ||||
|     // Cannot be removed, because we might run the loop only once | ||||
|     x := 1 | ||||
|     for { } calldataload(0) { } | ||||
|     { | ||||
|         mstore(x, 2) | ||||
|         // Cannot be removed because of the line above | ||||
|         x := 2 | ||||
|     } | ||||
|     x := 3 | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     x := 1 | ||||
| //     for { | ||||
| //     } | ||||
| //     calldataload(0) | ||||
| //     { | ||||
| //     } | ||||
| //     { | ||||
| //         mstore(x, 2) | ||||
| //         x := 2 | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,23 @@ | ||||
| { | ||||
|     let r | ||||
|     r := 1 | ||||
|     function f(x, y) -> a, b { | ||||
|         // Can be removed, is param | ||||
|         x := 1 | ||||
|         y := 2 | ||||
|         // Cannot be removed, is return param | ||||
|         a := 3 | ||||
|         b := 4 | ||||
|     } | ||||
|     r := 2 | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let r | ||||
| //     function f(x, y) -> a, b | ||||
| //     { | ||||
| //         a := 3 | ||||
| //         b := 4 | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,24 @@ | ||||
| { | ||||
|     let c | ||||
|     let d | ||||
|     c := calldataload(0) | ||||
|     d := 1 | ||||
|     if c { | ||||
|         d := 2 | ||||
|     } | ||||
|     // This enforces that none of the assignments above can be removed. | ||||
|     mstore(0, d) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let c | ||||
| //     let d | ||||
| //     c := calldataload(0) | ||||
| //     d := 1 | ||||
| //     if c | ||||
| //     { | ||||
| //         d := 2 | ||||
| //     } | ||||
| //     mstore(0, d) | ||||
| // } | ||||
| @ -0,0 +1,24 @@ | ||||
| { | ||||
|     let c | ||||
|     let d | ||||
|     c := calldataload(0) | ||||
|     // This assignment will be overwritten in all branches and thus can be removed. | ||||
|     d := 1 | ||||
|     if c { | ||||
|         d := 2 | ||||
|     } | ||||
|     d := 3 | ||||
|     mstore(0, d) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let c | ||||
| //     let d | ||||
| //     c := calldataload(0) | ||||
| //     if c | ||||
| //     { | ||||
| //     } | ||||
| //     d := 3 | ||||
| //     mstore(0, d) | ||||
| // } | ||||
| @ -0,0 +1,25 @@ | ||||
| { | ||||
|     let c | ||||
|     let d | ||||
|     c := calldataload(0) | ||||
|     d := 1 | ||||
|     if c { | ||||
|         // Uses the assignment above | ||||
|         d := d | ||||
|     } | ||||
|     d := 3 | ||||
|     mstore(0, d) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let c | ||||
| //     let d | ||||
| //     c := calldataload(0) | ||||
| //     d := 1 | ||||
| //     if c | ||||
| //     { | ||||
| //     } | ||||
| //     d := 3 | ||||
| //     mstore(0, d) | ||||
| // } | ||||
| @ -0,0 +1,19 @@ | ||||
| { | ||||
|     function f() -> a, b {} | ||||
|     let x, y | ||||
|     x := 1 | ||||
|     x := 2 | ||||
|     // Will not be used, but is a multi-assign, so not removed. | ||||
|     x, y := f() | ||||
|     x := 3 | ||||
|     y := 4 | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     function f() -> a, b | ||||
| //     { | ||||
| //     } | ||||
| //     let x, y | ||||
| //     x, y := f() | ||||
| // } | ||||
| @ -0,0 +1,15 @@ | ||||
| { | ||||
|     let a := 2 | ||||
|     a := 7 | ||||
|     let b := 8 | ||||
|     b := a | ||||
|     a := b | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let a := 2 | ||||
| //     a := 7 | ||||
| //     let b := 8 | ||||
| //     b := a | ||||
| // } | ||||
| @ -0,0 +1,11 @@ | ||||
| { | ||||
|     let a | ||||
|     a := 0 | ||||
|     a := mload(0) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let a | ||||
| //     a := mload(0) | ||||
| // } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     let a | ||||
|     { | ||||
|         let b | ||||
|         b := 2 | ||||
|         a := 2 | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let a | ||||
| //     { | ||||
| //         let b | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,10 @@ | ||||
| { | ||||
|     let a | ||||
|     a := 1 | ||||
|     a := 2 | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let a | ||||
| // } | ||||
| @ -0,0 +1,22 @@ | ||||
| { | ||||
|     let x | ||||
|     // Will be overwritten in all branches | ||||
|     x := 1 | ||||
|     switch calldataload(0) | ||||
|     case 0 { x := 2 } | ||||
|     default { x := 3 } | ||||
|     mstore(x, 0) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     switch calldataload(0) | ||||
| //     case 0 { | ||||
| //         x := 2 | ||||
| //     } | ||||
| //     default { | ||||
| //         x := 3 | ||||
| //     } | ||||
| //     mstore(x, 0) | ||||
| // } | ||||
| @ -0,0 +1,19 @@ | ||||
| { | ||||
|     let x | ||||
|     // Will NOT be overwritten in all branches | ||||
|     x := 1 | ||||
|     switch calldataload(0) | ||||
|     case 0 { x := 2 } | ||||
|     mstore(x, 0) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     x := 1 | ||||
| //     switch calldataload(0) | ||||
| //     case 0 { | ||||
| //         x := 2 | ||||
| //     } | ||||
| //     mstore(x, 0) | ||||
| // } | ||||
| @ -0,0 +1,23 @@ | ||||
| { | ||||
|     let x | ||||
|     // Will be used in some and overwritten in others | ||||
|     x := 1 | ||||
|     switch calldataload(0) | ||||
|     case 0 { x := 2 } | ||||
|     default { mstore(x, 1) } | ||||
|     mstore(x, 0) | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     x := 1 | ||||
| //     switch calldataload(0) | ||||
| //     case 0 { | ||||
| //         x := 2 | ||||
| //     } | ||||
| //     default { | ||||
| //         mstore(x, 1) | ||||
| //     } | ||||
| //     mstore(x, 0) | ||||
| // } | ||||
| @ -0,0 +1,16 @@ | ||||
| { | ||||
|     let x | ||||
|     // Not referenced anywhere. | ||||
|     x := 1 | ||||
|     switch calldataload(0) | ||||
|     case 0 { mstore(0, 1) } | ||||
| } | ||||
| // ---- | ||||
| // redundantAssignEliminator | ||||
| // { | ||||
| //     let x | ||||
| //     switch calldataload(0) | ||||
| //     case 0 { | ||||
| //         mstore(0, 1) | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,35 @@ | ||||
| { | ||||
|     function copy(from, to) -> length { | ||||
|         length := mload(from) | ||||
|         mstore(to, length) | ||||
|         from := add(from, 0x20) | ||||
|         to := add(to, 0x20) | ||||
|         for { let x := 1 } lt(x, length) { x := add(x, 0x20) } { | ||||
|             mstore(add(to, x), mload(add(from, x))) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // ssaPlusCleanup | ||||
| // { | ||||
| //     function copy(from, to) -> length | ||||
| //     { | ||||
| //         let length_1 := mload(from) | ||||
| //         length := length_1 | ||||
| //         mstore(to, length_1) | ||||
| //         let from_1 := add(from, 0x20) | ||||
| //         let to_1 := add(to, 0x20) | ||||
| //         for { | ||||
| //             let x_1 := 1 | ||||
| //             let x := x_1 | ||||
| //         } | ||||
| //         lt(x, length_1) | ||||
| //         { | ||||
| //             let x_2 := add(x, 0x20) | ||||
| //             x := x_2 | ||||
| //         } | ||||
| //         { | ||||
| //             mstore(add(to_1, x), mload(add(from_1, x))) | ||||
| //         } | ||||
| //     } | ||||
| // } | ||||
| @ -0,0 +1,17 @@ | ||||
| { | ||||
|     let a := 1 | ||||
|     a := 2 | ||||
|     a := 3 | ||||
|     a := 4 | ||||
|     mstore(0, a) | ||||
| } | ||||
| // ---- | ||||
| // ssaPlusCleanup | ||||
| // { | ||||
| //     let a_1 := 1 | ||||
| //     let a := a_1 | ||||
| //     let a_2 := 2 | ||||
| //     let a_3 := 3 | ||||
| //     let a_4 := 4 | ||||
| //     mstore(0, a_4) | ||||
| // } | ||||
| @ -0,0 +1,17 @@ | ||||
| { | ||||
|     let a := 1 | ||||
|     a := add(a, 2) | ||||
|     a := add(a, 3) | ||||
|     a := mload(add(a, 4)) | ||||
|     mstore(0, a) | ||||
| } | ||||
| // ---- | ||||
| // ssaPlusCleanup | ||||
| // { | ||||
| //     let a_1 := 1 | ||||
| //     let a := a_1 | ||||
| //     let a_2 := add(a_1, 2) | ||||
| //     let a_3 := add(a_2, 3) | ||||
| //     let a_4 := mload(add(a_3, 4)) | ||||
| //     mstore(0, a_4) | ||||
| // } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user