<div dir="ltr">It had been failing also there;<br><a href="http://bb.pgr.jp/builders/ninja-clang-i686-msc18-R/builds/2679">http://bb.pgr.jp/builders/ninja-clang-i686-msc18-R/builds/2679</a><br><div><a href="http://bb.pgr.jp/builders/ninja-clang-x64-mingw64-RA/builds/7660">http://bb.pgr.jp/builders/ninja-clang-x64-mingw64-RA/builds/7660</a><br></div><div><br></div><div>Fixed in <span style="color:rgb(51,51,51);font-family:Verdana,sans-serif;font-size:10px;line-height:normal;text-align:center;background-color:rgb(238,238,238)">r245168.</span></div></div><br><div class="gmail_quote"><div dir="ltr">On Sun, Aug 16, 2015 at 4:48 AM Pete Cooper via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">Hey JF, Jason<div><br></div><div>There’s an ASan/UBSan failure with a mergefunc test on the bots. The blame list range is r245138-r245153.</div><div><br></div><div>Mind taking a look to see if this commit might be the cause. Its the only MergeFunc related one I could see in the range.</div><div><br></div><div><a href="http://lab.llvm.org:8080/green/job/clang-stage2-cmake-RgSan_check/94/consoleFull#-121115394049ba4694-19c4-4d7e-bec5-911270d8a58c" target="_blank">http://lab.llvm.org:8080/green/job/clang-stage2-cmake-RgSan_check/94/consoleFull#-121115394049ba4694-19c4-4d7e-bec5-911270d8a58c</a></div><div><br></div><div>Cheers,</div><div>Pete</div></div><div style="word-wrap:break-word"><div><div><blockquote type="cite"><div>On Aug 14, 2015, at 7:06 PM, JF Bastien via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>> wrote:</div><br><div><p dir="ltr">I'll defer the answer to Jason, who authored the patch. </p>
<div class="gmail_quote">On Aug 14, 2015 6:39 PM, "David Blaikie" <<a href="mailto:dblaikie@gmail.com" target="_blank">dblaikie@gmail.com</a>> wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Aug 14, 2015 at 6:18 PM, JF Bastien via llvm-commits <span dir="ltr"><<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: jfb<br>
Date: Fri Aug 14 20:18:18 2015<br>
New Revision: 245140<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=245140&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=245140&view=rev</a><br>
Log:<br>
Accelerate MergeFunctions with hashing<br>
<br>
This patch makes the Merge Functions pass faster by calculating and comparing<br>
a hash value which captures the essential structure of a function before<br>
performing a full function comparison.<br>
<br>
The hash is calculated by hashing the function signature, then walking the basic<br>
blocks of the function in the same order as the main comparison function. The<br>
opcode of each instruction is hashed in sequence, which means that different<br>
functions according to the existing total order cannot have the same hash, as<br>
the comparison requires the opcodes of the two functions to be the same order.<br>
<br>
The hash function is a static member of the FunctionComparator class because it<br>
is tightly coupled to the exact comparison function used. For example, functions<br>
which are equivalent modulo a single variant callsite might be merged by a more<br>
aggressive MergeFunctions, and the hash function would need to be insensitive to<br>
these differences in order to exploit this.<br>
<br>
The hashing function uses a utility class which accumulates the values into an<br>
internal state using a standard bit-mixing function. Note that this is a different interface<br>
than a regular hashing routine, because the values to be hashed are scattered<br>
amongst the properties of a llvm::Function, not linear in memory. This scheme is<br>
fast because only one word of state needs to be kept, and the mixing function is<br>
a few instructions.<br>
<br>
The main runOnModule function first computes the hash of each function, and only<br>
further processes functions which do not have a unique function hash. The hash<br>
is also used to order the sorted function set. If the hashes differ, their<br>
values are used to order the functions, otherwise the full comparison is done.<br>
<br>
Both of these are helpful in speeding up MergeFunctions. Together they result in<br>
speedups of 9% for mysqld (a mostly C application with little redundancy), 46%<br>
for libxul in Firefox, and 117% for Chromium. (These are all LTO builds.) In all<br>
three cases, the new speed of MergeFunctions is about half that of the module<br>
verifier, making it relatively inexpensive even for large LTO builds with<br>
hundreds of thousands of functions. The same functions are merged, so this<br>
change is free performance.<br>
<br>
Author: jrkoenig<br>
<br>
Reviewers: nlewycky, dschuff, jfb<br>
<br>
Subscribers: llvm-commits, aemerson<br>
<br>
Differential revision: <a href="http://reviews.llvm.org/D11923" rel="noreferrer" target="_blank">http://reviews.llvm.org/D11923</a><br>
<br>
Modified:<br>
llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp<br>
llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll<br>
<br>
Modified: llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp?rev=245140&r1=245139&r2=245140&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp?rev=245140&r1=245139&r2=245140&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/IPO/MergeFunctions.cpp Fri Aug 14 20:18:18 2015<br>
@@ -27,6 +27,14 @@<br>
// -- We define Function* container class with custom "operator<" (FunctionPtr).<br>
// -- "FunctionPtr" instances are stored in std::set collection, so every<br>
// std::set::insert operation will give you result in log(N) time.<br>
+//<br>
+// As an optimization, a hash of the function structure is calculated first, and<br>
+// two functions are only compared if they have the same hash. This hash is<br>
+// cheap to compute, and has the property that if function F == G according to<br>
+// the comparison function, then hash(F) == hash(G). This consistency property<br>
+// is critical to ensuring all possible merging opportunities are exploited.<br>
+// Collisions in the hash affect the speed of the pass but not the correctness<br>
+// or determinism of the resulting transformation.<br>
//<br>
// When a match is found the functions are folded. If both functions are<br>
// overridable, we move the functionality into a new internal function and<br>
@@ -87,6 +95,7 @@<br>
#include "llvm/ADT/STLExtras.h"<br>
#include "llvm/ADT/SmallSet.h"<br>
#include "llvm/ADT/Statistic.h"<br>
+#include "llvm/ADT/Hashing.h"<br>
#include "llvm/IR/CallSite.h"<br>
#include "llvm/IR/Constants.h"<br>
#include "llvm/IR/DataLayout.h"<br>
@@ -132,6 +141,10 @@ public:<br>
<br>
/// Test whether the two functions have equivalent behaviour.<br>
int compare();<br>
+ /// Hash a function. Equivalent functions will have the same hash, and unequal<br>
+ /// functions will have different hashes with high probability.<br>
+ typedef uint64_t FunctionHash;<br>
+ static FunctionHash functionHash(Function &);<br>
<br>
private:<br>
/// Test whether two basic blocks have equivalent behaviour.<br>
@@ -390,9 +403,11 @@ private:<br>
<br>
class FunctionNode {<br>
mutable AssertingVH<Function> F;<br>
+ FunctionComparator::FunctionHash Hash;<br>
<br>
public:<br>
- FunctionNode(Function *F) : F(F) {}<br>
+ // Note the hash is recalculated potentially multiple times, but it is cheap.<br>
+ FunctionNode(Function *F) : F(F), Hash(FunctionComparator::functionHash(*F)){}<br>
Function *getFunc() const { return F; }<br>
<br>
/// Replace the reference to the function F by the function G, assuming their<br>
@@ -406,6 +421,9 @@ public:<br>
<br>
void release() { F = 0; }<br>
bool operator<(const FunctionNode &RHS) const {<br>
+ // Order first by hashes, then full function comparison.<br>
+ if (Hash != RHS.Hash)<br>
+ return Hash < RHS.Hash;<br>
return (FunctionComparator(F, RHS.getFunc()).compare()) == -1;<br>
}<br>
};<br>
@@ -1074,6 +1092,66 @@ int FunctionComparator::compare() {<br>
return 0;<br>
}<br>
<br>
+// Accumulate the hash of a sequence of 64-bit integers. This is similar to a<br>
+// hash of a sequence of 64bit ints, but the entire input does not need to be<br>
+// available at once. This interface is necessary for functionHash because it<br>
+// needs to accumulate the hash as the structure of the function is traversed<br>
+// without saving these values to an intermediate buffer. This form of hashing<br>
+// is not often needed, as usually the object to hash is just read from a<br>
+// buffer.<br>
+class HashAccumulator64 {<br></blockquote><div><br>Any chance of using LLVM's Hashing.h for hash aggregation? (it's already got accumulator support, if I'm not mistaken?)<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ uint64_t Hash;<br>
+public:<br>
+ // Initialize to random constant, so the state isn't zero.<br>
+ HashAccumulator64() { Hash = 0x6acaa36bef8325c5ULL; }<br>
+ void add(uint64_t V) {<br>
+ Hash = llvm::hashing::detail::hash_16_bytes(Hash, V);<br>
+ }<br>
+ // No finishing is required, because the entire hash value is used.<br>
+ uint64_t getHash() { return Hash; }<br>
+};<br>
+<br>
+// A function hash is calculated by considering only the number of arguments and<br>
+// whether a function is varargs, the order of basic blocks (given by the<br>
+// successors of each basic block in depth first order), and the order of<br>
+// opcodes of each instruction within each of these basic blocks. This mirrors<br>
+// the strategy compare() uses to compare functions by walking the BBs in depth<br>
+// first order and comparing each instruction in sequence. Because this hash<br>
+// does not look at the operands, it is insensitive to things such as the<br>
+// target of calls and the constants used in the function, which makes it useful<br>
+// when possibly merging functions which are the same modulo constants and call<br>
+// targets.<br>
+FunctionComparator::FunctionHash FunctionComparator::functionHash(Function &F) {<br>
+ HashAccumulator64 H;<br>
+ H.add(F.isVarArg());<br>
+ H.add(F.arg_size());<br>
+<br>
+ SmallVector<const BasicBlock *, 8> BBs;<br>
+ SmallSet<const BasicBlock *, 16> VisitedBBs;<br>
+<br>
+ // Walk the blocks in the same order as FunctionComparator::compare(),<br>
+ // accumulating the hash of the function "structure." (BB and opcode sequence)<br>
+ BBs.push_back(&F.getEntryBlock());<br>
+ VisitedBBs.insert(BBs[0]);<br>
+ while (!BBs.empty()) {<br>
+ const BasicBlock *BB = BBs.pop_back_val();<br>
+ // This random value acts as a block header, as otherwise the partition of<br>
+ // opcodes into BBs wouldn't affect the hash, only the order of the opcodes<br>
+ H.add(45798);<br>
+ for (auto &Inst : *BB) {<br>
+ H.add(Inst.getOpcode());<br>
+ }<br>
+ const TerminatorInst *Term = BB->getTerminator();<br>
+ for (unsigned i = 0, e = Term->getNumSuccessors(); i != e; ++i) {<br>
+ if (!VisitedBBs.insert(Term->getSuccessor(i)).second)<br>
+ continue;<br>
+ BBs.push_back(Term->getSuccessor(i));<br>
+ }<br>
+ }<br>
+ return H.getHash();<br>
+}<br>
+<br>
+<br>
namespace {<br>
<br>
/// MergeFunctions finds functions which will generate identical machine code,<br>
@@ -1228,11 +1306,28 @@ bool MergeFunctions::doSanityCheck(std::<br>
bool MergeFunctions::runOnModule(Module &M) {<br>
bool Changed = false;<br>
<br>
- for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {<br>
- if (!I->isDeclaration() && !I->hasAvailableExternallyLinkage())<br>
- Deferred.push_back(WeakVH(I));<br>
+ // All functions in the module, ordered by hash. Functions with a unique<br>
+ // hash value are easily eliminated.<br>
+ std::vector<std::pair<FunctionComparator::FunctionHash, Function *>><br>
+ HashedFuncs;<br>
+ for (Function &Func : M) {<br>
+ if (!Func.isDeclaration() && !Func.hasAvailableExternallyLinkage()) {<br>
+ HashedFuncs.push_back({FunctionComparator::functionHash(Func), &Func});<br>
+ }<br>
+ }<br>
+<br>
+ std::sort(HashedFuncs.begin(), HashedFuncs.end());<br>
+<br>
+ auto S = HashedFuncs.begin();<br>
+ for (auto I = HashedFuncs.begin(), IE = HashedFuncs.end(); I != IE; ++I) {<br>
+ // If the hash value matches the previous value or the next one, we must<br>
+ // consider merging it. Otherwise it is dropped and never considered again.<br>
+ if ((I != S && std::prev(I)->first == I->first) ||<br>
+ (std::next(I) != IE && std::next(I)->first == I->first) ) {<br>
+ Deferred.push_back(WeakVH(I->second));<br>
+ }<br>
}<br>
-<br>
+<br>
do {<br>
std::vector<WeakVH> Worklist;<br>
Deferred.swap(Worklist);<br>
<br>
Modified: llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll?rev=245140&r1=245139&r2=245140&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll?rev=245140&r1=245139&r2=245140&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll (original)<br>
+++ llvm/trunk/test/Transforms/MergeFunc/call-and-invoke-with-ranges.ll Fri Aug 14 20:18:18 2015<br>
@@ -63,14 +63,6 @@ lpad:<br>
resume { i8*, i32 } zeroinitializer<br>
}<br>
<br>
-define i8 @call_with_same_range() {<br>
-; CHECK-LABEL: @call_with_same_range<br>
-; CHECK: tail call i8 @call_with_range<br>
- bitcast i8 0 to i8<br>
- %out = call i8 @dummy(), !range !0<br>
- ret i8 %out<br>
-}<br>
-<br>
define i8 @invoke_with_same_range() personality i8* undef {<br>
; CHECK-LABEL: @invoke_with_same_range()<br>
; CHECK: tail call i8 @invoke_with_range()<br>
@@ -84,6 +76,16 @@ lpad:<br>
resume { i8*, i32 } zeroinitializer<br>
}<br>
<br>
+define i8 @call_with_same_range() {<br>
+; CHECK-LABEL: @call_with_same_range<br>
+; CHECK: tail call i8 @call_with_range<br>
+ bitcast i8 0 to i8<br>
+ %out = call i8 @dummy(), !range !0<br>
+ ret i8 %out<br>
+}<br>
+<br>
+<br>
+<br>
declare i8 @dummy();<br>
declare i32 @__gxx_personality_v0(...)<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div></div>
</blockquote></div>
_______________________________________________<br>llvm-commits mailing list<br><a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br><a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br></div></blockquote></div><br></div></div>_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div>