<div dir="ltr">Than,<div><br></div><div>Thank you for the detailed explanation of the implementation difference between GCC and LLVM. I have a question about GCC's implementation inlined.<br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Apr 11, 2016 at 8:15 AM, Than McIntosh <span dir="ltr"><<a href="mailto:thanm@google.com" target="_blank">thanm@google.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">thanm added a comment.<br>
<br>
Hi Wei,<br>
<br>
Thanks for the review. I responded to your comments in line.<br>
<br>
You are quite right, even after this patch, clang/llvm will still worse than GCC.  The reason has to do with the way that the GCC code computes interferences between variables.<br>
<br>
The GCC implementation uses an interference graph (node == var, edge == coloring conflict between vars), and it adds edges in the interference graph only at DEFs: statements where there could conceivably be some sort of memory write. In contrast, the LLVM algorithm works by building live intervals and checking to see whether there is any overlap between the intervals (there is no attention paid to DEF vs non-DEF). This second strategy is simpler but is definitely less precise in some cases.<br>
<br>
Let me expand on this a bit. Going back to the if/then example:<br>
<br>
  int foo(int x) {<br>
    int ar1[128]; int ar2[128];<br>
    if (x & 0x99) {<br>
      bar(&ar1); return x-1;<br>
    } else {<br>
      bar(&ar2); return x+1;<br>
    }<br>
  }<br>
<br>
Here is what the IR looks like at the point where the analysis is done (it's pretety much the same between both GCC and LLVM at an abstract level):<br>
<br>
         BB#1:<br>
  I0:    call bar(&ar1)<br>
  I1:    %vreg1 = %vregx - 1<br>
  I2:    jmp BB3<br>
<br>
         BB#2:<br>
  I3:    call bar(&ar1)<br>
  I4:    %vreg2 = %vregx + 1<br>
         <fall through><br>
<br>
         BB#3:<br>
  I5:    %vrefr = phi(%vreg1, %vreg2)<br>
  I6:    LIFETIME_END <ar1><br>
  I7:    LIFETIME_END <ar2><br>
  I8:    <return %vregr><br>
<br>
As mentioned previously, the GCC algorithm uses an uses an interference graph -- for each variable X it keeps a bit vector of other variables that X interferes with. At each BB, it computes the live-in set for the BB by unioning together the live out sets of the pred BBs. It then walks the BB and looks for any instruction that could write memory (essentially anything other than phi or debug). At such an inst, it adds conflicts between every variable in the work set.<br>
<br>
At the point where GCC processes BB#3 above, both "ar1" and "ar2" are in the work set. However as it walks through the BB it never sees any instruction that could write memory, so it never adds conflicts between the vars.<br>
<br></blockquote><div><br></div><div>But isn't bar() could potentially change ar1 and ar2 indirectly since ar1 and ar2 are escaped local arrays from current func?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Given that the new implementation is a bit better but not ideal, I think we can revisit it at some point and decide whether we want to throw out intervals and move to interference-graph.<br>
<br></blockquote><div><br></div><div>Yes, I agree with you. </div><div><br></div><div>Thanks,</div><div>Wei.</div></div></div></div></div>