Skip to content
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

[test] Add support to run yul optimizer assembly test. #15009

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

aarlt
Copy link
Member

@aarlt aarlt commented Apr 11, 2024

This is needed in the context of ethdebug. I will need to write some tests on how optimiser steps are dealing with the debug attributes defined by #14857. The ideas is to implement different PRs per optimiser step on top of #14969, where this PR will enable the visualisation how these debug attributes got potentially merged - depending on the optimiser step.

For context, see e.g. #15087.

@cameel cameel mentioned this pull request Apr 12, 2024
Copy link
Member

@cameel cameel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is pretty much an exact copy of YulOptimizerTest.h at this point, so I assume it's unfinished. What is actually the idea behind this test? Just displaying the assembly next to the optimized Yul or something more?


// Re-parse new code for compilability
// TODO: support for wordSizeTransform which needs different input and output dialects
if (m_optimizerStep != "wordSizeTransform" && !std::get<0>(parse(_stream, _linePrefix, _formatted, printed)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wordSizeTransform seems to be some Ewasm left-over. We have no such step now.

I think we can safely remove this from YulOptimizerTest and a few other places that still reference it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was marked as resolved, but this code is still here.

test/InteractiveTests.h Outdated Show resolved Hide resolved
@aarlt aarlt force-pushed the add_yul_optimizer_assembly_test branch from e953eca to 917cec1 Compare April 18, 2024 21:01
@aarlt aarlt force-pushed the add_yul_optimizer_assembly_test branch 4 times, most recently from 8a4d556 to de39abd Compare April 22, 2024 15:19
@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label May 7, 2024
@ethereum ethereum deleted a comment from github-actions bot May 7, 2024
@aarlt aarlt removed the stale The issue/PR was marked as stale because it has been open for too long. label May 7, 2024
@ekpyron
Copy link
Member

ekpyron commented May 13, 2024

I see this is pretty much an exact copy of YulOptimizerTest.h at this point, so I assume it's unfinished. What is actually the idea behind this test? Just displaying the assembly next to the optimized Yul or something more?

Just to answer this: the target here is to test the transport of the debugging context through any individual optimizer step and then to assembly (which is from where we'll output them in the end)

In any case, doesn't hurt to have, and not too much of a headache to just merge.


// Re-parse new code for compilability
// TODO: support for wordSizeTransform which needs different input and output dialects
if (m_optimizerStep != "wordSizeTransform" && !std::get<0>(parse(_stream, _linePrefix, _formatted, printed)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was marked as resolved, but this code is still here.

Comment on lines +66 to +67
auto dialectName = m_reader.stringSetting("dialect", "evm");
m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion());
Copy link
Member

@cameel cameel May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With Ewasm gone, adding an option for dialect selection in a newly written test case seems pointless to me. Technically we still support the evmTyped and yul dialects, but have you even checked if they work here? That would be a waste of time anyway and IMO would be much better to just remove this option and assume the evm dialect.

You could then also get rid of dialect checks in the rest of the test case.

EVMObjectCompiler::compile(
*m_object,
adapter,
EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}),
Copy link
Member

@cameel cameel May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we respecting the dialect and EVM version selected by the user here even though we do in the other places in this test case?

Same question for analyzeStrictAssertCorrect() above.

Also, it would be a good idea to have something EVM version-specific in the sample test case so that we can see if this works correctly.

Comment on lines +2 to +11
{
let _1 := mload(0)
let f_a := mload(1)
let f_r
{
f_a := mload(f_a)
f_r := add(f_a, calldatasize())
}
let z := mload(2)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if the new test is meant for inspecting the debug data, then it would be useful to actually have some debug annotations in the sample test to see if it's even preserved in the output.

Comment on lines +114 to +115
false,
std::nullopt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
false,
std::nullopt
false, // _optimize
std::nullopt // _eofVersion

I think it this should be using the value supplied to --eof-version (i.e. solidity::test::CommonOptions::get().eofVersion()). Which will still be nullopt, by default, but I see no reason to ignore this option if supplied.

As for optimization, I guess we don't want because that's only the legacy optimizer? If so, at least a comment saying that would be nice. Otherwise these choices seem arbitrary.

Comment on lines +118 to +120
std::ostringstream output;
output << assembly;
m_obtainedResult += "\nAssembly:\n" + output.str();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is so common that we already have a helper for it in CommonIO.h:

Suggested change
std::ostringstream output;
output << assembly;
m_obtainedResult += "\nAssembly:\n" + output.str();
m_obtainedResult += "\nAssembly:\n" + toString(assembly);


TestCase::TestResult YulOptimizerAssemblyTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted)
{
std::tie(m_object, m_analysisInfo) = parse(_stream, _linePrefix, _formatted, m_source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m_object and m_analysisInfo are never used outside of this function. What's the point of even having them as class members rather than locals?

Comment on lines +125 to +146
std::pair<std::shared_ptr<Object>, std::shared_ptr<AsmAnalysisInfo>> YulOptimizerAssemblyTest::parse(
std::ostream& _stream,
std::string const& _linePrefix,
bool const _formatted,
std::string const& _source
)
{
ErrorList errors;
soltestAssert(m_dialect, "");
std::shared_ptr<Object> object;
std::shared_ptr<AsmAnalysisInfo> analysisInfo;
std::tie(object, analysisInfo) = yul::test::parse(_source, *m_dialect, errors);
if (!object || !analysisInfo || Error::containsErrors(errors))
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl;
CharStream charStream(_source, "");
SourceReferenceFormatter{_stream, SingletonCharStreamProvider(charStream), true, false}
.printErrorInformation(errors);
return {};
}
return {std::move(object), std::move(analysisInfo)};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it was copied verbatim from YulOptimizerTest. Why not just reuse it? Other than m_dialect (which could be parameter), this could easily be converted into a static function or a standalone helper and just called by this class.

Comment on lines +104 to +106
m_object->analysisInfo = std::make_shared<AsmAnalysisInfo>(
AsmAnalyzer::analyzeStrictAssertCorrect(EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()), *m_object)
);
Copy link
Member

@cameel cameel May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really correct to simply overwrite the previous analysis info? Do we do that in real compilation too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants