<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi Jake,<div class=""><br class=""></div><div class=""><div><blockquote type="cite" class=""><div class="">On Sep 28, 2018, at 1:51 PM, Jake Ehrlich <<a href="mailto:jakehehrlich@google.com" class="">jakehehrlich@google.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Fantastic! I have been looking at creating a tool that a) only spits out actionable size reductions (preferably with a specific action should be specified)</div></div></blockquote><div><br class=""></div><div>I’m glad you brought this up. I think issuing FixIts for code size problems could be really useful.</div><div><br class=""></div><div>Would it make sense to write a code size optimization guide as a starting point (say, in llvm/docs)? It could cover the tradeoffs of using relevant compiler features (-Oz, -fvisibility=hidden, etc) and attributes. FixIts could then reference this doc.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""> and b) only analyzes the size of allocated sections. The other deficiency I've seen with bloaty is speed and scaling. It's very hard to get bloaty to analyze across a large system of interdependent shared libraries.</div></div></blockquote><div><br class=""></div><div>I’m not sure what you mean by ‘analyze across’: do you mean that it’s hard to analyze/diff a collection of executables/libraries as a single unit?</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""> You can add me as a reviewer to any changes as I would very much like to see such a tool exist.<br class=""></div></div></blockquote><div><br class=""></div><div><div>Thanks! Barring any objections I’ll split the logic out of llvm-dwarfdump and start a review. This should facilitate adding options, PDB support, etc.</div><div class=""><br class=""></div></div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class="">> <span style="color:rgb(33,33,33)" class="">Unlike bloaty, this </span><span class="inbox-inbox-lG" style="background-color:rgba(251,246,167,0.5);outline:transparent dashed 1px;color:rgb(33,33,33)">tool</span><span style="color:rgb(33,33,33)" class=""> focuses exclusively on the text segment.<br class=""><br class="">I'd like to see support for everything within PT_LOAD segments, not just the executable parts. Everything else you've said is basically what I wanted.</span></div></div></blockquote><div><br class=""></div><div>I’d like to see this too.</div><div><br class=""></div><div>vedant</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Wed, Sep 26, 2018 at 12:03 PM Vedant Kumar via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org" class="">llvm-dev@lists.llvm.org</a>> wrote:<br class=""></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hello,<br class="">
<br class="">
I worked on a code size analysis tool for a 'week of code' project and think<br class="">
that it might be useful enough to upstream.<br class="">
<br class="">
The tool is inspired by bloaty (<a href="https://github.com/google/bloaty" rel="noreferrer" target="_blank" class="">https://github.com/google/bloaty</a>), but tries to<br class="">
do more to attribute code size in actionable ways.<br class="">
<br class="">
For example, it can calculate how many bytes inlined instances of a function<br class="">
added to a binary. In its diff mode, it can show how much more aggressively a<br class="">
function was inlined compared to a baseline. This can be useful when you're,<br class="">
say, trying to figure out why firmware compiled by a new compiler is just a few<br class="">
bytes over the size limit imposed by your embedded device :). In this case,<br class="">
extra information about inlining can help inform a decision to either tweak the<br class="">
inliner's cost model or to judiciously add a few `noinline` attributes. (Note<br class="">
that if you're willing to recompile & write a few SQL queries, optimization<br class="">
remarks can give you similar information, albeit at the IR level.)<br class="">
<br class="">
As another example, this code size tool can attribute code size to semantically<br class="">
interesting groups of code, like C++/Swift classes, or files. In the diff mode,<br class="">
you can see how the code size of a class/file grew compared to a baseline. The<br class="">
tool understands inheritance, so you can also see interesting high-level trends.<br class="">
E.g `clang::Sema` grew more than `llvm::Pass` between clang-6 and clang-7.<br class="">
<br class="">
Unlike bloaty, this tool focuses exclusively on the text segment. Also unlike<br class="">
bloaty, it uses LLVM's DWARF parser instead of rolling its own. The tool is<br class="">
currently implemented as a sub-tool of llvm-dwarfdump.<br class="">
<br class="">
To get size information about a program, you do:<br class="">
<br class="">
llvm-dwarfdump size-info -baseline <object> -stats-dir <dir><br class="">
<br class="">
This emits four *.stats files into <dir>, each containing a distinct 'view' into<br class="">
the code groups in <object>. There's a file view, a function view, a class view,<br class="">
and an inlining view. Each view is sorted by code size, so you can see the<br class="">
largest functions/classes/etc immediately.<br class="">
<br class="">
The *.stats files are just human-readable text files. As it happens, they use<br class="">
the flamegraph format (<a href="http://brendangregg.com/flamegraphs.html" rel="noreferrer" target="_blank" class="">http://brendangregg.com/flamegraphs.html</a>). This makes it<br class="">
easy to visualize any view as a flamegraph. (If you haven't seen one before,<br class="">
it's a hierarchical visualization where the width of each entry corresponds to<br class="">
its frequency (or in this case size).)<br class="">
<br class="">
To look at code growth between two programs, you'd do:<br class="">
<br class="">
llvm-dwarfdump size-info -baseline <object> -target <object> -stats-dir <dir><br class="">
<br class="">
Similarly, this emits four 'view' files into <dir>, but with a *.diffstats<br class="">
suffix. The format is the same.<br class="">
<br class="">
Pending Work<br class="">
------------<br class="">
<br class="">
I think the main piece of work the tool needs is better testing. Currently<br class="">
there's just a single end-to-end test in clang. It might be better to check in<br class="">
a few binaries so we can check that the tool reports sizes correctly.<br class="">
<br class="">
Also, it may turn out that folks are interested in different ways of visualizing<br class="">
size data. While the textual format of flamegraphs is really convenient for<br class="">
humans to read, the graphs themselves do make more sense when the underlying<br class="">
data have a frequentist interpretation. If there's enough interest I can explore<br class="">
using an alternative format for visualization, e.g:<br class="">
<br class="">
<a href="http://neugierig.org/software/chromium/bloat/" rel="noreferrer" target="_blank" class="">http://neugierig.org/software/chromium/bloat/</a><br class="">
<a href="https://github.com/evmar/webtreemap" rel="noreferrer" target="_blank" class="">https://github.com/evmar/webtreemap</a><br class="">
<br class="">
(Thanks JF for pointing these out!)<br class="">
<br class="">
Here's a link to the source code:<br class="">
<br class="">
<a href="https://github.com/vedantk/llvm-project/tree/sizeinfo" rel="noreferrer" target="_blank" class="">https://github.com/vedantk/llvm-project/tree/sizeinfo</a> <br class="">
<br class="">
Selected Examples<br class="">
-----------------<br class="">
<br class="">
Here are a few interesting snippets from a comparison of clang-6 vs. clang-7.<br class="">
<br class="">
First, let's take a look at the function view diffstat. Here are the 10<br class="">
functions which grew in size the most. On the left hand side, you'll see the<br class="">
demangled function name. The *change* in code size in bytes is reported on the<br class="">
right hand side (only positive changes are reported).<br class="">
<br class="">
clang::Sema::CheckHexagonBuiltinCpu([snip]) [function] 170316<br class="">
ProcessDeclAttribute([snip]) [function] 125893<br class="">
llvm::AArch64InstPrinter::printAliasInstr([snip]) [function] 105133<br class="">
llvm::AArch64AppleInstPrinter::printAliasInstr([snip]) [function] 105133<br class="">
ParseCodeGenArgs([snip]) [function] 64692<br class="">
unswitchNontrivialInvariants([snip]) [function] 40180<br class="">
getAttrKind([snip]) [function] 35811<br class="">
clang::DumpCompilerOptionsAction::ExecuteAction() [function] 32417<br class="">
llvm::UpgradeIntrinsicCall([snip]) [function] 30239<br class="">
bool llvm::InstructionSelector::executeMatchTable<(anonymous namespace)::ARMInstructionSelector const, [snip]) const [function] 29352<br class="">
<br class="">
<br class="">
Next, let's look at the file view diffstat. This can be useful because it goes<br class="">
beyond simply identifying the files which grew the most. It actually describes<br class="">
which *functions* grew the most in those files, creating more opportunites to<br class="">
do something about the code growth.<br class="">
<br class="">
lib/Target/X86/X86ISelLowering.cpp [file];combineX86ShuffleChain([snip]) [function] 24864<br class="">
lib/Target/X86/X86ISelLowering.cpp [file];combineMul([snip]) [function] 14907<br class="">
lib/Target/X86/X86ISelLowering.cpp [file];combineStore([snip]) [function] 12220<br class="">
...<br class="">
tools/clang/lib/Sema/SemaExpr.cpp [file];clang::Sema::CheckCompareOperands([snip]) [function] 16024<br class="">
tools/clang/lib/Sema/SemaExpr.cpp [file];diagnoseTautologicalComparison([snip]) [function] 1740<br class="">
tools/clang/lib/Sema/SemaExpr.cpp [file];clang::Sema::ActOnNumericConstant([snip]) [function] 1436<br class="">
tools/clang/lib/Sema/SemaExpr.cpp [file];checkThreeWayNarrowingConversion([snip]) [function] 1356<br class="">
tools/clang/lib/Sema/SemaExpr.cpp [file];CheckIdentityFieldAssignment([snip]) [function] 1280<br class="">
<br class="">
<br class="">
The class view diffstat is a bit different because it has more levels of<br class="">
nesting than the other views, due to inheritance. This might help give a sense<br class="">
for the high-level changes in a program, but may also be less actionable.<br class="">
<br class="">
clang::Sema [class];clang::Sema::CheckHexagonBuiltinCpu([snip]) [function] 170316<br class="">
clang::Sema [class];clang::Sema::CheckHexagonBuiltinArgument([snip]) [function] 24156<br class="">
clang::Sema [class];clang::Sema::ActOnTag([snip]) [function] 22373<br class="">
...<br class="">
llvm::AArch64InstPrinter [class];llvm::AArch64AppleInstPrinter [class];llvm::AArch64AppleInstPrinter::printAliasInstr([snip]) [function] 105133<br class="">
llvm::AArch64InstPrinter [class];llvm::AArch64AppleInstPrinter [class];llvm::AArch64AppleInstPrinter::printInstruction([snip]) [function] 5824<br class="">
...<br class="">
llvm::Pass [class];llvm::FunctionPass [class];llvm::MachineFunctionPass [class];(anon)::X86SpeculativeLoadHardeningPass [class];(anonymous namespace)::X86SpeculativeLoadHardeningPass::checkAllLoads(llvm::MachineFunction&) [function] 19287<br class="">
...<br class="">
llvm::Pass [class];llvm::FunctionPass [class];llvm::MachineFunctionPass [class];(anon)::MachineLICMBase [class];(anonymous namespace)::MachineLICMBase::runOnMachineFunction(llvm::MachineFunction&) [function] 20343<br class="">
<br class="">
Here's a link to a flamegraph of the class view diffstat (warning: it's big):<br class="">
<br class="">
<a href="http://net.vedantk.com/static/llvm/swift-clang-4.2-vs-5.0.class-view.diffstats.svg" rel="noreferrer" target="_blank" class="">http://net.vedantk.com/static/llvm/swift-clang-4.2-vs-5.0.class-view.diffstats.svg</a><br class="">
<br class="">
Finally, here are a few interesting entries from the inlining view diffstat. As<br class="">
with all of the other views, the right hand side still shows code growth in<br class="">
bytes. For a given inlining target, this size is computed by diffing the sum of<br class="">
PC range lengths from all DW_TAG_inlined_subroutines referring to that target.<br class="">
This allows the size tool to attribute code size to an inlining target even<br class="">
when the inlined code is not contiguous in the caller.<br class="">
<br class="">
llvm::raw_ostream::operator<<(char const*) [inlining-target] 66720<br class="">
llvm::MCRegisterClass::contains(unsigned int) const [inlining-target] 64161<br class="">
llvm::StringRef::StringRef(char const*) [inlining-target] 39262<br class="">
llvm::MCInst::getOperand(unsigned int) const [inlining-target] 33268<br class="">
clang::CodeCompletionResult::~CodeCompletionResult() [inlining-target] 25763<br class="">
llvm::operator+(llvm::Twine const&, llvm::Twine const&) [inlining-target] 25525<br class="">
clang::ASTImporter::Import(clang::SourceLocation) [inlining-target] 21096<br class="">
clang::Sema::Diag(clang::SourceLocation, unsigned int) [inlining-target] 20898<br class="">
<br class="">
Feedback & questions welcome!<br class="">
<br class="">
thanks,<br class="">
vedant<br class="">
_______________________________________________<br class="">
LLVM Developers mailing list<br class="">
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank" class="">llvm-dev@lists.llvm.org</a><br class="">
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank" class="">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br class="">
</blockquote></div>
</div></blockquote></div><br class=""></div></body></html>