Refactor CodeGen to recurse on blocks.

This commit is contained in:
chriseth 2017-02-15 16:47:54 +01:00
parent 7f2def8971
commit 25dcfa3480
4 changed files with 97 additions and 75 deletions

View File

@ -593,7 +593,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
// code-generator and see whether it produces any errors. // code-generator and see whether it produces any errors.
// External references have already been resolved in a prior stage and stored in the annotation. // External references have already been resolved in a prior stage and stored in the annotation.
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors); assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) { if (!codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end()) if (ref == _inlineAssembly.annotation().externalReferences.end())
return false; return false;
@ -641,8 +641,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return false; return false;
} }
return true; return true;
}); }))
return false; return false;
return true;
} }
bool TypeChecker::visit(IfStatement const& _ifStatement) bool TypeChecker::visit(IfStatement const& _ifStatement)

View File

@ -36,7 +36,7 @@ using namespace dev::solidity;
using namespace dev::solidity::assembly; using namespace dev::solidity::assembly;
bool Scope::registerLabel(const string& _name, size_t _id) bool Scope::registerLabel(string const& _name, size_t _id)
{ {
if (lookup(_name)) if (lookup(_name))
return false; return false;
@ -45,7 +45,7 @@ bool Scope::registerLabel(const string& _name, size_t _id)
} }
bool Scope::registerVariable(const string& _name) bool Scope::registerVariable(string const& _name)
{ {
if (lookup(_name)) if (lookup(_name))
return false; return false;
@ -125,7 +125,7 @@ bool AsmAnalyzer::operator()(Label const& _item)
} }
label.resetStackHeight = true; label.resetStackHeight = true;
for (auto const& stackItem: _item.stackInfo) for (auto const& stackItem: _item.stackInfo)
{ if (!stackItem.empty())
if (!m_currentScope->registerVariable(stackItem)) if (!m_currentScope->registerVariable(stackItem))
{ {
//@TODO secondary location //@TODO secondary location
@ -137,11 +137,10 @@ bool AsmAnalyzer::operator()(Label const& _item)
success = false; success = false;
} }
} }
}
return success; return success;
} }
bool AsmAnalyzer::operator()(const FunctionalAssignment& _assignment) bool AsmAnalyzer::operator()(FunctionalAssignment const& _assignment)
{ {
return boost::apply_visitor(*this, *_assignment.value); return boost::apply_visitor(*this, *_assignment.value);
} }

View File

@ -33,6 +33,8 @@
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <memory> #include <memory>
#include <functional> #include <functional>
@ -77,15 +79,42 @@ public:
/// @param _identifierAccess used to resolve identifiers external to the inline assembly /// @param _identifierAccess used to resolve identifiers external to the inline assembly
explicit CodeTransform( explicit CodeTransform(
GeneratorState& _state, GeneratorState& _state,
assembly::Block const& _block,
assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess() assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess()
): ):
m_state(_state) m_state(_state),
m_scope(*m_state.scopes.at(&_block)),
m_initialDeposit(m_state.assembly.deposit()),
m_identifierAccess(_identifierAccess)
{ {
if (_identifierAccess) std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
m_identifierAccess = _identifierAccess;
else m_state.assembly.setSourceLocation(_block.location);
m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; };
m_currentScope = m_state.scopes[nullptr].get(); // pop variables
for (auto const& identifier: m_scope.identifiers)
if (identifier.second.type() == typeid(Scope::Variable))
m_state.assembly.append(solidity::Instruction::POP);
int deposit = m_state.assembly.deposit() - m_initialDeposit;
// issue warnings for stack height discrepancies
if (deposit < 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
_block.location
);
}
else if (deposit > 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
_block.location
);
}
} }
void operator()(assembly::Instruction const& _instruction) void operator()(assembly::Instruction const& _instruction)
@ -108,7 +137,7 @@ public:
{ {
m_state.assembly.setSourceLocation(_identifier.location); m_state.assembly.setSourceLocation(_identifier.location);
// First search internals, then externals. // First search internals, then externals.
if (!m_currentScope->lookup(_identifier.name, Scope::NonconstVisitor( if (m_scope.lookup(_identifier.name, Scope::NonconstVisitor(
[=](Scope::Variable& _var) [=](Scope::Variable& _var)
{ {
if (!_var.active) if (!_var.active)
@ -138,9 +167,14 @@ public:
{ {
if (_label.id == Scope::Label::unassignedLabelId) if (_label.id == Scope::Label::unassignedLabelId)
_label.id = m_state.newLabelId(); _label.id = m_state.newLabelId();
else if (_label.id == Scope::Label::errorLabelId)
_label.id = size_t(m_state.assembly.errorTag().data());
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id)); m_state.assembly.append(eth::AssemblyItem(eth::PushTag, _label.id));
} }
))) )))
{
}
else if (!m_identifierAccess || !m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue))
{ {
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
@ -166,13 +200,33 @@ public:
} }
void operator()(Label const& _label) void operator()(Label const& _label)
{ {
solAssert(_label.stackInfo.empty(), "Labels with stack info not yet supported.");
m_state.assembly.setSourceLocation(_label.location); m_state.assembly.setSourceLocation(_label.location);
solAssert(m_currentScope->identifiers.count(_label.name), ""); solAssert(m_scope.identifiers.count(_label.name), "");
Scope::Label& label = boost::get<Scope::Label>(m_currentScope->identifiers[_label.name]); Scope::Label& label = boost::get<Scope::Label>(m_scope.identifiers[_label.name]);
if (label.id == Scope::Label::unassignedLabelId) if (label.id == Scope::Label::unassignedLabelId)
label.id = m_state.newLabelId(); label.id = m_state.newLabelId();
m_state.assembly.append(eth::AssemblyItem(eth::PushTag, label.id)); else if (label.id == Scope::Label::errorLabelId)
label.id = size_t(m_state.assembly.errorTag().data());
if (label.resetStackHeight)
{
size_t numVariables = boost::range::count_if(
m_scope.identifiers | boost::adaptors::map_values,
[](Scope::Identifier const& var) { return var.type() == typeid(Scope::Variable) && boost::get<Scope::Variable>(var).active; }
);
for (auto const& identifier: _label.stackInfo)
if (!identifier.empty())
{
solAssert(m_scope.identifiers.count(identifier), "");
Scope::Variable& var = boost::get<Scope::Variable>(m_scope.identifiers[identifier]);
var.active = true;
var.stackHeight = m_initialDeposit + numVariables;
numVariables++;
}
m_state.assembly.setDeposit(m_initialDeposit + numVariables);
}
else if (label.stackAdjustment != 0)
m_state.assembly.adjustDeposit(label.stackAdjustment);
m_state.assembly.append(eth::AssemblyItem(eth::Tag, label.id));
} }
void operator()(assembly::Assignment const& _assignment) void operator()(assembly::Assignment const& _assignment)
{ {
@ -192,46 +246,14 @@ public:
int height = m_state.assembly.deposit(); int height = m_state.assembly.deposit();
boost::apply_visitor(*this, *_varDecl.value); boost::apply_visitor(*this, *_varDecl.value);
expectDeposit(1, height, locationOf(*_varDecl.value)); expectDeposit(1, height, locationOf(*_varDecl.value));
solAssert(m_currentScope && m_currentScope->identifiers.count(_varDecl.name), ""); solAssert(m_scope.identifiers.count(_varDecl.name), "");
auto& var = boost::get<Scope::Variable>(m_currentScope->identifiers[_varDecl.name]); auto& var = boost::get<Scope::Variable>(m_scope.identifiers[_varDecl.name]);
var.stackHeight = height; var.stackHeight = height;
var.active = true; var.active = true;
} }
void operator()(assembly::Block const& _block) void operator()(assembly::Block const& _block)
{ {
solAssert(m_state.scopes.count(&_block), "Scope for block not defined."); CodeTransform(m_state, _block, m_identifierAccess);
m_currentScope = m_state.scopes[&_block].get();
int deposit = m_state.assembly.deposit();
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
m_state.assembly.setSourceLocation(_block.location);
// pop variables
for (auto const& identifier: m_currentScope->identifiers)
if (identifier.second.type() == typeid(Scope::Variable))
m_state.assembly.append(solidity::Instruction::POP);
deposit = m_state.assembly.deposit() - deposit;
// issue warnings for stack height discrepancies
if (deposit < 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
_block.location
);
}
else if (deposit > 0)
{
m_state.addError(
Error::Type::Warning,
"Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
_block.location
);
}
m_currentScope = m_currentScope->superScope;
} }
void operator()(assembly::FunctionDefinition const&) void operator()(assembly::FunctionDefinition const&)
{ {
@ -241,7 +263,7 @@ public:
private: private:
void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location) void generateAssignment(assembly::Identifier const& _variableName, SourceLocation const& _location)
{ {
if (!m_currentScope->lookup(_variableName.name, Scope::Visitor( if (m_scope.lookup(_variableName.name, Scope::Visitor(
[=](Scope::Variable const& _var) [=](Scope::Variable const& _var)
{ {
if (!_var.active) if (!_var.active)
@ -272,7 +294,10 @@ private:
"Label \"" + string(_variableName.name) + "\" used as variable." "Label \"" + string(_variableName.name) + "\" used as variable."
); );
} }
)) && !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue)) )))
{
}
else if (!m_identifierAccess || !m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue))
m_state.addError( m_state.addError(
Error::Type::DeclarationError, Error::Type::DeclarationError,
"Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue." "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue."
@ -293,7 +318,8 @@ private:
} }
GeneratorState& m_state; GeneratorState& m_state;
Scope* m_currentScope; Scope& m_scope;
int const m_initialDeposit;
assembly::CodeGenerator::IdentifierAccess m_identifierAccess; assembly::CodeGenerator::IdentifierAccess m_identifierAccess;
}; };
@ -304,29 +330,25 @@ bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAcces
GeneratorState state(m_errors, assembly); GeneratorState state(m_errors, assembly);
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData)) if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
return false; return false;
(CodeTransform(state, _identifierAccess))(m_parsedData); CodeTransform(state, m_parsedData, _identifierAccess);
return m_errors.size() == initialErrorLen; return m_errors.size() == initialErrorLen;
} }
eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
{ {
size_t initialErrorLen = m_errors.size();
eth::Assembly assembly; eth::Assembly assembly;
GeneratorState state(m_errors, assembly); GeneratorState state(m_errors, assembly);
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData)) if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
solAssert(false, "Assembly error"); solAssert(false, "Assembly error");
(CodeTransform(state, _identifierAccess))(m_parsedData); CodeTransform(state, m_parsedData, _identifierAccess);
solAssert(m_errors.size() == initialErrorLen, "Assembly error");
return assembly; return assembly;
} }
void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) void assembly::CodeGenerator::assemble(eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierAccess const& _identifierAccess)
{ {
size_t initialErrorLen = m_errors.size();
GeneratorState state(m_errors, _assembly); GeneratorState state(m_errors, _assembly);
if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData)) if (!(AsmAnalyzer(state.scopes, m_errors))(m_parsedData))
solAssert(false, "Assembly error"); solAssert(false, "Assembly error");
(CodeTransform(state, _identifierAccess))(m_parsedData); CodeTransform(state, m_parsedData, _identifierAccess);
solAssert(m_errors.size() == initialErrorLen, "Assembly error");
} }

View File

@ -140,7 +140,7 @@ assembly::Statement Parser::parseStatement()
label.stackInfo.push_back("-" + m_scanner->currentLiteral()); label.stackInfo.push_back("-" + m_scanner->currentLiteral());
expectToken(Token::Number); expectToken(Token::Number);
} }
else else if (m_scanner->currentToken() != Token::RBrack)
while (true) while (true)
{ {
label.stackInfo.push_back(expectAsmIdentifier()); label.stackInfo.push_back(expectAsmIdentifier());