Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2020-07-06 15:25:25 +02:00
118 changed files with 1713 additions and 380 deletions
+16 -16
View File
@@ -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 ||
+4 -4
View File
@@ -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
View File
@@ -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;
}
+2 -2
View File
@@ -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;
}
+2 -1
View File
@@ -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() : ".")
);
+13 -14
View File
@@ -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());
}
));
+37
View File
@@ -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 {};
}
+9
View File
@@ -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.
+2 -6
View File
@@ -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);
+1 -1
View File
@@ -85,7 +85,7 @@ class NameDispenser;
*
* TODO Which transforms are required to keep this idempotent?
*
* Prerequisite: Disambiguator.
* Prerequisite: Disambiguator, ForLoopInitRewriter.
*/
class SSATransform: public ASTModifier
{