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

Simple inlining for old optimizer. #10761

Merged
merged 1 commit into from
Feb 9, 2021
Merged

Simple inlining for old optimizer. #10761

merged 1 commit into from
Feb 9, 2021

Conversation

ekpyron
Copy link
Member

@ekpyron ekpyron commented Jan 14, 2021

References #10698

@ekpyron ekpyron force-pushed the oldOptimizerInlining branch 2 times, most recently from c9138ed to 6df56a8 Compare January 14, 2021 12:11
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 6df56a8 to 19c5566 Compare January 14, 2021 12:37
@ethereum ethereum deleted a comment from stackenbotten Jan 14, 2021
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from ab7f768 to 5ef8ce5 Compare January 14, 2021 13:20
@ethereum ethereum deleted a comment from stackenbotten Jan 14, 2021
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 03d5ea3 to 906109e Compare January 14, 2021 13:55
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch 4 times, most recently from bea6def to 55bb561 Compare January 14, 2021 15:32
libevmasm/Inliner.h Outdated Show resolved Hide resolved
@ekpyron ekpyron marked this pull request as ready for review January 14, 2021 16:26
@ekpyron ekpyron requested a review from chriseth January 14, 2021 16:26
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from b3c4ead to c3d83df Compare January 14, 2021 17:56
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
@ethereum ethereum deleted a comment from stackenbotten Jan 14, 2021
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch 2 times, most recently from 621235d to 374ca72 Compare January 14, 2021 19:08
@ethereum ethereum deleted a comment from stackenbotten Jan 14, 2021
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.h Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.h Outdated Show resolved Hide resolved
Comment on lines 138 to 206
++block->pushTagCount;

Copy link
Member

Choose a reason for hiding this comment

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

I don't really follow how pushTagCount is used. Can you please explain?

Copy link
Member Author

Choose a reason for hiding this comment

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

pushTagCount counts how many times the tag at which the respective inline candidate lives is pushed, i.e. roughly how many jumps there are into that block. When inlining we can increase the number of pushes, though, that's accounted for here.
Unlikely to happen currently, since we restrict to full function inlining, but more likely to happen in the more general case and still not impossible in full function inlining.

@ekpyron ekpyron force-pushed the oldOptimizerInlining branch 9 times, most recently from 2f0eeb5 to b4b7df8 Compare February 3, 2021 10:42
@ekpyron
Copy link
Member Author

ekpyron commented Feb 3, 2021

FYI this is the difference in gas expectations https://gist.github.com/ekpyron/0b3c2b0ac499dfad578ad01ecdfd34ee stemming from rebasing this on top of #10863 (the diff is between updating once with the inliner disabled and once with it enabled).

Changelog.md Outdated
@@ -4,6 +4,7 @@ Language Features:


Compiler Features:
* Optimizer: Simple inlining of functions with straight control flow.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we explain this in more depth in the documentation?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe, yeah...

docs/using-the-compiler.rst Show resolved Hide resolved
libevmasm/Assembly.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.h Outdated Show resolved Hide resolved
{
// The number of PushTag's approximates the number of calls to a block.
if (item.type() == PushTag)
numPushTags[item.data()]++;
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, do you handle dynamic references? Can you add some tests?

contract C {
  function() internal pure returns (uint) x;
  function f() internal pure returns (uint) { return 6; }

  function g() public { x = f; }
  function h() public returns (uint) { return x(); }
  function i() public returns (uint) { return f(); } // this should be inlined
}

And another test where the assignment to x is done in the constructor and another where h and i are called in the constructor.

libevmasm/Inliner.cpp Outdated Show resolved Hide resolved

// If the estimated runtime cost over the lifetime of the contract plus the deposit cost in the uninlined case
// exceed the inlined deposit costs, it is beneficial to inline.
if (bigint(m_runs) * uninlinedExecutionCost + uninlinedDepositCost > inlinedDepositCost)
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this assume we can always delete the function if it is inlined?

Copy link
Member Author

@ekpyron ekpyron Feb 4, 2021

Choose a reason for hiding this comment

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

Yes... which is of course wrong in general... I could also calculate under the assumption that we can't remove it. It would probably also be possible to be sure about it (not only count pushtags, but also count which of those are pushtag jump), but only for "right now" and not for "maybe after further optimization cycles"...

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you take references from the surrounding assembly into account? That will probably not be removable since it is a dynamic jump.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah... let me see if I can... with "surrounding assembly" you mean the assembly the current assembly is a subassembly of, right :-)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, ok, there is _tagsReferencedFromOutside available, I'll just have to pass that through, nice! I'll do that then!

newItems += ranges::views::drop_last(inlinableBlock->items, 1);
newItems.emplace_back(*exitJump);

// We are removing one push tag to the block we inline.
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this change the heuristic while are inlining?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes... but only for a block that we already inlined once - and removing one jump to it will only make it more likely to inline it again afterwards, so probably fine...
Then again I could probably remove this and the loop below for now without any effect as well...

libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
stop
/* "optimizer_BlockDeDuplicator/input.sol":108:133 function fun_() public {} */
tag_4:
jump(tag_3)
Copy link
Contributor

Choose a reason for hiding this comment

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

This could actually be replaced by a single stop...

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes - in general we can consider also inlining pushtag(tag) jump when we see tag: ... stop or tag: ... invalid or tag: ... revert... it'd need a different heuristics, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Of course the size of the block to be inlined being smaller than the pushtag(tag) jump itself will always be good...

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be useful, even if again not that nice for the debugger. But maybe we can do that in another PR.

@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 7d7aa7f to dc0b255 Compare February 5, 2021 14:49
@ekpyron
Copy link
Member Author

ekpyron commented Feb 5, 2021

I just updated this to split the tag data into subId and tag everywhere and to only ever consider tags referring to the local subassembly (I think we could never have inlined across subassemblys anyways and this way push counting and tag tracking should be more robust).

I also pass in the externally referenced tags, s.t. we can consider them in the heuristics.

Also I addressed most of the other comments I think, so mainly missing are tests and docs I hope.

@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 105930a to 670a1d8 Compare February 8, 2021 10:27
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from c11f064 to 8222af9 Compare February 8, 2021 15:17
@ekpyron ekpyron requested a review from chriseth February 9, 2021 11:25
@chriseth
Copy link
Contributor

chriseth commented Feb 9, 2021

Can you squash "fix windows build"?

@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 8222af9 to e4f7cc4 Compare February 9, 2021 15:02
chriseth
chriseth previously approved these changes Feb 9, 2021
libevmasm/Inliner.cpp Outdated Show resolved Hide resolved
libevmasm/Inliner.cpp Show resolved Hide resolved
@ekpyron ekpyron force-pushed the oldOptimizerInlining branch from 53663f2 to cb74a45 Compare February 9, 2021 18:09
@chriseth chriseth merged commit 9a621e9 into develop Feb 9, 2021
@chriseth chriseth deleted the oldOptimizerInlining branch February 9, 2021 18:35
@ekpyron
Copy link
Member Author

ekpyron commented Feb 10, 2021

@haltman-at @yann300 So as you can see we merged this yesterday, so tonight's nightly release should include this in case you want to have another look before it's released.

The final version is restricted to only inline the "jump-in-jump-out" case, i.e. only entire functions will be inlined for the time being (we will extend this sooner or later, though, including to cases like #10761 (comment)).

We decided to go ahead without first defining new debugging output, since we noticed that the other steps in the optimizer will already ignore the fact that jumps have jump labels and may remove jumps into- or out-of- functions already, so we're not introducing a new problem with this PR, but we're "just" making an existing problem worse - so given that and the fact that the gas savings of this can be significant and it is required for zero-cost workarounds for #10698, we thought it worthwhile to merge now.
#10929 also added some (minimal) documentation about this.

By now it seems clear that we want to define debugging data that specifies function entries and exits on a more fine-grained level, but it's not yet entirely clear what information we want exactly and in what form (Do we only want something equivalent to jump labels on opcode level? Do we also want information about the function entry being "complete", i.e. the arguments and return values being initialized on stack? In general what do we do if opcodes shared among several functions is collapsed? How best to try and make this robust against potential future optimizations? Etc.) - we have a forum topic around the topic of debugging information in general (see https://forum.soliditylang.org/t/debugging-information/77) and can hopefully come up with something soon.

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

Successfully merging this pull request may close these issues.

8 participants