Provide inline assembly to the code generator. (#840)

* Directly usable inline assembly.

* Add missing header.
This commit is contained in:
chriseth 2016-08-16 16:27:20 +02:00 committed by GitHub
parent c547f9c24b
commit 77f4424589
5 changed files with 87 additions and 1 deletions

View File

@ -23,9 +23,12 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <utility>
#include <numeric>
#include <boost/algorithm/string/replace.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/interface/Version.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmStack.h>
using namespace std;
@ -172,6 +175,51 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
updateSourceLocation();
}
void CompilerContext::appendInlineAssembly(
string const& _assembly,
vector<string> const& _localVariables,
map<string, string> const& _replacements
)
{
string replacedAssembly;
string const* assembly = &_assembly;
if (!_replacements.empty())
{
replacedAssembly = _assembly;
for (auto const& replacement: _replacements)
replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second);
assembly = &replacedAssembly;
}
unsigned startStackHeight = stackHeight();
auto identifierAccess = [&](
assembly::Identifier const& _identifier,
eth::Assembly& _assembly,
assembly::CodeGenerator::IdentifierContext _context
) {
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name);
if (it == _localVariables.end())
return false;
unsigned stackDepth = _localVariables.end() - it;
int stackDiff = _assembly.deposit() - startStackHeight + stackDepth;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
errinfo_comment("Stack too deep, try removing local variables.")
);
if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
_assembly.append(dupInstruction(stackDiff));
else
{
_assembly.append(swapInstruction(stackDiff));
_assembly.append(Instruction::POP);
}
return true;
};
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, m_asm, identifierAccess), "");
}
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
{
eth::Assembly& sub = m_asm.sub(_subIndex);

View File

@ -132,6 +132,15 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
/// prior to parsing the inline assembly.
/// @param _localVariables assigns stack positions to variables with the last one being the stack top
void appendInlineAssembly(
std::string const& _assembly,
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{}
);
/// Prepends "PUSH <compiler version number> POP"
void injectVersionStampIntoSub(size_t _subIndex);

View File

@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolidity/ast/AST.h>

View File

@ -24,6 +24,7 @@
#include <memory>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/inlineasm/AsmParser.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
@ -32,7 +33,7 @@ using namespace dev;
using namespace dev::solidity;
using namespace dev::solidity::assembly;
bool InlineAssemblyStack::parse(const std::shared_ptr<Scanner>& _scanner)
bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner)
{
m_parserResult = make_shared<Block>();
Parser parser(m_errors);
@ -49,3 +50,22 @@ eth::Assembly InlineAssemblyStack::assemble()
return codeGen.assemble();
}
bool InlineAssemblyStack::parseAndAssemble(
string const& _input,
eth::Assembly& _assembly,
CodeGenerator::IdentifierAccess const& _identifierAccess
)
{
ErrorList errors;
auto scanner = make_shared<Scanner>(CharStream(_input), "--CODEGEN--");
auto parserResult = Parser(errors).parse(scanner);
if (!errors.empty())
return false;
CodeGenerator(*parserResult, errors).assemble(_assembly, _identifierAccess);
// At this point, the assembly might be messed up, but we should throw an
// internal compiler error anyway.
return errors.empty();
}

View File

@ -25,6 +25,7 @@
#include <string>
#include <functional>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/inlineasm/AsmCodeGen.h>
namespace dev
{
@ -47,6 +48,13 @@ public:
bool parse(std::shared_ptr<Scanner> const& _scanner);
eth::Assembly assemble();
/// Parse and assemble a string in one run - for use in Solidity code generation itself.
bool parseAndAssemble(
std::string const& _input,
eth::Assembly& _assembly,
CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess()
);
ErrorList const& errors() const { return m_errors; }
private: