Skip to content

Add support for importing asm-json. #11807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
235af2b
[solc] Add --import-asm-json input mode.
aarlt Nov 4, 2021
9a3e248
[libsolidity] Prepare evm assembly json import.
aarlt Nov 8, 2021
34492b3
[solc] Enable handling for InputMode::CompilerWithEvmAssemblyJsonImport.
aarlt Nov 9, 2021
f11044d
[libevmasm] Add support for assembly json import.
aarlt Nov 9, 2021
4473b3c
[libsolidity] Basic output generation of assembly json import.
aarlt Nov 9, 2021
4f2056f
[libsolidity] Add basic optimizer support for assembly json import.
aarlt Nov 9, 2021
200684a
[libsolidity] Preserve sourceList order, if assembly json import is u…
aarlt Nov 9, 2021
ef150b7
[libevmasm] Add more tests.
aarlt Nov 9, 2021
8608bc3
[libsolidity] sourceIndices: selectable internal sources.
aarlt Nov 9, 2021
c822263
[libsolidity] srcmap sourceList handling corrections.
aarlt Nov 9, 2021
dcb697f
[libevmasm] loadFromAssemblyJSON: optionally load from sourceList.
aarlt Nov 9, 2021
2855921
[test] Add some assembly json import tests.
aarlt Nov 10, 2021
6636817
[libsolidity] temporary disable optimization for assembly json import.
aarlt Nov 10, 2021
3de13c4
[solc] CommandLineParser: Fix Coding style error.
aarlt Nov 10, 2021
5d74861
[libevmasm] Add 'modifierDepth' to assembly json.
aarlt Nov 12, 2021
ba62b1a
[libsolidity] OptimiserSettings: add enabled flag.
aarlt Nov 12, 2021
5c3a618
[solc] Set flag to indicate whether optimiser was enabled.
aarlt Nov 12, 2021
ed9c09b
[scripts/AsmJsonImportTest.sh] Enable checks of source maps & minor i…
aarlt Nov 12, 2021
9721c82
[solc] Improve parameter checks for --import-asm-json.
aarlt Nov 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
423 changes: 335 additions & 88 deletions libevmasm/Assembly.cpp

Large diffs are not rendered by default.

25 changes: 23 additions & 2 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <sstream>
#include <memory>
#include <map>
#include <utility>

namespace solidity::evmasm
{
Expand Down Expand Up @@ -155,15 +156,28 @@ class Assembly

/// Create a JSON representation of the assembly.
Json::Value assemblyJSON(
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>(),
bool _includeSourceList = true
) const;

bool loadFromAssemblyJSON(Json::Value const& _json, bool _loadSources = true);

/// Mark this assembly as invalid. Calling ``assemble`` on it will throw.
void markAsInvalid() { m_invalid = true; }

std::vector<size_t> decodeSubPath(size_t _subObjectId) const;
size_t encodeSubPath(std::vector<size_t> const& _subPath);

void setSources(std::vector<std::shared_ptr<std::string const>> _sources) {
m_sources = std::move(_sources);
}

void setSources(std::vector<std::string> const& _sources) {
for (auto const& item: _sources)
m_sources.emplace_back(std::make_shared<std::string>(item));
}
std::vector<std::shared_ptr<std::string const>> sources() const& { return m_sources; }

protected:
/// Does the same operations as @a optimise, but should only be applied to a sub and
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
Expand All @@ -172,10 +186,15 @@ class Assembly

unsigned codeSize(unsigned subTagSize) const;

AssemblyItem loadItemFromJSON(Json::Value const& _json);
std::vector<Json::Value> assemblyItemAsJSON(AssemblyItem const& _item, int _sourceIndex) const;

private:
bool addAssemblyItemsFromJSON(Json::Value const& _code);
static Json::Value createJsonValue(
std::string _name,
int _source,
int _sourceIndex,
size_t _modifierDepth,
int _begin,
int _end,
std::string _value = std::string(),
Expand Down Expand Up @@ -226,6 +245,8 @@ class Assembly
std::string m_name;

langutil::SourceLocation m_currentSourceLocation;
std::vector<std::shared_ptr<std::string const>> m_sources;

public:
size_t m_currentModifierDepth = 0;
};
Expand Down
12 changes: 12 additions & 0 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ string AssemblyItem::getJumpTypeAsString() const
}
}

void AssemblyItem::setJumpType(std::string const& _jumpType)
{
if (_jumpType == "[in]")
m_jumpType = JumpType::IntoFunction;
else if (_jumpType == "[out]")
m_jumpType = JumpType::OutOfFunction;
else if (_jumpType.empty())
m_jumpType = JumpType::Ordinary;
else
assertThrow(false, AssemblyException, "Invalid jump type.");
}

string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
{
string text;
Expand Down
1 change: 1 addition & 0 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class AssemblyItem
langutil::SourceLocation const& location() const { return m_location; }

void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; }
void setJumpType(std::string const& _jumpType);
JumpType getJumpType() const { return m_jumpType; }
std::string getJumpTypeAsString() const;

Expand Down
167 changes: 115 additions & 52 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,28 @@ bool CompilerStack::parse()
return !m_hasError;
}

void CompilerStack::importEvmAssemblyJson(map<string, Json::Value> const& _sources)
{
solAssert(_sources.size() == 1, "");
solAssert(m_sources.empty(), "");
solAssert(m_sourceOrder.empty(), "");
if (m_stackState != Empty)
solThrow(CompilerError, "Must call importEvmAssemblyJson only before the SourcesSet state.");

Json::Value jsonValue = _sources.begin()->second;
if (jsonValue.isMember("sourceList"))
for (auto const& item: jsonValue["sourceList"])
{
Source source;
source.charStream = std::make_shared<CharStream>(item.asString(), "");
m_sources.emplace(std::make_pair(item.asString(), source));
m_sourceOrder.push_back(&m_sources[item.asString()]);
}
m_evmAssemblyJson[_sources.begin()->first] = jsonValue;
m_importedSources = true;
m_stackState = SourcesSet;
}

void CompilerStack::importASTs(map<string, Json::Value> const& _sources)
{
if (m_stackState != Empty)
Expand Down Expand Up @@ -597,6 +619,9 @@ bool CompilerStack::parseAndAnalyze(State _stopAfter)
{
m_stopAfter = _stopAfter;

if (!m_evmAssemblyJson.empty())
return true;

bool success = parse();
if (m_stackState >= m_stopAfter)
return success;
Expand Down Expand Up @@ -643,58 +668,88 @@ bool CompilerStack::compile(State _stopAfter)
if (m_hasError)
solThrow(CompilerError, "Called compile with errors.");

// Only compile contracts individually which have been requested.
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
if (!m_evmAssemblyJson.empty())
{
solAssert(m_importedSources, "");
solAssert(m_evmAssemblyJson.size() == 1, "");

string const evmAssemblyJsonSource = m_evmAssemblyJson.begin()->first;

evmasm::Assembly::OptimiserSettings optimiserSettings;
optimiserSettings.evmVersion = m_evmVersion;
optimiserSettings.expectedExecutionsPerDeployment = m_optimiserSettings.expectedExecutionsPerDeployment;
optimiserSettings.runCSE = m_optimiserSettings.runCSE;
optimiserSettings.runConstantOptimiser = m_optimiserSettings.runConstantOptimiser;
optimiserSettings.runDeduplicate = m_optimiserSettings.runDeduplicate;
optimiserSettings.runInliner = m_optimiserSettings.runInliner;
optimiserSettings.runJumpdestRemover = m_optimiserSettings.runJumpdestRemover;
optimiserSettings.runPeephole = m_optimiserSettings.runPeephole;

m_contracts[evmAssemblyJsonSource].evmAssembly = make_shared<evmasm::Assembly>(evmAssemblyJsonSource);
m_contracts[evmAssemblyJsonSource].evmAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmAssemblyJsonSource]);
if (m_optimiserSettings.enabled)
m_contracts[evmAssemblyJsonSource].evmAssembly->optimise(optimiserSettings);
m_contracts[evmAssemblyJsonSource].object = m_contracts[evmAssemblyJsonSource].evmAssembly->assemble();

m_contracts[evmAssemblyJsonSource].evmRuntimeAssembly = make_shared<evmasm::Assembly>(evmAssemblyJsonSource);
m_contracts[evmAssemblyJsonSource].evmRuntimeAssembly->setSources(m_contracts[evmAssemblyJsonSource].evmAssembly->sources());
m_contracts[evmAssemblyJsonSource].evmRuntimeAssembly->loadFromAssemblyJSON(m_evmAssemblyJson[evmAssemblyJsonSource][".data"]["0"], false);
if (m_optimiserSettings.enabled)
m_contracts[evmAssemblyJsonSource].evmRuntimeAssembly->optimise(optimiserSettings);
m_contracts[evmAssemblyJsonSource].runtimeObject = m_contracts[evmAssemblyJsonSource].evmRuntimeAssembly->assemble();
}
else
{
// Only compile contracts individually which have been requested.
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;

for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
{
try
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
{
if (m_viaIR || m_generateIR || m_generateEwasm)
generateIR(*contract);
if (m_generateEvmBytecode)
try
{
if (m_viaIR)
generateEVMFromIR(*contract);
else
compileContract(*contract, otherCompilers);
if (m_viaIR || m_generateIR || m_generateEwasm)
generateIR(*contract);
if (m_generateEvmBytecode)
{
if (m_viaIR)
generateEVMFromIR(*contract);
else
compileContract(*contract, otherCompilers);
}
if (m_generateEwasm)
generateEwasm(*contract);
}
if (m_generateEwasm)
generateEwasm(*contract);
}
catch (Error const& _error)
{
if (_error.type() != Error::Type::CodeGenerationError)
throw;
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
return false;
}
catch (UnimplementedFeatureError const& _unimplementedError)
{
if (
SourceLocation const* sourceLocation =
boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError)
)
catch (Error const& _error)
{
string const* comment = _unimplementedError.comment();
m_errorReporter.error(
1834_error,
Error::Type::CodeGenerationError,
*sourceLocation,
"Unimplemented feature error" +
((comment && !comment->empty()) ? ": " + *comment : string{}) +
" in " +
_unimplementedError.lineInfo()
);
if (_error.type() != Error::Type::CodeGenerationError)
throw;
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
return false;
}
else
throw;
catch (UnimplementedFeatureError const& _unimplementedError)
{
if (SourceLocation const* sourceLocation
= boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError))
{
string const* comment = _unimplementedError.comment();
m_errorReporter.error(
1834_error,
Error::Type::CodeGenerationError,
*sourceLocation,
"Unimplemented feature error"
+ ((comment && !comment->empty()) ? ": " + *comment : string{}) + " in "
+ _unimplementedError.lineInfo());
return false;
}
else
throw;
}
}
}
}

m_stackState = CompilationSuccessful;
this->link();
return true;
Expand Down Expand Up @@ -924,20 +979,28 @@ Json::Value CompilerStack::assemblyJSON(string const& _contractName) const

vector<string> CompilerStack::sourceNames() const
{
vector<string> names;
for (auto const& s: m_sources)
names.push_back(s.first);
map<string, unsigned> indices = sourceIndices(false);
vector<string> names(indices.size());
for (auto const& s: indices)
names[s.second] = s.first;
return names;
}

map<string, unsigned> CompilerStack::sourceIndices() const
map<string, unsigned> CompilerStack::sourceIndices(bool _includeInternalSources /* = true */) const
{
map<string, unsigned> indices;
unsigned index = 0;
for (auto const& s: m_sources)
indices[s.first] = index++;
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
indices[CompilerContext::yulUtilityFileName()] = index++;
if (m_evmAssemblyJson.empty())
{
for (auto const& s: m_sources)
indices[s.first] = index++;
solAssert(!indices.count(CompilerContext::yulUtilityFileName()), "");
if (_includeInternalSources)
indices[CompilerContext::yulUtilityFileName()] = index++;
}
else
for (auto const& s: m_sourceOrder)
indices[s->charStream->source()] = index++;
return indices;
}

Expand Down
5 changes: 4 additions & 1 deletion libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ class CompilerStack: public langutil::CharStreamProvider
/// Will throw errors if the import fails
void importASTs(std::map<std::string, Json::Value> const& _sources);

void importEvmAssemblyJson(std::map<std::string, Json::Value> const& _sources);

/// Performs the analysis steps (imports, scopesetting, syntaxCheck, referenceResolving,
/// typechecking, staticAnalysis) on previously parsed sources.
/// @returns false on error.
Expand All @@ -240,7 +242,7 @@ class CompilerStack: public langutil::CharStreamProvider

/// @returns a mapping assigning each source name its index inside the vector returned
/// by sourceNames().
std::map<std::string, unsigned> sourceIndices() const;
std::map<std::string, unsigned> sourceIndices(bool _includeInternalSources = true) const;

/// @returns the previously used character stream, useful for counting lines during error reporting.
langutil::CharStream const& charStream(std::string const& _sourceName) const override;
Expand Down Expand Up @@ -499,6 +501,7 @@ class CompilerStack: public langutil::CharStreamProvider
std::map<std::string const, Source> m_sources;
// if imported, store AST-JSONS for each filename
std::map<std::string, Json::Value> m_sourceJsons;
std::map<std::string, Json::Value> m_evmAssemblyJson;
std::vector<std::string> m_unhandledSMTLib2Queries;
std::map<util::h256, std::string> m_smtlib2Responses;
std::shared_ptr<GlobalContext> m_globalContext;
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/interface/OptimiserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ struct OptimiserSettings
expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment;
}

/// Optimizer enabled.
bool enabled = false;
/// Move literals to the right of commutative binary operators during code generation.
/// This helps exploiting associativity.
bool runOrderLiterals = false;
Expand Down
Loading