mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
+16
-16
@@ -264,7 +264,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
if (f->literalArguments)
|
||||
needsLiteralArguments = &f->literalArguments.value();
|
||||
|
||||
warnOnInstructions(_funCall);
|
||||
validateInstructions(_funCall);
|
||||
}
|
||||
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
|
||||
[&](Scope::Variable const&)
|
||||
@@ -282,7 +282,7 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
||||
}
|
||||
}))
|
||||
{
|
||||
if (!warnOnInstructions(_funCall))
|
||||
if (!validateInstructions(_funCall))
|
||||
m_errorReporter.declarationError(4619_error, _funCall.functionName.location, "Function not found.");
|
||||
yulAssert(!watcher.ok(), "Expected a reported error.");
|
||||
}
|
||||
@@ -541,16 +541,16 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour
|
||||
);
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
||||
bool AsmAnalyzer::validateInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
|
||||
{
|
||||
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
|
||||
if (builtin && builtin->instruction.has_value())
|
||||
return warnOnInstructions(builtin->instruction.value(), _location);
|
||||
return validateInstructions(builtin->instruction.value(), _location);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation const& _location)
|
||||
bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocation const& _location)
|
||||
{
|
||||
// We assume that returndatacopy, returndatasize and staticcall are either all available
|
||||
// or all not available.
|
||||
@@ -558,9 +558,9 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
// Similarly we assume bitwise shifting and create2 go together.
|
||||
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
|
||||
|
||||
auto errorForVM = [&](string const& vmKindMessage) {
|
||||
auto errorForVM = [&](ErrorId _errorId, string const& vmKindMessage) {
|
||||
m_errorReporter.typeError(
|
||||
7079_error,
|
||||
_errorId,
|
||||
_location,
|
||||
"The \"" +
|
||||
boost::to_lower_copy(instructionInfo(_instr).name)
|
||||
@@ -577,25 +577,24 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
_instr == evmasm::Instruction::RETURNDATACOPY ||
|
||||
_instr == evmasm::Instruction::RETURNDATASIZE
|
||||
) && !m_evmVersion.supportsReturndata())
|
||||
errorForVM("only available for Byzantium-compatible");
|
||||
errorForVM(7756_error, "only available for Byzantium-compatible");
|
||||
else if (_instr == evmasm::Instruction::STATICCALL && !m_evmVersion.hasStaticCall())
|
||||
errorForVM("only available for Byzantium-compatible");
|
||||
errorForVM(1503_error, "only available for Byzantium-compatible");
|
||||
else if ((
|
||||
_instr == evmasm::Instruction::SHL ||
|
||||
_instr == evmasm::Instruction::SHR ||
|
||||
_instr == evmasm::Instruction::SAR
|
||||
) && !m_evmVersion.hasBitwiseShifting())
|
||||
errorForVM("only available for Constantinople-compatible");
|
||||
errorForVM(6612_error, "only available for Constantinople-compatible");
|
||||
else if (_instr == evmasm::Instruction::CREATE2 && !m_evmVersion.hasCreate2())
|
||||
errorForVM("only available for Constantinople-compatible");
|
||||
errorForVM(6166_error, "only available for Constantinople-compatible");
|
||||
else if (_instr == evmasm::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
||||
errorForVM("only available for Constantinople-compatible");
|
||||
errorForVM(7110_error, "only available for Constantinople-compatible");
|
||||
else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID())
|
||||
errorForVM("only available for Istanbul-compatible");
|
||||
errorForVM(1561_error, "only available for Istanbul-compatible");
|
||||
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
|
||||
errorForVM("only available for Istanbul-compatible");
|
||||
errorForVM(7721_error, "only available for Istanbul-compatible");
|
||||
else if (_instr == evmasm::Instruction::PC)
|
||||
{
|
||||
m_errorReporter.error(
|
||||
2450_error,
|
||||
Error::Type::SyntaxError,
|
||||
@@ -603,7 +602,8 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
"PC instruction is a low-level EVM feature. "
|
||||
"Because of that PC is disallowed in strict assembly."
|
||||
);
|
||||
}
|
||||
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
|
||||
errorForVM(3672_error, "only available for Istanbul-compatible");
|
||||
else if (
|
||||
_instr == evmasm::Instruction::JUMP ||
|
||||
_instr == evmasm::Instruction::JUMPI ||
|
||||
|
||||
@@ -110,12 +110,12 @@ private:
|
||||
Scope& scope(Block const* _block);
|
||||
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(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
||||
|
||||
bool warnOnInstructions(FunctionCall const& _functionCall)
|
||||
bool validateInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location);
|
||||
bool validateInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
|
||||
bool validateInstructions(FunctionCall const& _functionCall)
|
||||
{
|
||||
return warnOnInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.location);
|
||||
return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.location);
|
||||
}
|
||||
|
||||
yul::ExternalIdentifierAccess::Resolver m_resolver;
|
||||
|
||||
+10
-11
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <libyul/AsmJsonConverter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
@@ -38,7 +38,7 @@ Json::Value AsmJsonConverter::operator()(Block const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(TypedName const& _node) const
|
||||
{
|
||||
solAssert(!_node.name.empty(), "Invalid variable name.");
|
||||
yulAssert(!_node.name.empty(), "Invalid variable name.");
|
||||
Json::Value ret = createAstNode(_node.location, "YulTypedName");
|
||||
ret["name"] = _node.name.str();
|
||||
ret["type"] = _node.type.str();
|
||||
@@ -51,7 +51,7 @@ Json::Value AsmJsonConverter::operator()(Literal const& _node) const
|
||||
switch (_node.kind)
|
||||
{
|
||||
case LiteralKind::Number:
|
||||
solAssert(
|
||||
yulAssert(
|
||||
util::isValidDecimal(_node.value.str()) || util::isValidHex(_node.value.str()),
|
||||
"Invalid number literal"
|
||||
);
|
||||
@@ -71,7 +71,7 @@ Json::Value AsmJsonConverter::operator()(Literal const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(Identifier const& _node) const
|
||||
{
|
||||
solAssert(!_node.name.empty(), "Invalid identifier");
|
||||
yulAssert(!_node.name.empty(), "Invalid identifier");
|
||||
Json::Value ret = createAstNode(_node.location, "YulIdentifier");
|
||||
ret["name"] = _node.name.str();
|
||||
return ret;
|
||||
@@ -79,7 +79,7 @@ Json::Value AsmJsonConverter::operator()(Identifier const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(Assignment const& _node) const
|
||||
{
|
||||
solAssert(_node.variableNames.size() >= 1, "Invalid assignment syntax");
|
||||
yulAssert(_node.variableNames.size() >= 1, "Invalid assignment syntax");
|
||||
Json::Value ret = createAstNode(_node.location, "YulAssignment");
|
||||
for (auto const& var: _node.variableNames)
|
||||
ret["variableNames"].append((*this)(var));
|
||||
@@ -115,7 +115,7 @@ Json::Value AsmJsonConverter::operator()(VariableDeclaration const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const
|
||||
{
|
||||
solAssert(!_node.name.empty(), "Invalid function name.");
|
||||
yulAssert(!_node.name.empty(), "Invalid function name.");
|
||||
Json::Value ret = createAstNode(_node.location, "YulFunctionDefinition");
|
||||
ret["name"] = _node.name.str();
|
||||
for (auto const& var: _node.parameters)
|
||||
@@ -128,7 +128,7 @@ Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(If const& _node) const
|
||||
{
|
||||
solAssert(_node.condition, "Invalid if condition.");
|
||||
yulAssert(_node.condition, "Invalid if condition.");
|
||||
Json::Value ret = createAstNode(_node.location, "YulIf");
|
||||
ret["condition"] = std::visit(*this, *_node.condition);
|
||||
ret["body"] = (*this)(_node.body);
|
||||
@@ -137,7 +137,7 @@ Json::Value AsmJsonConverter::operator()(If const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(Switch const& _node) const
|
||||
{
|
||||
solAssert(_node.expression, "Invalid expression pointer.");
|
||||
yulAssert(_node.expression, "Invalid expression pointer.");
|
||||
Json::Value ret = createAstNode(_node.location, "YulSwitch");
|
||||
ret["expression"] = std::visit(*this, *_node.expression);
|
||||
for (auto const& var: _node.cases)
|
||||
@@ -155,14 +155,14 @@ Json::Value AsmJsonConverter::operator()(Case const& _node) const
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(ForLoop const& _node) const
|
||||
{
|
||||
solAssert(_node.condition, "Invalid for loop condition.");
|
||||
yulAssert(_node.condition, "Invalid for loop condition.");
|
||||
Json::Value ret = createAstNode(_node.location, "YulForLoop");
|
||||
ret["pre"] = (*this)(_node.pre);
|
||||
ret["condition"] = std::visit(*this, *_node.condition);
|
||||
ret["post"] = (*this)(_node.post);
|
||||
ret["body"] = (*this)(_node.body);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value AsmJsonConverter::operator()(Break const& _node) const
|
||||
{
|
||||
@@ -196,7 +196,6 @@ Json::Value AsmJsonConverter::vectorOfVariantsToJson(vector<T> const& _vec) cons
|
||||
Json::Value ret{Json::arrayValue};
|
||||
for (auto const& var: _vec)
|
||||
ret.append(std::visit(*this, var));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ void AssemblyStack::translate(AssemblyStack::Language _targetLanguage)
|
||||
if (m_language == _targetLanguage)
|
||||
return;
|
||||
|
||||
solAssert(
|
||||
yulAssert(
|
||||
m_language == Language::StrictAssembly && _targetLanguage == Language::Ewasm,
|
||||
"Invalid language combination"
|
||||
);
|
||||
@@ -160,7 +160,7 @@ void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _o
|
||||
dialect = &EVMDialectTyped::instance(m_evmVersion);
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Invalid language.");
|
||||
yulAssert(false, "Invalid language.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -218,8 +218,9 @@ void CodeGenerator::assemble(
|
||||
}
|
||||
catch (StackTooDeepError const& _e)
|
||||
{
|
||||
yulAssert(
|
||||
assertThrow(
|
||||
false,
|
||||
langutil::StackTooDeepError,
|
||||
"Stack too deep when compiling inline assembly" +
|
||||
(_e.comment() ? ": " + *_e.comment() : ".")
|
||||
);
|
||||
|
||||
@@ -93,7 +93,7 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
||||
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void(Expression const&)>)> _generateCode
|
||||
)
|
||||
{
|
||||
solAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
|
||||
yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
|
||||
|
||||
YulString name{std::move(_name)};
|
||||
BuiltinFunctionForEVM f;
|
||||
@@ -124,19 +124,18 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
)
|
||||
builtins.emplace(createEVMFunction(instr.first, instr.second));
|
||||
|
||||
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, [](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext&,
|
||||
function<void(Expression const&)>
|
||||
) {
|
||||
yulAssert(_call.arguments.size() == 1, "");
|
||||
Expression const& arg = _call.arguments.front();
|
||||
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
|
||||
}));
|
||||
|
||||
if (_objectAccess)
|
||||
{
|
||||
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, [](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext&,
|
||||
function<void(Expression const&)>
|
||||
) {
|
||||
yulAssert(_call.arguments.size() == 1, "");
|
||||
Expression const& arg = _call.arguments.front();
|
||||
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
|
||||
}));
|
||||
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, [](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
@@ -207,7 +206,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
BuiltinContext&,
|
||||
std::function<void(Expression const&)> _visitExpression
|
||||
) {
|
||||
solAssert(_call.arguments.size() == 2, "");
|
||||
yulAssert(_call.arguments.size() == 2, "");
|
||||
|
||||
_visitExpression(_call.arguments[1]);
|
||||
_assembly.setSourceLocation(_call.location);
|
||||
@@ -227,7 +226,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
BuiltinContext&,
|
||||
std::function<void(Expression const&)>
|
||||
) {
|
||||
solAssert(_call.arguments.size() == 1, "");
|
||||
yulAssert(_call.arguments.size() == 1, "");
|
||||
_assembly.appendImmutable(std::get<Literal>(_call.arguments.front()).value.str());
|
||||
}
|
||||
));
|
||||
|
||||
@@ -253,6 +253,21 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
|
||||
// assignment to slot contents denoted by "name"
|
||||
m_memory.eraseValue(name);
|
||||
}
|
||||
|
||||
if (_value && _variables.size() == 1)
|
||||
{
|
||||
YulString variable = *_variables.begin();
|
||||
if (!movableChecker.referencedVariables().count(variable))
|
||||
{
|
||||
// This might erase additional knowledge about the slot.
|
||||
// On the other hand, if we knew the value in the slot
|
||||
// already, then the sload() / mload() would have been replaced by a variable anyway.
|
||||
if (auto key = isSimpleLoad(evmasm::Instruction::MLOAD, *_value))
|
||||
m_memory.set(*key, variable);
|
||||
else if (auto key = isSimpleLoad(evmasm::Instruction::SLOAD, *_value))
|
||||
m_storage.set(*key, variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::pushScope(bool _functionScope)
|
||||
@@ -401,3 +416,25 @@ std::optional<pair<YulString, YulString>> DataFlowAnalyzer::isSimpleStore(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<YulString> DataFlowAnalyzer::isSimpleLoad(
|
||||
evmasm::Instruction _load,
|
||||
Expression const& _expression
|
||||
) const
|
||||
{
|
||||
yulAssert(
|
||||
_load == evmasm::Instruction::MLOAD ||
|
||||
_load == evmasm::Instruction::SLOAD,
|
||||
""
|
||||
);
|
||||
if (holds_alternative<FunctionCall>(_expression))
|
||||
{
|
||||
FunctionCall const& funCall = std::get<FunctionCall>(_expression);
|
||||
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
|
||||
if (auto const* builtin = dialect->builtin(funCall.functionName.name))
|
||||
if (builtin->instruction == _load)
|
||||
if (holds_alternative<Identifier>(funCall.arguments.at(0)))
|
||||
return std::get<Identifier>(funCall.arguments.at(0)).name;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -142,11 +142,20 @@ protected:
|
||||
/// Returns true iff the variable is in scope.
|
||||
bool inScope(YulString _variableName) const;
|
||||
|
||||
/// Checks if the statement is sstore(a, b) / mstore(a, b)
|
||||
/// where a and b are variables and returns these variables in that case.
|
||||
std::optional<std::pair<YulString, YulString>> isSimpleStore(
|
||||
evmasm::Instruction _store,
|
||||
ExpressionStatement const& _statement
|
||||
) const;
|
||||
|
||||
/// Checks if the expression is sload(a) / mload(a)
|
||||
/// where a is a variable and returns the variable in that case.
|
||||
std::optional<YulString> isSimpleLoad(
|
||||
evmasm::Instruction _load,
|
||||
Expression const& _expression
|
||||
) const;
|
||||
|
||||
Dialect const& m_dialect;
|
||||
/// Side-effects of user-defined functions. Worst-case side-effects are assumed
|
||||
/// if this is not provided or the function is not found.
|
||||
|
||||
@@ -194,7 +194,7 @@ void IntroduceControlFlowSSA::operator()(FunctionDefinition& _function)
|
||||
|
||||
void IntroduceControlFlowSSA::operator()(ForLoop& _for)
|
||||
{
|
||||
(*this)(_for.pre);
|
||||
yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
@@ -357,11 +357,7 @@ void PropagateValues::operator()(Assignment& _assignment)
|
||||
|
||||
void PropagateValues::operator()(ForLoop& _for)
|
||||
{
|
||||
// This will clear the current value in case of a reassignment inside the
|
||||
// init part, although the new variable would still be in scope inside the whole loop.
|
||||
// This small inefficiency is fine if we move the pre part of all for loops out
|
||||
// of the for loop.
|
||||
(*this)(_for.pre);
|
||||
yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
|
||||
@@ -85,7 +85,7 @@ class NameDispenser;
|
||||
*
|
||||
* TODO Which transforms are required to keep this idempotent?
|
||||
*
|
||||
* Prerequisite: Disambiguator.
|
||||
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||
*/
|
||||
class SSATransform: public ASTModifier
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user