mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8068 from ethereum/evmTypedDialect
[Yul] EVM typed dialect
This commit is contained in:
@@ -608,7 +608,7 @@ Scope& AsmAnalyzer::scope(Block const* _block)
|
||||
}
|
||||
void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location)
|
||||
{
|
||||
if (!_type.empty() && !contains(m_dialect.types, _type))
|
||||
if (!_type.empty() && !m_dialect.types.count(_type))
|
||||
m_errorReporter.typeError(
|
||||
_location,
|
||||
"\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)."
|
||||
|
||||
@@ -366,7 +366,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
location(),
|
||||
kind,
|
||||
YulString{currentLiteral()},
|
||||
{}
|
||||
kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType
|
||||
};
|
||||
advance();
|
||||
if (currentToken() == Token::Colon)
|
||||
@@ -497,6 +497,9 @@ TypedName Parser::parseTypedName()
|
||||
typedName.location.end = endPosition();
|
||||
typedName.type = expectAsmIdentifier();
|
||||
}
|
||||
else
|
||||
typedName.type = m_dialect.defaultType;
|
||||
|
||||
return typedName;
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -23,6 +23,7 @@
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/Dialect.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
@@ -49,7 +50,7 @@ string AsmPrinter::operator()(Literal const& _literal) const
|
||||
return _literal.value.str() + appendTypeName(_literal.type);
|
||||
case LiteralKind::Boolean:
|
||||
yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal.");
|
||||
return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type);
|
||||
return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type, true);
|
||||
case LiteralKind::String:
|
||||
break;
|
||||
}
|
||||
@@ -236,9 +237,18 @@ string AsmPrinter::formatTypedName(TypedName _variable) const
|
||||
return _variable.name.str() + appendTypeName(_variable.type);
|
||||
}
|
||||
|
||||
string AsmPrinter::appendTypeName(YulString _type) const
|
||||
string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const
|
||||
{
|
||||
if (!_type.empty())
|
||||
if (m_dialect && !_type.empty())
|
||||
{
|
||||
if (!_isBoolLiteral && _type == m_dialect->defaultType)
|
||||
_type = {};
|
||||
else if (_isBoolLiteral && _type == m_dialect->boolType && !m_dialect->defaultType.empty())
|
||||
// Special case: If we have a bool type but empty default type, do not remove the type.
|
||||
_type = {};
|
||||
}
|
||||
if (_type.empty())
|
||||
return {};
|
||||
else
|
||||
return ":" + _type.str();
|
||||
return "";
|
||||
}
|
||||
|
||||
+10
-2
@@ -30,10 +30,16 @@ namespace solidity::yul
|
||||
{
|
||||
struct Dialect;
|
||||
|
||||
/**
|
||||
* Converts a parsed Yul AST into readable string representation.
|
||||
* Ignores source locations.
|
||||
* If a dialect is provided, the dialect's default type is omitted.
|
||||
*/
|
||||
class AsmPrinter
|
||||
{
|
||||
public:
|
||||
explicit AsmPrinter() {}
|
||||
AsmPrinter() {}
|
||||
explicit AsmPrinter(Dialect const& _dialect): m_dialect(&_dialect) {}
|
||||
|
||||
std::string operator()(Literal const& _literal) const;
|
||||
std::string operator()(Identifier const& _identifier) const;
|
||||
@@ -52,7 +58,9 @@ public:
|
||||
|
||||
private:
|
||||
std::string formatTypedName(TypedName _variable) const;
|
||||
std::string appendTypeName(YulString _type) const;
|
||||
std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const;
|
||||
|
||||
Dialect const* m_dialect = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _
|
||||
case AssemblyStack::Language::StrictAssembly:
|
||||
return EVMDialect::strictAssemblyForEVMObjects(_version);
|
||||
case AssemblyStack::Language::Yul:
|
||||
return Dialect::yul();
|
||||
return EVMDialectTyped::instance(_version);
|
||||
case AssemblyStack::Language::Ewasm:
|
||||
return WasmDialect::instance();
|
||||
}
|
||||
yulAssert(false, "");
|
||||
return Dialect::yul();
|
||||
return Dialect::yulDeprecated();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -157,7 +157,7 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _o
|
||||
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
|
||||
break;
|
||||
case Language::Yul:
|
||||
dialect = &EVMDialect::yulForEVM(m_evmVersion);
|
||||
dialect = &EVMDialectTyped::instance(m_evmVersion);
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Invalid language.");
|
||||
@@ -236,7 +236,7 @@ string AssemblyStack::print() const
|
||||
{
|
||||
yulAssert(m_parserResult, "");
|
||||
yulAssert(m_parserResult->code, "");
|
||||
return m_parserResult->toString(m_language == Language::Yul) + "\n";
|
||||
return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion)) + "\n";
|
||||
}
|
||||
|
||||
shared_ptr<Object> AssemblyStack::parserResult() const
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
using namespace solidity::yul;
|
||||
using namespace std;
|
||||
|
||||
Dialect const& Dialect::yul()
|
||||
Dialect const& Dialect::yulDeprecated()
|
||||
{
|
||||
static unique_ptr<Dialect> dialect;
|
||||
static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }};
|
||||
|
||||
+4
-2
@@ -48,10 +48,11 @@ struct BuiltinFunction
|
||||
|
||||
struct Dialect: boost::noncopyable
|
||||
{
|
||||
/// Default type, can be omitted.
|
||||
YulString defaultType;
|
||||
/// Type used for the literals "true" and "false".
|
||||
YulString boolType;
|
||||
std::vector<YulString> types;
|
||||
std::set<YulString> types;
|
||||
|
||||
/// @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; }
|
||||
@@ -65,7 +66,8 @@ struct Dialect: boost::noncopyable
|
||||
Dialect() = default;
|
||||
virtual ~Dialect() = default;
|
||||
|
||||
static Dialect const& yul();
|
||||
/// Old "yul" dialect. This is only used for testing.
|
||||
static Dialect const& yulDeprecated();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -45,18 +45,18 @@ string indent(std::string const& _input)
|
||||
|
||||
}
|
||||
|
||||
string Data::toString(bool) const
|
||||
string Data::toString(Dialect const*) const
|
||||
{
|
||||
return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\"";
|
||||
}
|
||||
|
||||
string Object::toString(bool _yul) const
|
||||
string Object::toString(Dialect const* _dialect) const
|
||||
{
|
||||
yulAssert(code, "No code");
|
||||
string inner = "code " + AsmPrinter{}(*code);
|
||||
string inner = "code " + (_dialect ? AsmPrinter{*_dialect} : AsmPrinter{})(*code);
|
||||
|
||||
for (auto const& obj: subObjects)
|
||||
inner += "\n" + obj->toString(_yul);
|
||||
inner += "\n" + obj->toString(_dialect);
|
||||
|
||||
return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}";
|
||||
}
|
||||
|
||||
+5
-3
@@ -30,6 +30,7 @@
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
struct Dialect;
|
||||
struct AsmAnalysisInfo;
|
||||
|
||||
|
||||
@@ -39,7 +40,8 @@ struct AsmAnalysisInfo;
|
||||
struct ObjectNode
|
||||
{
|
||||
virtual ~ObjectNode() = default;
|
||||
virtual std::string toString(bool _yul) const = 0;
|
||||
virtual std::string toString(Dialect const* _dialect) const = 0;
|
||||
std::string toString() { return toString(nullptr); }
|
||||
|
||||
YulString name;
|
||||
};
|
||||
@@ -50,7 +52,7 @@ struct ObjectNode
|
||||
struct Data: ObjectNode
|
||||
{
|
||||
Data(YulString _name, bytes _data): data(std::move(_data)) { name = _name; }
|
||||
std::string toString(bool _yul) const override;
|
||||
std::string toString(Dialect const* _dialect) const override;
|
||||
|
||||
bytes data;
|
||||
};
|
||||
@@ -62,7 +64,7 @@ struct Object: ObjectNode
|
||||
{
|
||||
public:
|
||||
/// @returns a (parseable) string representation. Includes types if @a _yul is set.
|
||||
std::string toString(bool _yul) const override;
|
||||
std::string toString(Dialect const* _dialect) const override;
|
||||
|
||||
/// @returns the set of names of data objects accessible from within the code of
|
||||
/// this object.
|
||||
|
||||
@@ -203,15 +203,6 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _
|
||||
return *dialects[_version];
|
||||
}
|
||||
|
||||
EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version)
|
||||
{
|
||||
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
|
||||
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
|
||||
if (!dialects[_version])
|
||||
dialects[_version] = make_unique<EVMDialect>(_version, false);
|
||||
return *dialects[_version];
|
||||
}
|
||||
|
||||
SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instruction)
|
||||
{
|
||||
return SideEffects{
|
||||
@@ -222,3 +213,77 @@ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instructio
|
||||
evmasm::SemanticInformation::invalidatesMemory(_instruction)
|
||||
};
|
||||
}
|
||||
|
||||
EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess):
|
||||
EVMDialect(_evmVersion, _objectAccess)
|
||||
{
|
||||
defaultType = "u256"_yulstring;
|
||||
boolType = "bool"_yulstring;
|
||||
types = {defaultType, boolType};
|
||||
|
||||
m_functions["lt"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["gt"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["slt"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["sgt"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["eq"_yulstring].returns = {"bool"_yulstring};
|
||||
|
||||
// "not" and "bitnot" replace "iszero" and "not"
|
||||
m_functions["bitnot"_yulstring] = m_functions["not"_yulstring];
|
||||
m_functions["bitnot"_yulstring].name = "bitnot"_yulstring;
|
||||
m_functions["not"_yulstring] = m_functions["iszero"_yulstring];
|
||||
m_functions["not"_yulstring].name = "not"_yulstring;
|
||||
m_functions["not"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["not"_yulstring].parameters = {"bool"_yulstring};
|
||||
m_functions.erase("iszero"_yulstring);
|
||||
|
||||
m_functions["bitand"_yulstring] = m_functions["and"_yulstring];
|
||||
m_functions["bitand"_yulstring].name = "bitand"_yulstring;
|
||||
m_functions["bitor"_yulstring] = m_functions["or"_yulstring];
|
||||
m_functions["bitor"_yulstring].name = "bitor"_yulstring;
|
||||
m_functions["bitxor"_yulstring] = m_functions["xor"_yulstring];
|
||||
m_functions["bitxor"_yulstring].name = "bitxor"_yulstring;
|
||||
m_functions["and"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring};
|
||||
m_functions["and"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["or"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring};
|
||||
m_functions["or"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["xor"_yulstring].parameters = {"bool"_yulstring, "bool"_yulstring};
|
||||
m_functions["xor"_yulstring].returns = {"bool"_yulstring};
|
||||
m_functions["popbool"_yulstring] = m_functions["pop"_yulstring];
|
||||
m_functions["popbool"_yulstring].name = "popbool"_yulstring;
|
||||
m_functions["popbool"_yulstring].parameters = {"bool"_yulstring};
|
||||
m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, false, [](
|
||||
FunctionCall const&,
|
||||
AbstractAssembly&,
|
||||
BuiltinContext&,
|
||||
std::function<void()> _visitArguments
|
||||
) {
|
||||
_visitArguments();
|
||||
}));
|
||||
m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring};
|
||||
m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, false, [](
|
||||
FunctionCall const&,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext&,
|
||||
std::function<void()> _visitArguments
|
||||
) {
|
||||
// A value larger than 1 causes an invalid instruction.
|
||||
_visitArguments();
|
||||
_assembly.appendConstant(2);
|
||||
_assembly.appendInstruction(evmasm::Instruction::DUP2);
|
||||
_assembly.appendInstruction(evmasm::Instruction::LT);
|
||||
AbstractAssembly::LabelID inRange = _assembly.newLabelId();
|
||||
_assembly.appendJumpToIf(inRange);
|
||||
_assembly.appendInstruction(evmasm::Instruction::INVALID);
|
||||
_assembly.appendLabel(inRange);
|
||||
}));
|
||||
m_functions["u256_to_bool"_yulstring].returns = {"bool"_yulstring};
|
||||
}
|
||||
|
||||
EVMDialectTyped const& EVMDialectTyped::instance(langutil::EVMVersion _version)
|
||||
{
|
||||
static map<langutil::EVMVersion, unique_ptr<EVMDialectTyped const>> dialects;
|
||||
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
|
||||
if (!dialects[_version])
|
||||
dialects[_version] = make_unique<EVMDialectTyped>(_version, true);
|
||||
return *dialects[_version];
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ struct EVMDialect: public Dialect
|
||||
|
||||
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
|
||||
static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version);
|
||||
static EVMDialect const& yulForEVM(langutil::EVMVersion _version);
|
||||
|
||||
langutil::EVMVersion evmVersion() const { return m_evmVersion; }
|
||||
|
||||
@@ -88,4 +87,22 @@ protected:
|
||||
std::map<YulString, BuiltinFunctionForEVM> m_functions;
|
||||
};
|
||||
|
||||
/**
|
||||
* EVM dialect with types u256 (default) and bool.
|
||||
* Difference to EVMDialect:
|
||||
* - All comparison functions return type bool
|
||||
* - bitwise operations are called bitor, bitand, bitxor and bitnot
|
||||
* - and, or, xor take bool and return bool
|
||||
* - iszero is replaced by not, which takes bool and returns bool
|
||||
* - there are conversion functions bool_to_u256 and u256_to_bool.
|
||||
* - there is popbool
|
||||
*/
|
||||
struct EVMDialectTyped: public EVMDialect
|
||||
{
|
||||
/// Constructor, should only be used internally. Use the factory function below.
|
||||
EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectAccess);
|
||||
|
||||
static EVMDialectTyped const& instance(langutil::EVMVersion _version);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user