mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8161 from ethereum/yulTypeChecking
Yul type checking
This commit is contained in:
commit
44bcff42f5
@ -82,7 +82,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
|
|||||||
vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
||||||
{
|
{
|
||||||
expectValidType(_literal.type, _literal.location);
|
expectValidType(_literal.type, _literal.location);
|
||||||
|
|
||||||
if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32)
|
if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32)
|
||||||
typeError(
|
typeError(
|
||||||
_literal.location,
|
_literal.location,
|
||||||
@ -93,6 +92,13 @@ vector<YulString> AsmAnalyzer::operator()(Literal const& _literal)
|
|||||||
else if (_literal.kind == LiteralKind::Boolean)
|
else if (_literal.kind == LiteralKind::Boolean)
|
||||||
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
|
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
|
||||||
|
|
||||||
|
if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type))
|
||||||
|
typeError(
|
||||||
|
_literal.location,
|
||||||
|
"Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"."
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return {_literal.type};
|
return {_literal.type};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +185,8 @@ void AsmAnalyzer::operator()(Assignment const& _assignment)
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (size_t i = 0; i < numVariables; ++i)
|
for (size_t i = 0; i < numVariables; ++i)
|
||||||
checkAssignment(_assignment.variableNames[i]);
|
if (i < types.size())
|
||||||
|
checkAssignment(_assignment.variableNames[i], types[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
||||||
@ -193,6 +200,9 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
|||||||
yul::IdentifierContext::VariableDeclaration,
|
yul::IdentifierContext::VariableDeclaration,
|
||||||
m_currentScope->insideFunction()
|
m_currentScope->insideFunction()
|
||||||
);
|
);
|
||||||
|
for (auto const& variable: _varDecl.variables)
|
||||||
|
expectValidType(variable.type, variable.location);
|
||||||
|
|
||||||
if (_varDecl.value)
|
if (_varDecl.value)
|
||||||
{
|
{
|
||||||
vector<YulString> types = std::visit(*this, *_varDecl.value);
|
vector<YulString> types = std::visit(*this, *_varDecl.value);
|
||||||
@ -204,16 +214,26 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
|||||||
to_string(types.size()) +
|
to_string(types.size()) +
|
||||||
" values."
|
" values."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _varDecl.variables.size(); ++i)
|
||||||
|
{
|
||||||
|
YulString givenType = m_dialect.defaultType;
|
||||||
|
if (i < types.size())
|
||||||
|
givenType = types[i];
|
||||||
|
TypedName const& variable = _varDecl.variables[i];
|
||||||
|
if (variable.type != givenType)
|
||||||
|
typeError(
|
||||||
|
variable.location,
|
||||||
|
"Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TypedName const& variable: _varDecl.variables)
|
for (TypedName const& variable: _varDecl.variables)
|
||||||
{
|
|
||||||
expectValidType(variable.type, variable.location);
|
|
||||||
m_activeVariables.insert(&std::get<Scope::Variable>(
|
m_activeVariables.insert(&std::get<Scope::Variable>(
|
||||||
m_currentScope->identifiers.at(variable.name))
|
m_currentScope->identifiers.at(variable.name))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
|
void AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
|
||||||
{
|
{
|
||||||
@ -291,6 +311,11 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::reverse(argTypes.begin(), argTypes.end());
|
||||||
|
|
||||||
|
if (parameterTypes && parameterTypes->size() == argTypes.size())
|
||||||
|
for (size_t i = 0; i < parameterTypes->size(); ++i)
|
||||||
|
expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i]));
|
||||||
|
|
||||||
if (m_success)
|
if (m_success)
|
||||||
{
|
{
|
||||||
@ -306,7 +331,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
|
|
||||||
void AsmAnalyzer::operator()(If const& _if)
|
void AsmAnalyzer::operator()(If const& _if)
|
||||||
{
|
{
|
||||||
expectExpression(*_if.condition);
|
expectBoolExpression(*_if.condition);
|
||||||
|
|
||||||
(*this)(_if.body);
|
(*this)(_if.body);
|
||||||
}
|
}
|
||||||
@ -315,32 +340,15 @@ void AsmAnalyzer::operator()(Switch const& _switch)
|
|||||||
{
|
{
|
||||||
yulAssert(_switch.expression, "");
|
yulAssert(_switch.expression, "");
|
||||||
|
|
||||||
expectExpression(*_switch.expression);
|
YulString valueType = expectExpression(*_switch.expression);
|
||||||
|
|
||||||
YulString caseType;
|
|
||||||
bool mismatchingTypes = false;
|
|
||||||
for (auto const& _case: _switch.cases)
|
|
||||||
if (_case.value)
|
|
||||||
{
|
|
||||||
if (caseType.empty())
|
|
||||||
caseType = _case.value->type;
|
|
||||||
else if (caseType != _case.value->type)
|
|
||||||
{
|
|
||||||
mismatchingTypes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mismatchingTypes)
|
|
||||||
m_errorReporter.typeError(
|
|
||||||
_switch.location,
|
|
||||||
"Switch cases have non-matching types."
|
|
||||||
);
|
|
||||||
|
|
||||||
set<u256> cases;
|
set<u256> cases;
|
||||||
for (auto const& _case: _switch.cases)
|
for (auto const& _case: _switch.cases)
|
||||||
{
|
{
|
||||||
if (_case.value)
|
if (_case.value)
|
||||||
{
|
{
|
||||||
|
expectType(valueType, _case.value->type, _case.value->location);
|
||||||
|
|
||||||
// We cannot use "expectExpression" here because *_case.value is not an
|
// We cannot use "expectExpression" here because *_case.value is not an
|
||||||
// Expression and would be converted to an Expression otherwise.
|
// Expression and would be converted to an Expression otherwise.
|
||||||
(*this)(*_case.value);
|
(*this)(*_case.value);
|
||||||
@ -366,8 +374,7 @@ void AsmAnalyzer::operator()(ForLoop const& _for)
|
|||||||
// condition, the body and the post part inside.
|
// condition, the body and the post part inside.
|
||||||
m_currentScope = &scope(&_for.pre);
|
m_currentScope = &scope(&_for.pre);
|
||||||
|
|
||||||
expectExpression(*_for.condition);
|
expectBoolExpression(*_for.condition);
|
||||||
|
|
||||||
// backup outer for-loop & create new state
|
// backup outer for-loop & create new state
|
||||||
auto outerForLoop = m_currentForLoop;
|
auto outerForLoop = m_currentForLoop;
|
||||||
m_currentForLoop = &_for;
|
m_currentForLoop = &_for;
|
||||||
@ -403,10 +410,24 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr)
|
|||||||
return types.empty() ? m_dialect.defaultType : types.front();
|
return types.empty() ? m_dialect.defaultType : types.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
void AsmAnalyzer::expectBoolExpression(Expression const& _expr)
|
||||||
|
{
|
||||||
|
YulString type = expectExpression(_expr);
|
||||||
|
if (type != m_dialect.boolType)
|
||||||
|
typeError(locationOf(_expr),
|
||||||
|
"Expected a value of boolean type \"" +
|
||||||
|
m_dialect.boolType.str() +
|
||||||
|
"\" but got \"" +
|
||||||
|
type.str() +
|
||||||
|
"\""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueType)
|
||||||
{
|
{
|
||||||
yulAssert(!_variable.name.empty(), "");
|
yulAssert(!_variable.name.empty(), "");
|
||||||
size_t numErrorsBefore = m_errorReporter.errors().size();
|
size_t numErrorsBefore = m_errorReporter.errors().size();
|
||||||
|
YulString const* variableType = nullptr;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name))
|
||||||
{
|
{
|
||||||
@ -418,6 +439,8 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
|||||||
_variable.location,
|
_variable.location,
|
||||||
"Variable " + _variable.name.str() + " used before it was declared."
|
"Variable " + _variable.name.str() + " used before it was declared."
|
||||||
);
|
);
|
||||||
|
else
|
||||||
|
variableType = &std::get<Scope::Variable>(*var).type;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
else if (m_resolver)
|
else if (m_resolver)
|
||||||
@ -427,6 +450,7 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
|||||||
if (variableSize != size_t(-1))
|
if (variableSize != size_t(-1))
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
|
variableType = &m_dialect.defaultType;
|
||||||
yulAssert(variableSize == 1, "Invalid stack size of external reference.");
|
yulAssert(variableSize == 1, "Invalid stack size of external reference.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -438,6 +462,17 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable)
|
|||||||
if (numErrorsBefore == m_errorReporter.errors().size())
|
if (numErrorsBefore == m_errorReporter.errors().size())
|
||||||
declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
declarationError(_variable.location, "Variable not found or variable not lvalue.");
|
||||||
}
|
}
|
||||||
|
if (variableType && *variableType != _valueType)
|
||||||
|
typeError(_variable.location,
|
||||||
|
"Assigning a value of type \"" +
|
||||||
|
_valueType.str() +
|
||||||
|
"\" to a variable of type \"" +
|
||||||
|
variableType->str() +
|
||||||
|
"\"."
|
||||||
|
);
|
||||||
|
|
||||||
|
if (m_success)
|
||||||
|
yulAssert(variableType, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope& AsmAnalyzer::scope(Block const* _block)
|
Scope& AsmAnalyzer::scope(Block const* _block)
|
||||||
@ -447,15 +482,28 @@ Scope& AsmAnalyzer::scope(Block const* _block)
|
|||||||
yulAssert(scopePtr, "Scope requested but not present.");
|
yulAssert(scopePtr, "Scope requested but not present.");
|
||||||
return *scopePtr;
|
return *scopePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location)
|
void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
if (!_type.empty() && !m_dialect.types.count(_type))
|
if (!m_dialect.types.count(_type))
|
||||||
m_errorReporter.typeError(
|
typeError(
|
||||||
_location,
|
_location,
|
||||||
"\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)."
|
"\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, SourceLocation const& _location)
|
||||||
|
{
|
||||||
|
if (_expectedType != _givenType)
|
||||||
|
typeError(_location,
|
||||||
|
"Expected a value of type \"" +
|
||||||
|
_expectedType.str() +
|
||||||
|
"\" but got \"" +
|
||||||
|
_givenType.str() +
|
||||||
|
"\""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
||||||
{
|
{
|
||||||
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
|
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
|
||||||
|
@ -96,13 +96,18 @@ private:
|
|||||||
/// Visits the expression, expects that it evaluates to exactly one value and
|
/// Visits the expression, expects that it evaluates to exactly one value and
|
||||||
/// returns the type. Reports errors on errors and returns the default type.
|
/// returns the type. Reports errors on errors and returns the default type.
|
||||||
YulString expectExpression(Expression const& _expr);
|
YulString expectExpression(Expression const& _expr);
|
||||||
|
/// Vists the expression and expects it to return a single boolean value.
|
||||||
|
/// Reports an error otherwise.
|
||||||
|
void expectBoolExpression(Expression const& _expr);
|
||||||
bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location);
|
bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location);
|
||||||
|
|
||||||
/// Verifies that a variable to be assigned to exists and can be assigned to.
|
/// Verifies that a variable to be assigned to exists, can be assigned to
|
||||||
void checkAssignment(Identifier const& _variable);
|
/// and has the same type as the value.
|
||||||
|
void checkAssignment(Identifier const& _variable, YulString _valueType);
|
||||||
|
|
||||||
Scope& scope(Block const* _block);
|
Scope& scope(Block const* _block);
|
||||||
void expectValidType(YulString _type, langutil::SourceLocation const& _location);
|
void expectValidType(YulString _type, langutil::SourceLocation const& _location);
|
||||||
|
void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location);
|
||||||
bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location);
|
bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location);
|
||||||
bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
||||||
|
|
||||||
|
@ -154,6 +154,8 @@ add_library(yul
|
|||||||
optimiser/Suite.h
|
optimiser/Suite.h
|
||||||
optimiser/SyntacticalEquality.cpp
|
optimiser/SyntacticalEquality.cpp
|
||||||
optimiser/SyntacticalEquality.h
|
optimiser/SyntacticalEquality.h
|
||||||
|
optimiser/TypeInfo.cpp
|
||||||
|
optimiser/TypeInfo.h
|
||||||
optimiser/UnusedPruner.cpp
|
optimiser/UnusedPruner.cpp
|
||||||
optimiser/UnusedPruner.h
|
optimiser/UnusedPruner.h
|
||||||
optimiser/VarDeclInitializer.cpp
|
optimiser/VarDeclInitializer.cpp
|
||||||
|
@ -19,9 +19,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
|
Literal Dialect::zeroLiteralForType(solidity::yul::YulString _type) const
|
||||||
|
{
|
||||||
|
if (_type == boolType && _type != defaultType)
|
||||||
|
return {SourceLocation{}, LiteralKind::Boolean, "false"_yulstring, _type};
|
||||||
|
return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const
|
||||||
|
{
|
||||||
|
if (_kind == LiteralKind::Boolean)
|
||||||
|
return _type == boolType;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Dialect const& Dialect::yulDeprecated()
|
Dialect const& Dialect::yulDeprecated()
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,8 @@ namespace solidity::yul
|
|||||||
|
|
||||||
class YulString;
|
class YulString;
|
||||||
using Type = YulString;
|
using Type = YulString;
|
||||||
|
enum class LiteralKind;
|
||||||
|
struct Literal;
|
||||||
|
|
||||||
struct BuiltinFunction
|
struct BuiltinFunction
|
||||||
{
|
{
|
||||||
@ -52,15 +54,21 @@ struct Dialect: boost::noncopyable
|
|||||||
YulString defaultType;
|
YulString defaultType;
|
||||||
/// Type used for the literals "true" and "false".
|
/// Type used for the literals "true" and "false".
|
||||||
YulString boolType;
|
YulString boolType;
|
||||||
std::set<YulString> types;
|
std::set<YulString> types = {{}};
|
||||||
|
|
||||||
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
||||||
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
|
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
|
||||||
|
|
||||||
virtual BuiltinFunction const* discardFunction() const { return nullptr; }
|
virtual BuiltinFunction const* discardFunction(YulString /* _type */) const { return nullptr; }
|
||||||
virtual BuiltinFunction const* equalityFunction() const { return nullptr; }
|
virtual BuiltinFunction const* equalityFunction(YulString /* _type */) const { return nullptr; }
|
||||||
virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; }
|
virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; }
|
||||||
|
|
||||||
|
/// Check whether the given type is legal for the given literal value.
|
||||||
|
/// Should only be called if the type exists in the dialect at all.
|
||||||
|
virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const;
|
||||||
|
|
||||||
|
virtual Literal zeroLiteralForType(YulString _type) const;
|
||||||
|
|
||||||
virtual std::set<YulString> fixedFunctionNames() const { return {}; }
|
virtual std::set<YulString> fixedFunctionNames() const { return {}; }
|
||||||
|
|
||||||
Dialect() = default;
|
Dialect() = default;
|
||||||
|
@ -290,6 +290,28 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
|
|||||||
m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring};
|
m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuiltinFunctionForEVM const* EVMDialectTyped::discardFunction(YulString _type) const
|
||||||
|
{
|
||||||
|
if (_type == "bool"_yulstring)
|
||||||
|
return builtin("popbool"_yulstring);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yulAssert(_type == defaultType, "");
|
||||||
|
return builtin("pop"_yulstring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinFunctionForEVM const* EVMDialectTyped::equalityFunction(YulString _type) const
|
||||||
|
{
|
||||||
|
if (_type == "bool"_yulstring)
|
||||||
|
return nullptr;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yulAssert(_type == defaultType, "");
|
||||||
|
return builtin("eq"_yulstring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EVMDialectTyped const& EVMDialectTyped::instance(langutil::EVMVersion _version)
|
EVMDialectTyped const& EVMDialectTyped::instance(langutil::EVMVersion _version)
|
||||||
{
|
{
|
||||||
static map<langutil::EVMVersion, unique_ptr<EVMDialectTyped const>> dialects;
|
static map<langutil::EVMVersion, unique_ptr<EVMDialectTyped const>> dialects;
|
||||||
|
@ -68,8 +68,8 @@ struct EVMDialect: public Dialect
|
|||||||
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
|
||||||
BuiltinFunctionForEVM const* builtin(YulString _name) const override;
|
BuiltinFunctionForEVM const* builtin(YulString _name) const override;
|
||||||
|
|
||||||
BuiltinFunctionForEVM const* discardFunction() const override { return builtin("pop"_yulstring); }
|
BuiltinFunctionForEVM const* discardFunction(YulString /*_type*/) const override { return builtin("pop"_yulstring); }
|
||||||
BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); }
|
BuiltinFunctionForEVM const* equalityFunction(YulString /*_type*/) const override { return builtin("eq"_yulstring); }
|
||||||
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); }
|
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); }
|
||||||
|
|
||||||
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
|
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
|
||||||
@ -102,6 +102,10 @@ struct EVMDialectTyped: public EVMDialect
|
|||||||
/// Constructor, should only be used internally. Use the factory function below.
|
/// Constructor, should only be used internally. Use the factory function below.
|
||||||
EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess);
|
EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess);
|
||||||
|
|
||||||
|
BuiltinFunctionForEVM const* discardFunction(YulString _type) const override;
|
||||||
|
BuiltinFunctionForEVM const* equalityFunction(YulString _type) const override;
|
||||||
|
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("not"_yulstring); }
|
||||||
|
|
||||||
static EVMDialectTyped const& instance(langutil::EVMVersion _version);
|
static EVMDialectTyped const& instance(langutil::EVMVersion _version);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1235,7 +1235,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
|||||||
MainFunction{}(ast);
|
MainFunction{}(ast);
|
||||||
ForLoopConditionIntoBody::run(context, ast);
|
ForLoopConditionIntoBody::run(context, ast);
|
||||||
ExpressionSplitter::run(context, ast);
|
ExpressionSplitter::run(context, ast);
|
||||||
WordSizeTransform::run(m_dialect, WasmDialect::instance().defaultType, ast, nameDispenser);
|
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);
|
||||||
|
|
||||||
NameDisplacer{nameDispenser, m_polyfillFunctions}(ast);
|
NameDisplacer{nameDispenser, m_polyfillFunctions}(ast);
|
||||||
for (auto const& st: m_polyfill->statements)
|
for (auto const& st: m_polyfill->statements)
|
||||||
@ -1251,8 +1251,12 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
|||||||
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames());
|
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames());
|
||||||
if (!analyzer.analyze(*ret.code))
|
if (!analyzer.analyze(*ret.code))
|
||||||
{
|
{
|
||||||
// TODO the errors here are "wrong" because they have invalid source references!
|
string message = "Invalid code generated after EVM to wasm translation.\n";
|
||||||
string message;
|
message += "Note that the source locations in the errors below will reference the original, not the translated code.\n";
|
||||||
|
message += "Translated code:\n";
|
||||||
|
message += "----------------------------------\n";
|
||||||
|
message += ret.toString(&WasmDialect::instance());
|
||||||
|
message += "----------------------------------\n";
|
||||||
for (auto const& err: errors)
|
for (auto const& err: errors)
|
||||||
message += langutil::SourceReferenceFormatter::formatErrorInformation(*err);
|
message += langutil::SourceReferenceFormatter::formatErrorInformation(*err);
|
||||||
yulAssert(false, message);
|
yulAssert(false, message);
|
||||||
|
@ -91,8 +91,9 @@ WasmDialect::WasmDialect()
|
|||||||
m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true;
|
m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true;
|
||||||
|
|
||||||
// Drop is actually overloaded for all types, but Yul does not support that.
|
// Drop is actually overloaded for all types, but Yul does not support that.
|
||||||
// We could introduce "i32.drop".
|
// Because of that, we introduce "i32.drop".
|
||||||
addFunction("drop", {i64}, {});
|
addFunction("drop", {i64}, {});
|
||||||
|
addFunction("i32.drop", {i32}, {});
|
||||||
|
|
||||||
addFunction("nop", {}, {});
|
addFunction("nop", {}, {});
|
||||||
addFunction("unreachable", {}, {}, false);
|
addFunction("unreachable", {}, {}, false);
|
||||||
@ -114,6 +115,22 @@ BuiltinFunction const* WasmDialect::builtin(YulString _name) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuiltinFunction const* WasmDialect::discardFunction(YulString _type) const
|
||||||
|
{
|
||||||
|
if (_type == "i32"_yulstring)
|
||||||
|
return builtin("i32.drop"_yulstring);
|
||||||
|
yulAssert(_type == "i64"_yulstring, "");
|
||||||
|
return builtin("drop"_yulstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuiltinFunction const* WasmDialect::equalityFunction(YulString _type) const
|
||||||
|
{
|
||||||
|
if (_type == "i32"_yulstring)
|
||||||
|
return builtin("i32.eq"_yulstring);
|
||||||
|
yulAssert(_type == "i64"_yulstring, "");
|
||||||
|
return builtin("i64.eq"_yulstring);
|
||||||
|
}
|
||||||
|
|
||||||
WasmDialect const& WasmDialect::instance()
|
WasmDialect const& WasmDialect::instance()
|
||||||
{
|
{
|
||||||
static std::unique_ptr<WasmDialect> dialect;
|
static std::unique_ptr<WasmDialect> dialect;
|
||||||
|
@ -35,19 +35,19 @@ struct Object;
|
|||||||
/**
|
/**
|
||||||
* Yul dialect for Wasm as a backend.
|
* Yul dialect for Wasm as a backend.
|
||||||
*
|
*
|
||||||
* Builtin functions are a subset of the wasm instructions, always implicitly assuming
|
* Builtin functions are a subset of the wasm instructions.
|
||||||
* unsigned 64 bit types.
|
*
|
||||||
|
* There is a builtin function `i32.drop` that takes an i32, while `drop` takes i64.
|
||||||
*
|
*
|
||||||
* !This is subject to changes!
|
|
||||||
*/
|
*/
|
||||||
struct WasmDialect: public Dialect
|
struct WasmDialect: public Dialect
|
||||||
{
|
{
|
||||||
WasmDialect();
|
WasmDialect();
|
||||||
|
|
||||||
BuiltinFunction const* builtin(YulString _name) const override;
|
BuiltinFunction const* builtin(YulString _name) const override;
|
||||||
BuiltinFunction const* discardFunction() const override { return builtin("drop"_yulstring); }
|
BuiltinFunction const* discardFunction(YulString _type) const override;
|
||||||
BuiltinFunction const* equalityFunction() const override { return builtin("i64.eq"_yulstring); }
|
BuiltinFunction const* equalityFunction(YulString _type) const override;
|
||||||
BuiltinFunction const* booleanNegationFunction() const override { return builtin("i64.eqz"_yulstring); }
|
BuiltinFunction const* booleanNegationFunction() const override { return builtin("i32.eqz"_yulstring); }
|
||||||
|
|
||||||
std::set<YulString> fixedFunctionNames() const override { return {"main"_yulstring}; }
|
std::set<YulString> fixedFunctionNames() const override { return {"main"_yulstring}; }
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ void WordSizeTransform::operator()(FunctionCall& _fc)
|
|||||||
if (fun->literalArguments)
|
if (fun->literalArguments)
|
||||||
{
|
{
|
||||||
for (Expression& arg: _fc.arguments)
|
for (Expression& arg: _fc.arguments)
|
||||||
get<Literal>(arg).type = m_defaultType;
|
get<Literal>(arg).type = m_targetDialect.defaultType;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +106,17 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
ret.push_back(VariableDeclaration{
|
ret.push_back(VariableDeclaration{
|
||||||
varDecl.location,
|
varDecl.location,
|
||||||
{TypedName{varDecl.location, newLhs[i], m_defaultType}},
|
{TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}},
|
||||||
make_unique<Expression>(Literal{locationOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, m_defaultType})
|
make_unique<Expression>(Literal{
|
||||||
|
locationOf(*varDecl.value),
|
||||||
|
LiteralKind::Number,
|
||||||
|
"0"_yulstring,
|
||||||
|
m_targetDialect.defaultType
|
||||||
|
})
|
||||||
});
|
});
|
||||||
ret.push_back(VariableDeclaration{
|
ret.push_back(VariableDeclaration{
|
||||||
varDecl.location,
|
varDecl.location,
|
||||||
{TypedName{varDecl.location, newLhs[3], m_defaultType}},
|
{TypedName{varDecl.location, newLhs[3], m_targetDialect.defaultType}},
|
||||||
std::move(varDecl.value)
|
std::move(varDecl.value)
|
||||||
});
|
});
|
||||||
return {std::move(ret)};
|
return {std::move(ret)};
|
||||||
@ -133,7 +138,7 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
ret.push_back(
|
ret.push_back(
|
||||||
VariableDeclaration{
|
VariableDeclaration{
|
||||||
varDecl.location,
|
varDecl.location,
|
||||||
{TypedName{varDecl.location, newLhs[i], m_defaultType}},
|
{TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}},
|
||||||
std::move(newRhs[i])
|
std::move(newRhs[i])
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -163,7 +168,12 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
ret.push_back(Assignment{
|
ret.push_back(Assignment{
|
||||||
assignment.location,
|
assignment.location,
|
||||||
{Identifier{assignment.location, newLhs[i]}},
|
{Identifier{assignment.location, newLhs[i]}},
|
||||||
make_unique<Expression>(Literal{locationOf(*assignment.value), LiteralKind::Number, "0"_yulstring, m_defaultType})
|
make_unique<Expression>(Literal{
|
||||||
|
locationOf(*assignment.value),
|
||||||
|
LiteralKind::Number,
|
||||||
|
"0"_yulstring,
|
||||||
|
m_targetDialect.defaultType
|
||||||
|
})
|
||||||
});
|
});
|
||||||
ret.push_back(Assignment{
|
ret.push_back(Assignment{
|
||||||
assignment.location,
|
assignment.location,
|
||||||
@ -209,14 +219,25 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
|
|
||||||
void WordSizeTransform::run(
|
void WordSizeTransform::run(
|
||||||
Dialect const& _inputDialect,
|
Dialect const& _inputDialect,
|
||||||
YulString _targetDefaultType,
|
Dialect const& _targetDialect,
|
||||||
Block& _ast,
|
Block& _ast,
|
||||||
NameDispenser& _nameDispenser
|
NameDispenser& _nameDispenser
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Free the name `or_bool`.
|
// Free the name `or_bool`.
|
||||||
NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast);
|
NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast);
|
||||||
WordSizeTransform{_inputDialect, _nameDispenser, _targetDefaultType}(_ast);
|
WordSizeTransform{_inputDialect, _targetDialect, _nameDispenser}(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
WordSizeTransform::WordSizeTransform(
|
||||||
|
Dialect const& _inputDialect,
|
||||||
|
Dialect const& _targetDialect,
|
||||||
|
NameDispenser& _nameDispenser
|
||||||
|
):
|
||||||
|
m_inputDialect(_inputDialect),
|
||||||
|
m_targetDialect(_targetDialect),
|
||||||
|
m_nameDispenser(_nameDispenser)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList)
|
void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList)
|
||||||
@ -227,7 +248,7 @@ void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList)
|
|||||||
{
|
{
|
||||||
TypedNameList ret;
|
TypedNameList ret;
|
||||||
for (auto newName: generateU64IdentifierNames(_n.name))
|
for (auto newName: generateU64IdentifierNames(_n.name))
|
||||||
ret.emplace_back(TypedName{_n.location, newName, m_defaultType});
|
ret.emplace_back(TypedName{_n.location, newName, m_targetDialect.defaultType});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -291,7 +312,7 @@ vector<Statement> WordSizeTransform::handleSwitchInternal(
|
|||||||
|
|
||||||
for (auto& c: cases)
|
for (auto& c: cases)
|
||||||
{
|
{
|
||||||
Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_defaultType};
|
Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_targetDialect.defaultType};
|
||||||
ret.cases.emplace_back(Case{
|
ret.cases.emplace_back(Case{
|
||||||
c.second.front().location,
|
c.second.front().location,
|
||||||
make_unique<Literal>(std::move(label)),
|
make_unique<Literal>(std::move(label)),
|
||||||
@ -312,7 +333,7 @@ vector<Statement> WordSizeTransform::handleSwitchInternal(
|
|||||||
Assignment{
|
Assignment{
|
||||||
_location,
|
_location,
|
||||||
{{_location, _runDefaultFlag}},
|
{{_location, _runDefaultFlag}},
|
||||||
make_unique<Expression>(Literal{_location, LiteralKind::Number, "1"_yulstring, m_defaultType})
|
make_unique<Expression>(Literal{_location, LiteralKind::Boolean, "true"_yulstring, m_targetDialect.boolType})
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
});
|
});
|
||||||
@ -337,7 +358,7 @@ std::vector<Statement> WordSizeTransform::handleSwitch(Switch& _switch)
|
|||||||
_switch.cases.pop_back();
|
_switch.cases.pop_back();
|
||||||
ret.emplace_back(VariableDeclaration{
|
ret.emplace_back(VariableDeclaration{
|
||||||
_switch.location,
|
_switch.location,
|
||||||
{TypedName{_switch.location, runDefaultFlag, m_defaultType}},
|
{TypedName{_switch.location, runDefaultFlag, m_targetDialect.boolType}},
|
||||||
{}
|
{}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -392,7 +413,7 @@ array<unique_ptr<Expression>, 4> WordSizeTransform::expandValue(Expression const
|
|||||||
lit.location,
|
lit.location,
|
||||||
LiteralKind::Number,
|
LiteralKind::Number,
|
||||||
YulString(currentVal.str()),
|
YulString(currentVal.str()),
|
||||||
m_defaultType
|
m_targetDialect.defaultType
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ namespace solidity::yul
|
|||||||
* take four times the parameters and each of type u64.
|
* take four times the parameters and each of type u64.
|
||||||
* In addition, it uses a single other builtin function called `or_bool` that
|
* In addition, it uses a single other builtin function called `or_bool` that
|
||||||
* takes four u64 parameters and is supposed to return the logical disjunction
|
* takes four u64 parameters and is supposed to return the logical disjunction
|
||||||
* of them as a u64 value. If this name is already used somewhere, it is renamed.
|
* of them as a i32 value. If this name is already used somewhere, it is renamed.
|
||||||
*
|
*
|
||||||
* Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter
|
* Prerequisite: Disambiguator, ForLoopConditionIntoBody, ExpressionSplitter
|
||||||
*/
|
*/
|
||||||
@ -69,7 +69,7 @@ public:
|
|||||||
|
|
||||||
static void run(
|
static void run(
|
||||||
Dialect const& _inputDialect,
|
Dialect const& _inputDialect,
|
||||||
YulString _targetDefaultType,
|
Dialect const& _targetDialect,
|
||||||
Block& _ast,
|
Block& _ast,
|
||||||
NameDispenser& _nameDispenser
|
NameDispenser& _nameDispenser
|
||||||
);
|
);
|
||||||
@ -77,13 +77,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
explicit WordSizeTransform(
|
explicit WordSizeTransform(
|
||||||
Dialect const& _inputDialect,
|
Dialect const& _inputDialect,
|
||||||
NameDispenser& _nameDispenser,
|
Dialect const& _targetDialect,
|
||||||
YulString _defaultType
|
NameDispenser& _nameDispenser
|
||||||
):
|
);
|
||||||
m_inputDialect(_inputDialect),
|
|
||||||
m_nameDispenser(_nameDispenser),
|
|
||||||
m_defaultType(_defaultType)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
void rewriteVarDeclList(std::vector<TypedName>&);
|
void rewriteVarDeclList(std::vector<TypedName>&);
|
||||||
void rewriteIdentifierList(std::vector<Identifier>&);
|
void rewriteIdentifierList(std::vector<Identifier>&);
|
||||||
@ -103,8 +99,8 @@ private:
|
|||||||
std::vector<Expression> expandValueToVector(Expression const& _e);
|
std::vector<Expression> expandValueToVector(Expression const& _e);
|
||||||
|
|
||||||
Dialect const& m_inputDialect;
|
Dialect const& m_inputDialect;
|
||||||
|
Dialect const& m_targetDialect;
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
YulString m_defaultType;
|
|
||||||
/// maps original u256 variable's name to corresponding u64 variables' names
|
/// maps original u256 variable's name to corresponding u64 variables' names
|
||||||
std::map<YulString, std::array<YulString, 4>> m_variableMapping;
|
std::map<YulString, std::array<YulString, 4>> m_variableMapping;
|
||||||
};
|
};
|
||||||
|
@ -77,12 +77,7 @@ void ConditionalSimplifier::operator()(Block& _block)
|
|||||||
Assignment{
|
Assignment{
|
||||||
location,
|
location,
|
||||||
{Identifier{location, condition}},
|
{Identifier{location, condition}},
|
||||||
make_unique<Expression>(Literal{
|
make_unique<Expression>(m_dialect.zeroLiteralForType(m_dialect.boolType))
|
||||||
location,
|
|
||||||
LiteralKind::Number,
|
|
||||||
"0"_yulstring,
|
|
||||||
{}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/optimiser/OptimiserStep.h>
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/optimiser/TypeInfo.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
@ -38,14 +39,13 @@ namespace
|
|||||||
|
|
||||||
ExpressionStatement makeDiscardCall(
|
ExpressionStatement makeDiscardCall(
|
||||||
langutil::SourceLocation const& _location,
|
langutil::SourceLocation const& _location,
|
||||||
Dialect const& _dialect,
|
BuiltinFunction const& _discardFunction,
|
||||||
Expression&& _expression
|
Expression&& _expression
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
yulAssert(_dialect.discardFunction(), "No discard function available.");
|
|
||||||
return {_location, FunctionCall{
|
return {_location, FunctionCall{
|
||||||
_location,
|
_location,
|
||||||
Identifier{_location, _dialect.discardFunction()->name},
|
Identifier{_location, _discardFunction.name},
|
||||||
{std::move(_expression)}
|
{std::move(_expression)}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -74,62 +74,12 @@ void removeEmptyCasesFromSwitch(Switch& _switchStmt)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionalStatements reduceNoCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
|
|
||||||
{
|
|
||||||
yulAssert(_switchStmt.cases.empty(), "Expected no case!");
|
|
||||||
if (!_dialect.discardFunction())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto loc = locationOf(*_switchStmt.expression);
|
|
||||||
|
|
||||||
return make_vector<Statement>(makeDiscardCall(
|
|
||||||
loc,
|
|
||||||
_dialect,
|
|
||||||
std::move(*_switchStmt.expression)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
|
|
||||||
{
|
|
||||||
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
|
|
||||||
|
|
||||||
auto& switchCase = _switchStmt.cases.front();
|
|
||||||
auto loc = locationOf(*_switchStmt.expression);
|
|
||||||
if (switchCase.value)
|
|
||||||
{
|
|
||||||
if (!_dialect.equalityFunction())
|
|
||||||
return {};
|
|
||||||
return make_vector<Statement>(If{
|
|
||||||
std::move(_switchStmt.location),
|
|
||||||
make_unique<Expression>(FunctionCall{
|
|
||||||
loc,
|
|
||||||
Identifier{loc, _dialect.equalityFunction()->name},
|
|
||||||
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
|
|
||||||
}),
|
|
||||||
std::move(switchCase.body)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_dialect.discardFunction())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return make_vector<Statement>(
|
|
||||||
makeDiscardCall(
|
|
||||||
loc,
|
|
||||||
_dialect,
|
|
||||||
std::move(*_switchStmt.expression)
|
|
||||||
),
|
|
||||||
std::move(switchCase.body)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
{
|
{
|
||||||
ControlFlowSimplifier{_context.dialect}(_ast);
|
TypeInfo typeInfo(_context.dialect, _ast);
|
||||||
|
ControlFlowSimplifier{_context.dialect, typeInfo}(_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControlFlowSimplifier::operator()(Block& _block)
|
void ControlFlowSimplifier::operator()(Block& _block)
|
||||||
@ -194,12 +144,12 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
|||||||
GenericVisitor visitor{
|
GenericVisitor visitor{
|
||||||
VisitorFallback<OptionalStatements>{},
|
VisitorFallback<OptionalStatements>{},
|
||||||
[&](If& _ifStmt) -> OptionalStatements {
|
[&](If& _ifStmt) -> OptionalStatements {
|
||||||
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction())
|
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction(m_dialect.boolType))
|
||||||
{
|
{
|
||||||
OptionalStatements s = vector<Statement>{};
|
OptionalStatements s = vector<Statement>{};
|
||||||
s->emplace_back(makeDiscardCall(
|
s->emplace_back(makeDiscardCall(
|
||||||
_ifStmt.location,
|
_ifStmt.location,
|
||||||
m_dialect,
|
*m_dialect.discardFunction(m_dialect.boolType),
|
||||||
std::move(*_ifStmt.condition)
|
std::move(*_ifStmt.condition)
|
||||||
));
|
));
|
||||||
return s;
|
return s;
|
||||||
@ -211,9 +161,9 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
|||||||
removeEmptyCasesFromSwitch(_switchStmt);
|
removeEmptyCasesFromSwitch(_switchStmt);
|
||||||
|
|
||||||
if (_switchStmt.cases.empty())
|
if (_switchStmt.cases.empty())
|
||||||
return reduceNoCaseSwitch(m_dialect, _switchStmt);
|
return reduceNoCaseSwitch(_switchStmt);
|
||||||
else if (_switchStmt.cases.size() == 1)
|
else if (_switchStmt.cases.size() == 1)
|
||||||
return reduceSingleCaseSwitch(m_dialect, _switchStmt);
|
return reduceSingleCaseSwitch(_switchStmt);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -231,3 +181,58 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const
|
||||||
|
{
|
||||||
|
yulAssert(_switchStmt.cases.empty(), "Expected no case!");
|
||||||
|
BuiltinFunction const* discardFunction =
|
||||||
|
m_dialect.discardFunction(m_typeInfo.typeOf(*_switchStmt.expression));
|
||||||
|
if (!discardFunction)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto loc = locationOf(*_switchStmt.expression);
|
||||||
|
|
||||||
|
return make_vector<Statement>(makeDiscardCall(
|
||||||
|
loc,
|
||||||
|
*discardFunction,
|
||||||
|
std::move(*_switchStmt.expression)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const
|
||||||
|
{
|
||||||
|
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
|
||||||
|
|
||||||
|
auto& switchCase = _switchStmt.cases.front();
|
||||||
|
auto loc = locationOf(*_switchStmt.expression);
|
||||||
|
YulString type = m_typeInfo.typeOf(*_switchStmt.expression);
|
||||||
|
if (switchCase.value)
|
||||||
|
{
|
||||||
|
if (!m_dialect.equalityFunction(type))
|
||||||
|
return {};
|
||||||
|
return make_vector<Statement>(If{
|
||||||
|
std::move(_switchStmt.location),
|
||||||
|
make_unique<Expression>(FunctionCall{
|
||||||
|
loc,
|
||||||
|
Identifier{loc, m_dialect.equalityFunction(type)->name},
|
||||||
|
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
|
||||||
|
}),
|
||||||
|
std::move(switchCase.body)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!m_dialect.discardFunction(type))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return make_vector<Statement>(
|
||||||
|
makeDiscardCall(
|
||||||
|
loc,
|
||||||
|
*m_dialect.discardFunction(type),
|
||||||
|
std::move(*_switchStmt.expression)
|
||||||
|
),
|
||||||
|
std::move(switchCase.body)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ namespace solidity::yul
|
|||||||
{
|
{
|
||||||
struct Dialect;
|
struct Dialect;
|
||||||
struct OptimiserStepContext;
|
struct OptimiserStepContext;
|
||||||
|
class TypeInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplifies several control-flow structures:
|
* Simplifies several control-flow structures:
|
||||||
@ -61,11 +62,18 @@ public:
|
|||||||
void visit(Statement& _st) override;
|
void visit(Statement& _st) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
|
ControlFlowSimplifier(Dialect const& _dialect, TypeInfo const& _typeInfo):
|
||||||
|
m_dialect(_dialect),
|
||||||
|
m_typeInfo(_typeInfo)
|
||||||
|
{}
|
||||||
|
|
||||||
void simplify(std::vector<Statement>& _statements);
|
void simplify(std::vector<Statement>& _statements);
|
||||||
|
|
||||||
|
std::optional<std::vector<Statement>> reduceNoCaseSwitch(Switch& _switchStmt) const;
|
||||||
|
std::optional<std::vector<Statement>> reduceSingleCaseSwitch(Switch& _switchStmt) const;
|
||||||
|
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
TypeInfo const& m_typeInfo;
|
||||||
size_t m_numBreakStatements = 0;
|
size_t m_numBreakStatements = 0;
|
||||||
size_t m_numContinueStatements = 0;
|
size_t m_numContinueStatements = 0;
|
||||||
};
|
};
|
||||||
|
@ -23,11 +23,13 @@
|
|||||||
|
|
||||||
#include <libyul/optimiser/ASTWalker.h>
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
#include <libyul/optimiser/OptimiserStep.h>
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/optimiser/TypeInfo.h>
|
||||||
|
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
@ -39,7 +41,8 @@ using namespace solidity::langutil;
|
|||||||
|
|
||||||
void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
|
void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
{
|
{
|
||||||
ExpressionSplitter{_context.dialect, _context.dispenser}(_ast);
|
TypeInfo typeInfo(_context.dialect, _ast);
|
||||||
|
ExpressionSplitter{_context.dialect, _context.dispenser, typeInfo}(_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionSplitter::operator()(FunctionCall& _funCall)
|
void ExpressionSplitter::operator()(FunctionCall& _funCall)
|
||||||
@ -103,10 +106,13 @@ void ExpressionSplitter::outlineExpression(Expression& _expr)
|
|||||||
|
|
||||||
SourceLocation location = locationOf(_expr);
|
SourceLocation location = locationOf(_expr);
|
||||||
YulString var = m_nameDispenser.newName({});
|
YulString var = m_nameDispenser.newName({});
|
||||||
|
YulString type = m_typeInfo.typeOf(_expr);
|
||||||
m_statementsToPrefix.emplace_back(VariableDeclaration{
|
m_statementsToPrefix.emplace_back(VariableDeclaration{
|
||||||
location,
|
location,
|
||||||
{{TypedName{location, var, {}}}},
|
{{TypedName{location, var, type}}},
|
||||||
make_unique<Expression>(std::move(_expr))
|
make_unique<Expression>(std::move(_expr))
|
||||||
});
|
});
|
||||||
_expr = Identifier{location, var};
|
_expr = Identifier{location, var};
|
||||||
|
m_typeInfo.setVariableType(var, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,9 +30,9 @@
|
|||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
|
|
||||||
class NameCollector;
|
|
||||||
struct Dialect;
|
struct Dialect;
|
||||||
struct OptimiserStepContext;
|
struct OptimiserStepContext;
|
||||||
|
class TypeInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimiser component that modifies an AST in place, turning complex
|
* Optimiser component that modifies an AST in place, turning complex
|
||||||
@ -68,8 +68,14 @@ public:
|
|||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser):
|
explicit ExpressionSplitter(
|
||||||
m_dialect(_dialect), m_nameDispenser(_nameDispenser)
|
Dialect const& _dialect,
|
||||||
|
NameDispenser& _nameDispenser,
|
||||||
|
TypeInfo& _typeInfo
|
||||||
|
):
|
||||||
|
m_dialect(_dialect),
|
||||||
|
m_nameDispenser(_nameDispenser),
|
||||||
|
m_typeInfo(_typeInfo)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/// Replaces the expression by a variable if it is a function call or functional
|
/// Replaces the expression by a variable if it is a function call or functional
|
||||||
@ -82,6 +88,7 @@ private:
|
|||||||
std::vector<Statement> m_statementsToPrefix;
|
std::vector<Statement> m_statementsToPrefix;
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
|
TypeInfo& m_typeInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,9 +56,9 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop)
|
|||||||
_forLoop.condition = make_unique<Expression>(
|
_forLoop.condition = make_unique<Expression>(
|
||||||
Literal {
|
Literal {
|
||||||
loc,
|
loc,
|
||||||
LiteralKind::Number,
|
LiteralKind::Boolean,
|
||||||
"1"_yulstring,
|
"true"_yulstring,
|
||||||
{}
|
m_dialect.boolType
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
@ -41,11 +42,11 @@ using namespace solidity::yul;
|
|||||||
|
|
||||||
void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
|
void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
{
|
{
|
||||||
FullInliner{_ast, _context.dispenser}.run();
|
FullInliner{_ast, _context.dispenser, _context.dialect}.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
|
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect):
|
||||||
m_ast(_ast), m_nameDispenser(_dispenser)
|
m_ast(_ast), m_nameDispenser(_dispenser), m_dialect(_dialect)
|
||||||
{
|
{
|
||||||
// Determine constants
|
// Determine constants
|
||||||
SSAValueTracker tracker;
|
SSAValueTracker tracker;
|
||||||
@ -139,7 +140,7 @@ void FullInliner::updateCodeSize(FunctionDefinition const& _fun)
|
|||||||
|
|
||||||
void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block)
|
void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block)
|
||||||
{
|
{
|
||||||
InlineModifier{*this, m_nameDispenser, _currentFunctionName}(_block);
|
InlineModifier{*this, m_nameDispenser, _currentFunctionName, m_dialect}(_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FullInliner::recursive(FunctionDefinition const& _fun) const
|
bool FullInliner::recursive(FunctionDefinition const& _fun) const
|
||||||
@ -198,7 +199,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
|
|||||||
if (_value)
|
if (_value)
|
||||||
varDecl.value = make_unique<Expression>(std::move(*_value));
|
varDecl.value = make_unique<Expression>(std::move(*_value));
|
||||||
else
|
else
|
||||||
varDecl.value = make_unique<Expression>(Literal{{}, LiteralKind::Number, YulString{"0"}, {}});
|
varDecl.value = make_unique<Expression>(m_dialect.zeroLiteralForType(varDecl.variables.front().type));
|
||||||
newStatements.emplace_back(std::move(varDecl));
|
newStatements.emplace_back(std::move(varDecl));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class FullInliner: public ASTModifier
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr char const* name{"FullInliner"};
|
static constexpr char const* name{"FullInliner"};
|
||||||
static void run(OptimiserStepContext&, Block& _ast);
|
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||||
|
|
||||||
/// Inlining heuristic.
|
/// Inlining heuristic.
|
||||||
/// @param _callSite the name of the function in which the function call is located.
|
/// @param _callSite the name of the function in which the function call is located.
|
||||||
@ -89,7 +89,7 @@ public:
|
|||||||
void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
|
void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FullInliner(Block& _ast, NameDispenser& _dispenser);
|
FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect);
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
void updateCodeSize(FunctionDefinition const& _fun);
|
void updateCodeSize(FunctionDefinition const& _fun);
|
||||||
@ -108,6 +108,7 @@ private:
|
|||||||
std::set<YulString> m_constants;
|
std::set<YulString> m_constants;
|
||||||
std::map<YulString, size_t> m_functionSizes;
|
std::map<YulString, size_t> m_functionSizes;
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
|
Dialect const& m_dialect;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,10 +118,11 @@ private:
|
|||||||
class InlineModifier: public ASTModifier
|
class InlineModifier: public ASTModifier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName):
|
InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName, Dialect const& _dialect):
|
||||||
m_currentFunction(std::move(_functionName)),
|
m_currentFunction(std::move(_functionName)),
|
||||||
m_driver(_driver),
|
m_driver(_driver),
|
||||||
m_nameDispenser(_nameDispenser)
|
m_nameDispenser(_nameDispenser),
|
||||||
|
m_dialect(_dialect)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
@ -132,6 +134,7 @@ private:
|
|||||||
YulString m_currentFunction;
|
YulString m_currentFunction;
|
||||||
FullInliner& m_driver;
|
FullInliner& m_driver;
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
|
Dialect const& m_dialect;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/TypeInfo.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
@ -42,8 +44,14 @@ namespace
|
|||||||
class IntroduceSSA: public ASTModifier
|
class IntroduceSSA: public ASTModifier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit IntroduceSSA(NameDispenser& _nameDispenser, set<YulString> const& _variablesToReplace):
|
explicit IntroduceSSA(
|
||||||
m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace)
|
NameDispenser& _nameDispenser,
|
||||||
|
set<YulString> const& _variablesToReplace,
|
||||||
|
TypeInfo& _typeInfo
|
||||||
|
):
|
||||||
|
m_nameDispenser(_nameDispenser),
|
||||||
|
m_variablesToReplace(_variablesToReplace),
|
||||||
|
m_typeInfo(_typeInfo)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
@ -51,6 +59,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
set<YulString> const& m_variablesToReplace;
|
set<YulString> const& m_variablesToReplace;
|
||||||
|
TypeInfo const& m_typeInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -83,10 +92,10 @@ void IntroduceSSA::operator()(Block& _block)
|
|||||||
{
|
{
|
||||||
YulString oldName = var.name;
|
YulString oldName = var.name;
|
||||||
YulString newName = m_nameDispenser.newName(oldName);
|
YulString newName = m_nameDispenser.newName(oldName);
|
||||||
newVariables.emplace_back(TypedName{loc, newName, {}});
|
newVariables.emplace_back(TypedName{loc, newName, var.type});
|
||||||
statements.emplace_back(VariableDeclaration{
|
statements.emplace_back(VariableDeclaration{
|
||||||
loc,
|
loc,
|
||||||
{TypedName{loc, oldName, {}}},
|
{TypedName{loc, oldName, var.type}},
|
||||||
make_unique<Expression>(Identifier{loc, newName})
|
make_unique<Expression>(Identifier{loc, newName})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -110,7 +119,11 @@ void IntroduceSSA::operator()(Block& _block)
|
|||||||
{
|
{
|
||||||
YulString oldName = var.name;
|
YulString oldName = var.name;
|
||||||
YulString newName = m_nameDispenser.newName(oldName);
|
YulString newName = m_nameDispenser.newName(oldName);
|
||||||
newVariables.emplace_back(TypedName{loc, newName, {}});
|
newVariables.emplace_back(TypedName{
|
||||||
|
loc,
|
||||||
|
newName,
|
||||||
|
m_typeInfo.typeOfVariable(oldName)
|
||||||
|
});
|
||||||
statements.emplace_back(Assignment{
|
statements.emplace_back(Assignment{
|
||||||
loc,
|
loc,
|
||||||
{Identifier{loc, oldName}},
|
{Identifier{loc, oldName}},
|
||||||
@ -136,9 +149,12 @@ class IntroduceControlFlowSSA: public ASTModifier
|
|||||||
public:
|
public:
|
||||||
explicit IntroduceControlFlowSSA(
|
explicit IntroduceControlFlowSSA(
|
||||||
NameDispenser& _nameDispenser,
|
NameDispenser& _nameDispenser,
|
||||||
set<YulString> const& _variablesToReplace
|
set<YulString> const& _variablesToReplace,
|
||||||
|
TypeInfo const& _typeInfo
|
||||||
):
|
):
|
||||||
m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace)
|
m_nameDispenser(_nameDispenser),
|
||||||
|
m_variablesToReplace(_variablesToReplace),
|
||||||
|
m_typeInfo(_typeInfo)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void operator()(FunctionDefinition& _function) override;
|
void operator()(FunctionDefinition& _function) override;
|
||||||
@ -153,6 +169,7 @@ private:
|
|||||||
set<YulString> m_variablesInScope;
|
set<YulString> m_variablesInScope;
|
||||||
/// Set of variables that do not have a specific value.
|
/// Set of variables that do not have a specific value.
|
||||||
set<YulString> m_variablesToReassign;
|
set<YulString> m_variablesToReassign;
|
||||||
|
TypeInfo const& m_typeInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
void IntroduceControlFlowSSA::operator()(FunctionDefinition& _function)
|
void IntroduceControlFlowSSA::operator()(FunctionDefinition& _function)
|
||||||
@ -221,7 +238,7 @@ void IntroduceControlFlowSSA::operator()(Block& _block)
|
|||||||
YulString newName = m_nameDispenser.newName(toReassign);
|
YulString newName = m_nameDispenser.newName(toReassign);
|
||||||
toPrepend.emplace_back(VariableDeclaration{
|
toPrepend.emplace_back(VariableDeclaration{
|
||||||
locationOf(_s),
|
locationOf(_s),
|
||||||
{TypedName{locationOf(_s), newName, {}}},
|
{TypedName{locationOf(_s), newName, m_typeInfo.typeOfVariable(toReassign)}},
|
||||||
make_unique<Expression>(Identifier{locationOf(_s), toReassign})
|
make_unique<Expression>(Identifier{locationOf(_s), toReassign})
|
||||||
});
|
});
|
||||||
assignedVariables.insert(toReassign);
|
assignedVariables.insert(toReassign);
|
||||||
@ -375,10 +392,11 @@ void PropagateValues::operator()(Block& _block)
|
|||||||
|
|
||||||
void SSATransform::run(OptimiserStepContext& _context, Block& _ast)
|
void SSATransform::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
{
|
{
|
||||||
|
TypeInfo typeInfo(_context.dialect, _ast);
|
||||||
Assignments assignments;
|
Assignments assignments;
|
||||||
assignments(_ast);
|
assignments(_ast);
|
||||||
IntroduceSSA{_context.dispenser, assignments.names()}(_ast);
|
IntroduceSSA{_context.dispenser, assignments.names(), typeInfo}(_ast);
|
||||||
IntroduceControlFlowSSA{_context.dispenser, assignments.names()}(_ast);
|
IntroduceControlFlowSSA{_context.dispenser, assignments.names(), typeInfo}(_ast);
|
||||||
PropagateValues{assignments.names()}(_ast);
|
PropagateValues{assignments.names()}(_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
libyul/optimiser/TypeInfo.cpp
Normal file
103
libyul/optimiser/TypeInfo.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Helper class that keeps track of the types while performing optimizations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libyul/optimiser/TypeInfo.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
|
||||||
|
#include <libyul/AsmData.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
class TypeInfo::TypeCollector: public ASTWalker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TypeCollector(Block const& _block)
|
||||||
|
{
|
||||||
|
(*this)(_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
using ASTWalker::operator();
|
||||||
|
void operator()(VariableDeclaration const& _varDecl) override
|
||||||
|
{
|
||||||
|
for (auto const& var: _varDecl.variables)
|
||||||
|
variableTypes[var.name] = var.type;
|
||||||
|
}
|
||||||
|
void operator()(FunctionDefinition const& _funDef) override
|
||||||
|
{
|
||||||
|
ASTWalker::operator()(_funDef);
|
||||||
|
|
||||||
|
auto& funType = functionTypes[_funDef.name];
|
||||||
|
for (auto const arg: _funDef.parameters)
|
||||||
|
{
|
||||||
|
funType.parameters.emplace_back(arg.type);
|
||||||
|
variableTypes[arg.name] = arg.type;
|
||||||
|
}
|
||||||
|
for (auto const ret: _funDef.returnVariables)
|
||||||
|
{
|
||||||
|
funType.returns.emplace_back(ret.type);
|
||||||
|
variableTypes[ret.name] = ret.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<YulString, YulString> variableTypes;
|
||||||
|
std::map<YulString, FunctionType> functionTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TypeInfo::TypeInfo(Dialect const& _dialect, Block const& _ast):
|
||||||
|
m_dialect(_dialect)
|
||||||
|
{
|
||||||
|
TypeCollector types(_ast);
|
||||||
|
m_functionTypes = std::move(types.functionTypes);
|
||||||
|
m_variableTypes = std::move(types.variableTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
YulString TypeInfo::typeOf(Expression const& _expression) const
|
||||||
|
{
|
||||||
|
return std::visit(GenericVisitor{
|
||||||
|
[&](FunctionCall const& _funCall) {
|
||||||
|
YulString name = _funCall.functionName.name;
|
||||||
|
vector<YulString> const* retTypes = nullptr;
|
||||||
|
if (BuiltinFunction const* fun = m_dialect.builtin(name))
|
||||||
|
retTypes = &fun->returns;
|
||||||
|
else
|
||||||
|
retTypes = &m_functionTypes.at(name).returns;
|
||||||
|
yulAssert(retTypes && retTypes->size() == 1, "Call to typeOf for non-single-value expression.");
|
||||||
|
return retTypes->front();
|
||||||
|
},
|
||||||
|
[&](Identifier const& _identifier) {
|
||||||
|
return m_variableTypes.at(_identifier.name);
|
||||||
|
},
|
||||||
|
[&](Literal const& _literal) {
|
||||||
|
return _literal.type;
|
||||||
|
}
|
||||||
|
}, _expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
YulString TypeInfo::typeOfVariable(YulString _name) const
|
||||||
|
{
|
||||||
|
return m_variableTypes.at(_name);
|
||||||
|
}
|
64
libyul/optimiser/TypeInfo.h
Normal file
64
libyul/optimiser/TypeInfo.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Helper class that keeps track of the types while performing optimizations.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/AsmDataForward.h>
|
||||||
|
#include <libyul/YulString.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
struct Dialect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that keeps track of the types while performing optimizations.
|
||||||
|
*
|
||||||
|
* Only works on disambiguated sources!
|
||||||
|
*/
|
||||||
|
class TypeInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TypeInfo(Dialect const& _dialect, Block const& _ast);
|
||||||
|
|
||||||
|
void setVariableType(YulString _name, YulString _type) { m_variableTypes[_name] = _type; }
|
||||||
|
|
||||||
|
/// @returns the type of an expression that is assumed to return exactly one value.
|
||||||
|
YulString typeOf(Expression const& _expression) const;
|
||||||
|
|
||||||
|
/// \returns the type of variable
|
||||||
|
YulString typeOfVariable(YulString _name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class TypeCollector;
|
||||||
|
|
||||||
|
struct FunctionType
|
||||||
|
{
|
||||||
|
std::vector<YulString> parameters;
|
||||||
|
std::vector<YulString> returns;
|
||||||
|
};
|
||||||
|
|
||||||
|
Dialect const& m_dialect;
|
||||||
|
std::map<YulString, YulString> m_variableTypes;
|
||||||
|
std::map<YulString, FunctionType> m_functionTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -100,10 +100,10 @@ void UnusedPruner::operator()(Block& _block)
|
|||||||
subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
|
subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
|
||||||
statement = Block{std::move(varDecl.location), {}};
|
statement = Block{std::move(varDecl.location), {}};
|
||||||
}
|
}
|
||||||
else if (varDecl.variables.size() == 1 && m_dialect.discardFunction())
|
else if (varDecl.variables.size() == 1 && m_dialect.discardFunction(varDecl.variables.front().type))
|
||||||
statement = ExpressionStatement{varDecl.location, FunctionCall{
|
statement = ExpressionStatement{varDecl.location, FunctionCall{
|
||||||
varDecl.location,
|
varDecl.location,
|
||||||
{varDecl.location, m_dialect.discardFunction()->name},
|
{varDecl.location, m_dialect.discardFunction(varDecl.variables.front().type)->name},
|
||||||
{*std::move(varDecl.value)}
|
{*std::move(varDecl.value)}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -32,14 +33,14 @@ void VarDeclInitializer::operator()(Block& _block)
|
|||||||
using OptionalStatements = std::optional<vector<Statement>>;
|
using OptionalStatements = std::optional<vector<Statement>>;
|
||||||
util::GenericVisitor visitor{
|
util::GenericVisitor visitor{
|
||||||
util::VisitorFallback<OptionalStatements>{},
|
util::VisitorFallback<OptionalStatements>{},
|
||||||
[](VariableDeclaration& _varDecl) -> OptionalStatements
|
[this](VariableDeclaration& _varDecl) -> OptionalStatements
|
||||||
{
|
{
|
||||||
if (_varDecl.value)
|
if (_varDecl.value)
|
||||||
return {};
|
return {};
|
||||||
Literal zero{{}, LiteralKind::Number, YulString{"0"}, {}};
|
|
||||||
if (_varDecl.variables.size() == 1)
|
if (_varDecl.variables.size() == 1)
|
||||||
{
|
{
|
||||||
_varDecl.value = make_unique<Expression>(std::move(zero));
|
_varDecl.value = make_unique<Expression>(m_dialect.zeroLiteralForType(_varDecl.variables.front().type));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -47,7 +48,10 @@ void VarDeclInitializer::operator()(Block& _block)
|
|||||||
OptionalStatements ret{vector<Statement>{}};
|
OptionalStatements ret{vector<Statement>{}};
|
||||||
langutil::SourceLocation loc{std::move(_varDecl.location)};
|
langutil::SourceLocation loc{std::move(_varDecl.location)};
|
||||||
for (auto& var: _varDecl.variables)
|
for (auto& var: _varDecl.variables)
|
||||||
ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, make_unique<Expression>(zero)});
|
{
|
||||||
|
unique_ptr<Expression> expr = make_unique<Expression >(m_dialect.zeroLiteralForType(var.type));
|
||||||
|
ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, std::move(expr)});
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,14 @@ class VarDeclInitializer: public ASTModifier
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr char const* name{"VarDeclInitializer"};
|
static constexpr char const* name{"VarDeclInitializer"};
|
||||||
static void run(OptimiserStepContext&, Block& _ast) { VarDeclInitializer{}(_ast); }
|
static void run(OptimiserStepContext& _ctx, Block& _ast) { VarDeclInitializer{_ctx.dialect}(_ast); }
|
||||||
|
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit VarDeclInitializer(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||||
|
|
||||||
|
Dialect const& m_dialect;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(simple_inside_structures)
|
|||||||
BOOST_CHECK_EQUAL(inlinableFunctions("{"
|
BOOST_CHECK_EQUAL(inlinableFunctions("{"
|
||||||
"function g(a:u256) -> b:u256 { b := a }"
|
"function g(a:u256) -> b:u256 { b := a }"
|
||||||
"for {"
|
"for {"
|
||||||
"} 1:u256 {"
|
"} true {"
|
||||||
"function f() -> x:u256 { x := g(2:u256) }"
|
"function f() -> x:u256 { x := g(2:u256) }"
|
||||||
"}"
|
"}"
|
||||||
"{"
|
"{"
|
||||||
|
@ -245,16 +245,6 @@ BOOST_AUTO_TEST_CASE(optional_types)
|
|||||||
BOOST_CHECK(successParse("{ function f(a:u256) -> b {} }"));
|
BOOST_CHECK(successParse("{ function f(a:u256) -> b {} }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(invalid_types)
|
|
||||||
{
|
|
||||||
/// testing invalid literal
|
|
||||||
/// NOTE: these will need to change when types are compared
|
|
||||||
CHECK_ERROR("{ let x:bool := 1:invalid }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported).");
|
|
||||||
/// testing invalid variable declaration
|
|
||||||
CHECK_ERROR("{ let x:invalid := 1:bool }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported).");
|
|
||||||
CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported).");
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(number_literals)
|
BOOST_AUTO_TEST_CASE(number_literals)
|
||||||
{
|
{
|
||||||
BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }"));
|
BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }"));
|
||||||
@ -268,7 +258,7 @@ BOOST_AUTO_TEST_CASE(builtin_types)
|
|||||||
{
|
{
|
||||||
BOOST_CHECK(successParse("{ let x:bool := true:bool }"));
|
BOOST_CHECK(successParse("{ let x:bool := true:bool }"));
|
||||||
BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }"));
|
BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }"));
|
||||||
BOOST_CHECK(successParse("{ let x:s8 := 1:u8 }"));
|
BOOST_CHECK(successParse("{ let x:s8 := 1:s8 }"));
|
||||||
BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }"));
|
BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }"));
|
||||||
BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }"));
|
BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }"));
|
||||||
BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }"));
|
BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }"));
|
||||||
@ -495,15 +485,7 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid)
|
|||||||
{
|
{
|
||||||
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
|
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
|
||||||
CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'");
|
CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'");
|
||||||
// TODO change this to an error once we check types.
|
CHECK_ERROR("{ if 42:u256 { } }", TypeError, "Expected a value of boolean type");
|
||||||
BOOST_CHECK(successParse("{ if 42:u256 { } }"));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(switch_case_types)
|
|
||||||
{
|
|
||||||
CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case 1:u32 {} }", TypeError, "Switch cases have non-matching types.");
|
|
||||||
// The following should be an error in the future, but this is not yet detected.
|
|
||||||
BOOST_CHECK(successParse("{ switch 0:u256 case 0:u32 {} case 1:u32 {} }"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(switch_duplicate_case)
|
BOOST_AUTO_TEST_CASE(switch_duplicate_case)
|
||||||
|
@ -111,6 +111,8 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename)
|
|||||||
m_dialect = &WasmDialect::instance();
|
m_dialect = &WasmDialect::instance();
|
||||||
else if (dialectName == "evm")
|
else if (dialectName == "evm")
|
||||||
m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion());
|
m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion());
|
||||||
|
else if (dialectName == "evmTyped")
|
||||||
|
m_dialect = &EVMDialectTyped::instance(solidity::test::CommonOptions::get().evmVersion());
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName));
|
BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName));
|
||||||
|
|
||||||
@ -357,7 +359,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
|||||||
{
|
{
|
||||||
disambiguate();
|
disambiguate();
|
||||||
ExpressionSplitter::run(*m_context, *m_ast);
|
ExpressionSplitter::run(*m_context, *m_ast);
|
||||||
WordSizeTransform::run(*m_dialect, ""_yulstring, *m_ast, *m_nameDispenser);
|
WordSizeTransform::run(*m_dialect, *m_dialect, *m_ast, *m_nameDispenser);
|
||||||
}
|
}
|
||||||
else if (m_optimizerStep == "fullSuite")
|
else if (m_optimizerStep == "fullSuite")
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
let y:bool := false
|
||||||
|
for {} true { } {
|
||||||
|
if y { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: yul
|
||||||
|
// step: conditionalSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let y:bool := false
|
||||||
|
// for { } true { }
|
||||||
|
// {
|
||||||
|
// if y { break }
|
||||||
|
// y := false
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
let y:i32 := 0:i32
|
||||||
|
for {} true { } {
|
||||||
|
if y { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// step: conditionalSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let y:i32 := 0:i32
|
||||||
|
// for { } true { }
|
||||||
|
// {
|
||||||
|
// if y { break }
|
||||||
|
// y := false
|
||||||
|
// }
|
||||||
|
// }
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
{ let a:u256, b:u256 }
|
{ let a:u256, b:u256 }
|
||||||
{
|
{
|
||||||
for { let a:u256 } a { a := a } {
|
function eq(x: u256, y: u256) -> z: bool {}
|
||||||
|
for { let a:u256 } eq(a, a) { a := a } {
|
||||||
let b:u256 := a
|
let b:u256 := a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,7 +14,9 @@
|
|||||||
// {
|
// {
|
||||||
// { let a, b }
|
// { let a, b }
|
||||||
// {
|
// {
|
||||||
// for { let a_1 } a_1 { a_1 := a_1 }
|
// function eq(x, y) -> z:bool
|
||||||
|
// { }
|
||||||
|
// for { let a_1 } eq(a_1, a_1) { a_1 := a_1 }
|
||||||
// { let b_2 := a_1 }
|
// { let b_2 := a_1 }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
42
test/libyul/yulOptimizerTests/expressionSplitter/typed.yul
Normal file
42
test/libyul/yulOptimizerTests/expressionSplitter/typed.yul
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
function fun(x: i32, y) -> t: i32, z: i32 {
|
||||||
|
z := i32.add(x, i32.add(z, z))
|
||||||
|
|
||||||
|
}
|
||||||
|
i64.store(i32.load(5:i32), i64.load(8:i32))
|
||||||
|
let i := 0
|
||||||
|
for {} i32.eqz(i32.load(9:i32)) { i := i64.add(i, 1) } {
|
||||||
|
let f: i32, g: i32 := fun(i32.load(1:i32), i64.load(i32.load(0: i32)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// step: expressionSplitter
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// function fun(x:i32, y) -> t:i32, z:i32
|
||||||
|
// {
|
||||||
|
// let _1:i32 := i32.add(z, z)
|
||||||
|
// z := i32.add(x, _1)
|
||||||
|
// }
|
||||||
|
// let _2:i32 := 8:i32
|
||||||
|
// let _3 := i64.load(_2)
|
||||||
|
// let _4:i32 := 5:i32
|
||||||
|
// let _5:i32 := i32.load(_4)
|
||||||
|
// i64.store(_5, _3)
|
||||||
|
// let i := 0
|
||||||
|
// for { }
|
||||||
|
// i32.eqz(i32.load(9:i32))
|
||||||
|
// {
|
||||||
|
// let _6 := 1
|
||||||
|
// i := i64.add(i, _6)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let _7:i32 := 0:i32
|
||||||
|
// let _8:i32 := i32.load(_7)
|
||||||
|
// let _9 := i64.load(_8)
|
||||||
|
// let _10:i32 := 1:i32
|
||||||
|
// let _11:i32 := i32.load(_10)
|
||||||
|
// let f:i32, g:i32 := fun(_11, _9)
|
||||||
|
// }
|
||||||
|
// }
|
@ -19,7 +19,7 @@
|
|||||||
// { }
|
// { }
|
||||||
// for { } a { }
|
// for { } a { }
|
||||||
// { }
|
// { }
|
||||||
// for { } 1 { }
|
// for { } true { }
|
||||||
// {
|
// {
|
||||||
// if iszero(add(a, a)) { break }
|
// if iszero(add(a, a)) { break }
|
||||||
// }
|
// }
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// step: forLoopConditionIntoBody
|
// step: forLoopConditionIntoBody
|
||||||
// ----
|
// ----
|
||||||
// {
|
// {
|
||||||
// for { let a := 1 } 1 { a := add(a, 1) }
|
// for { let a := 1 } true { a := add(a, 1) }
|
||||||
// {
|
// {
|
||||||
// if iszero(iszero(eq(a, 10))) { break }
|
// if iszero(iszero(eq(a, 10))) { break }
|
||||||
// }
|
// }
|
||||||
|
@ -19,16 +19,16 @@
|
|||||||
// {
|
// {
|
||||||
// let random := 42
|
// let random := 42
|
||||||
// for {
|
// for {
|
||||||
// for { let a := 1 } 1 { }
|
// for { let a := 1 } true { }
|
||||||
// {
|
// {
|
||||||
// if iszero(iszero(eq(a, 10))) { break }
|
// if iszero(iszero(eq(a, 10))) { break }
|
||||||
// a := add(a, 1)
|
// a := add(a, 1)
|
||||||
// }
|
// }
|
||||||
// let b := 1
|
// let b := 1
|
||||||
// }
|
// }
|
||||||
// 1
|
// true
|
||||||
// {
|
// {
|
||||||
// for { let c := 1 } 1 { c := add(c, 1) }
|
// for { let c := 1 } true { c := add(c, 1) }
|
||||||
// {
|
// {
|
||||||
// if iszero(iszero(eq(c, 2))) { break }
|
// if iszero(iszero(eq(c, 2))) { break }
|
||||||
// b := add(b, 1)
|
// b := add(b, 1)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// ----
|
// ----
|
||||||
// {
|
// {
|
||||||
// let random := 42
|
// let random := 42
|
||||||
// for { let a := 1 } 1 { a := add(a, 1) }
|
// for { let a := 1 } true { a := add(a, 1) }
|
||||||
// {
|
// {
|
||||||
// if iszero(iszero(eq(a, 10))) { break }
|
// if iszero(iszero(eq(a, 10))) { break }
|
||||||
// a := add(a, 1)
|
// a := add(a, 1)
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
function f(a: u256) -> x: bool, y:u256 {
|
||||||
|
y := mul(a, a)
|
||||||
|
}
|
||||||
|
let r: bool, s: u256 := f(mload(3))
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// step: fullInliner
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// let a_3 := mload(3)
|
||||||
|
// let x_4:bool := false
|
||||||
|
// let y_5 := 0
|
||||||
|
// y_5 := mul(a_3, a_3)
|
||||||
|
// let r:bool := x_4
|
||||||
|
// let s := y_5
|
||||||
|
// }
|
||||||
|
// function f(a) -> x:bool, y
|
||||||
|
// { y := mul(a, a) }
|
||||||
|
// }
|
@ -14,7 +14,7 @@
|
|||||||
// {
|
// {
|
||||||
// let _1 := iszero(caller())
|
// let _1 := iszero(caller())
|
||||||
// for { }
|
// for { }
|
||||||
// 1
|
// true
|
||||||
// {
|
// {
|
||||||
// for { } iszero(_1) { }
|
// for { } iszero(_1) { }
|
||||||
// { }
|
// { }
|
||||||
|
41
test/libyul/yulOptimizerTests/ssaTransform/typed.yul
Normal file
41
test/libyul/yulOptimizerTests/ssaTransform/typed.yul
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
let b:bool := true
|
||||||
|
let c:bool := false
|
||||||
|
c := b
|
||||||
|
b := false
|
||||||
|
|
||||||
|
let a:u256 := 1
|
||||||
|
a := add(a, 1)
|
||||||
|
if c {
|
||||||
|
a := add(a, 1)
|
||||||
|
}
|
||||||
|
a := add(a, 1)
|
||||||
|
mstore(a, 1)
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// step: ssaTransform
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let b_1:bool := true
|
||||||
|
// let b:bool := b_1
|
||||||
|
// let c_2:bool := false
|
||||||
|
// let c:bool := c_2
|
||||||
|
// let c_3:bool := b_1
|
||||||
|
// c := c_3
|
||||||
|
// let b_4:bool := false
|
||||||
|
// b := b_4
|
||||||
|
// let a_5 := 1
|
||||||
|
// let a := a_5
|
||||||
|
// let a_6 := add(a_5, 1)
|
||||||
|
// a := a_6
|
||||||
|
// if c_3
|
||||||
|
// {
|
||||||
|
// let a_7 := add(a_6, 1)
|
||||||
|
// a := a_7
|
||||||
|
// }
|
||||||
|
// let a_9 := a
|
||||||
|
// let a_8 := add(a_9, 1)
|
||||||
|
// a := a_8
|
||||||
|
// mstore(a_8, 1)
|
||||||
|
// }
|
25
test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul
Normal file
25
test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
let b:bool := true
|
||||||
|
let c:bool := false
|
||||||
|
for {} b {} {
|
||||||
|
c := true
|
||||||
|
}
|
||||||
|
let d: bool := c
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// step: ssaTransform
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let b:bool := true
|
||||||
|
// let c_1:bool := false
|
||||||
|
// let c:bool := c_1
|
||||||
|
// for { } b { }
|
||||||
|
// {
|
||||||
|
// let c_3:bool := c
|
||||||
|
// let c_2:bool := true
|
||||||
|
// c := c_2
|
||||||
|
// }
|
||||||
|
// let c_4:bool := c
|
||||||
|
// let d:bool := c_4
|
||||||
|
// }
|
25
test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul
Normal file
25
test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
let b:bool := true
|
||||||
|
let c:bool := false
|
||||||
|
switch b
|
||||||
|
case true { c := true}
|
||||||
|
case false { }
|
||||||
|
let d: bool := c
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// step: ssaTransform
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let b:bool := true
|
||||||
|
// let c_1:bool := false
|
||||||
|
// let c:bool := c_1
|
||||||
|
// switch b
|
||||||
|
// case true {
|
||||||
|
// let c_2:bool := true
|
||||||
|
// c := c_2
|
||||||
|
// }
|
||||||
|
// case false { }
|
||||||
|
// let c_3:bool := c
|
||||||
|
// let d:bool := c_3
|
||||||
|
// }
|
23
test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul
Normal file
23
test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
let a1
|
||||||
|
let a2: bool
|
||||||
|
let b1, b2: bool
|
||||||
|
function f(a:u256, b:u256, c:bool) -> r:bool, t {
|
||||||
|
let x1: bool, x2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// step: varDeclInitializer
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// let a1 := 0
|
||||||
|
// let a2:bool := false
|
||||||
|
// let b1 := 0
|
||||||
|
// let b2:bool := false
|
||||||
|
// function f(a, b, c:bool) -> r:bool, t
|
||||||
|
// {
|
||||||
|
// let x1:bool := false
|
||||||
|
// let x2 := 0
|
||||||
|
// }
|
||||||
|
// }
|
@ -67,13 +67,13 @@
|
|||||||
// let _10_3 := 3
|
// let _10_3 := 3
|
||||||
// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3)
|
// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3)
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// if run_default
|
// if run_default
|
||||||
// {
|
// {
|
||||||
// let _11_0 := 0
|
// let _11_0 := 0
|
||||||
|
@ -45,9 +45,9 @@
|
|||||||
// let _8_3 := 2
|
// let _8_3 := 2
|
||||||
// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3)
|
// sstore(_8_0, _8_1, _8_2, _8_3, _7_0, _7_1, _7_2, _7_3)
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// case 536870912 {
|
// case 536870912 {
|
||||||
// switch _2_2
|
// switch _2_2
|
||||||
@ -75,13 +75,13 @@
|
|||||||
// let _10_3 := 3
|
// let _10_3 := 3
|
||||||
// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3)
|
// sstore(_10_0, _10_1, _10_2, _10_3, _9_0, _9_1, _9_2, _9_3)
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// }
|
// }
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// if run_default
|
// if run_default
|
||||||
// {
|
// {
|
||||||
// let _11_0 := 0
|
// let _11_0 := 0
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3)
|
// let _2_0, _2_1, _2_2, _2_3 := calldataload(_1_0, _1_1, _1_2, _1_3)
|
||||||
// let run_default
|
// let run_default
|
||||||
// switch _2_0
|
// switch _2_0
|
||||||
// default { run_default := 1 }
|
// default { run_default := true }
|
||||||
// if run_default
|
// if run_default
|
||||||
// {
|
// {
|
||||||
// let _3_0 := 0
|
// let _3_0 := 0
|
||||||
|
12
test/libyul/yulSyntaxTests/assignment_fail.yul
Normal file
12
test/libyul/yulSyntaxTests/assignment_fail.yul
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
let x:u256
|
||||||
|
let y := x
|
||||||
|
let z:bool
|
||||||
|
z := y
|
||||||
|
y := z
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (51-52): Assigning a value of type "u256" to a variable of type "bool".
|
||||||
|
// TypeError: (62-63): Assigning a value of type "bool" to a variable of type "u256".
|
7
test/libyul/yulSyntaxTests/for_loop_condition.yul
Normal file
7
test/libyul/yulSyntaxTests/for_loop_condition.yul
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
let x:bool
|
||||||
|
for {} x {} {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
8
test/libyul/yulSyntaxTests/for_loop_condition_fail.yul
Normal file
8
test/libyul/yulSyntaxTests/for_loop_condition_fail.yul
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
let x
|
||||||
|
for {} x {} {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (23-24): Expected a value of boolean type "bool" but got "u256"
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
let x
|
||||||
|
for {} x {} {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// ----
|
||||||
|
// TypeError: (23-24): Expected a value of boolean type "i32" but got "i64"
|
7
test/libyul/yulSyntaxTests/invalid_type.yul
Normal file
7
test/libyul/yulSyntaxTests/invalid_type.yul
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
let x: invalidType
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (10-24): "invalidType" is not a valid type (user defined types are not yet supported).
|
8
test/libyul/yulSyntaxTests/invalid_type2.yul
Normal file
8
test/libyul/yulSyntaxTests/invalid_type2.yul
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
let x := 1:invalidType
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (15-28): "invalidType" is not a valid type (user defined types are not yet supported).
|
||||||
|
// TypeError: (10-11): Assigning value of type "invalidType" to variable of type "u256.
|
8
test/libyul/yulSyntaxTests/invalid_type3.yul
Normal file
8
test/libyul/yulSyntaxTests/invalid_type3.yul
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
function f(a: invalidType) -> b: invalidType {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (17-31): "invalidType" is not a valid type (user defined types are not yet supported).
|
||||||
|
// TypeError: (36-50): "invalidType" is not a valid type (user defined types are not yet supported).
|
9
test/libyul/yulSyntaxTests/invalid_type4.yul
Normal file
9
test/libyul/yulSyntaxTests/invalid_type4.yul
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
switch 1
|
||||||
|
case 8: invalidType {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (24-38): Expected a value of type "u256" but got "invalidType"
|
||||||
|
// TypeError: (24-38): "invalidType" is not a valid type (user defined types are not yet supported).
|
8
test/libyul/yulSyntaxTests/type_check_cases.yul
Normal file
8
test/libyul/yulSyntaxTests/type_check_cases.yul
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
switch 7:i64
|
||||||
|
case 0:i64 {}
|
||||||
|
case 2:i64 {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// ----
|
10
test/libyul/yulSyntaxTests/type_check_cases_fail.yul
Normal file
10
test/libyul/yulSyntaxTests/type_check_cases_fail.yul
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
switch 7:i32
|
||||||
|
case 0:i64 {}
|
||||||
|
case 2:i64 {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// ----
|
||||||
|
// TypeError: (28-33): Expected a value of type "i32" but got "i64"
|
||||||
|
// TypeError: (46-51): Expected a value of type "i32" but got "i64"
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
switch 7
|
||||||
|
case true:bool {}
|
||||||
|
case true:bool {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (24-33): Expected a value of type "u256" but got "bool"
|
||||||
|
// TypeError: (46-55): Expected a value of type "u256" but got "bool"
|
7
test/libyul/yulSyntaxTests/type_check_if_condition.yul
Normal file
7
test/libyul/yulSyntaxTests/type_check_if_condition.yul
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
let x:i32
|
||||||
|
if x {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// ----
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
let x:i64
|
||||||
|
if x {}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: ewasm
|
||||||
|
// ----
|
||||||
|
// TypeError: (23-24): Expected a value of boolean type "i32" but got "i64"
|
12
test/libyul/yulSyntaxTests/user_defined_functions_fail.yul
Normal file
12
test/libyul/yulSyntaxTests/user_defined_functions_fail.yul
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
function f(a:u256, b:u256, c:bool) -> r:bool, t {
|
||||||
|
r := lt(a, b)
|
||||||
|
t := bool_to_u256(not(c))
|
||||||
|
}
|
||||||
|
let x, y: bool := f(1, 2: u256, true)
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
||||||
|
// TypeError: (126-127): Assigning value of type "bool" to variable of type "u256.
|
||||||
|
// TypeError: (129-136): Assigning value of type "u256" to variable of type "bool.
|
10
test/libyul/yulSyntaxTests/user_defined_functions_fine.yul
Normal file
10
test/libyul/yulSyntaxTests/user_defined_functions_fine.yul
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
function f(a:u256, b:u256, c:bool) -> r:bool, t {
|
||||||
|
r := lt(a, b)
|
||||||
|
t := bool_to_u256(not(c))
|
||||||
|
}
|
||||||
|
let x: bool, y: u256 := f(1, 2: u256, true)
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// dialect: evmTyped
|
||||||
|
// ----
|
Loading…
Reference in New Issue
Block a user