<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Jun 19, 2016 at 12:01 AM, Sanjoy Das <span dir="ltr"><<a href="mailto:sanjoy@playingwithpointers.com" target="_blank">sanjoy@playingwithpointers.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Hi David,<span><br>
<br>
Xinliang David Li wrote:<br>
>> I believe it is primarily used for ordering the visitation of CallSCC's (i.e. SCC's in the "call graph").<br>
> This is what it can do -- but what benefit does it provide?<br>
<br></span>
One benefit is that once you get to a function F that constructs an<br>
instance of a class with virtual functions and then calls a virtual<br>
function on the instance, then the virtual function being called and<br>
the constructor will have been maximally simplified (F refs the<br>
constructor, and the constructor refs all the virtual functions), and<br>
you're more likely to inline the constructor and devirtualize the<br>
call. I don't have any real data to back up that this will materially<br>
help, though.</blockquote><div><br></div><div><br></div><div>Sanjoy, this is a good example. The code pattern is basically like this:</div><div><br></div><div>Worker(Base *B) {</div><div> B->vCall();</div><div>}</div><div><br></div><div>Factory::create(Kind K) {</div><div> if (K == ..) return new D1();</div><div> else ...</div><div>}</div><div><br></div><div>Caller() { </div><div> ..</div><div> Base *B = Factory::create(K, ...);</div><div> Worker(B);</div><div>}</div><div><br></div><div><br></div><div>The added ordering constraints from Factory::create() node to all virtual methods in Base's hierarchy ensures that after </div><div>1) Factory::create gets inlined to Caller, and</div><div>2) Worker(..) method gets inlined to Caller, and</div><div>3) newly exposed vcall gets devirtualized</div><div><br></div><div>the inliner sees a callee to say D1::vCall which is already simplified.</div><div><br></div><div><br></div><div>However, in real applications, what I see is the following pattern (for instances LLVM's Pass )</div><div><br></div><div>Caller() {</div><div> Base *B = Factory::create(...);</div><div> Stash (B); // store the object in some container to be retrieved later</div><div> ...</div><div>}</div><div><br></div><div>SomeTask() {</div><div><br></div><div> Base *B = findObject(...);</div><div> B->vCall(); // do the work </div><div>}</div><div><br></div><div>Driver() {</div><div> Caller(); // create objects ...</div><div> SomeTask();</div><div>}</div><div> </div><div><br></div><div>Set aside the fact that it is usually much harder to do de-viritualization in this case, assuming the virtual call in SomeTask can be devritualized. What we need is that the virtual functions are processed before SomeTask node, but this is not guaranteed unless we also model the call edge ordering imposed by control flow.</div><div><br></div><div>However, this is enforcing virtual methods to be processed before their object's creators. Are there other simpler ways to achieve the effect (if we have data to justify it)?</div><div><br></div><div>David</div><div><br></div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span><font color="#888888"><br>
<br>
-- Sanjoy<br>
</font></span></blockquote></div><br></div></div>