Hello,<br><br>Following a question on SO about how MSVC handled the devirtualization of calls, I tried the following in Clang to check its behavior:<br><br style="font-family: times new roman,serif;"><div style="margin-left: 40px;">
<span style="font-family: times new roman,serif;">struct A { virtual void foo(); };</span><br style="font-family: times new roman,serif;"><br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">struct C { A a; };</span><br style="font-family: times new roman,serif;">
<br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">C& GetRef();</span><br style="font-family: times new roman,serif;"><br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">void devirtualizeDirect() {<br>
  A a;<br>  a.foo();<br><br>  C c;<br>  c.a.foo();<br>}<br><br>void devirtualizeRef() {<br>  A a;<br>  A& ar = a;<br>  ar.foo();<br><br>  C c;<br>  C& cr = c;<br>  cr.a.foo();<br>}</span><br style="font-family: times new roman,serif;">
<br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">void nodevirtualize() {</span><br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">  C& cr = GetRef();</span><br style="font-family: times new roman,serif;">
<span style="font-family: times new roman,serif;">  cr.a.foo();</span><br style="font-family: times new roman,serif;"><span style="font-family: times new roman,serif;">}</span><br></div><br>Here is the IR emitted by the Try Out LLVM page (for the 3 functions of interest):<br>
<pre style="font-family: times new roman,serif; margin-left: 40px;"><span>
<span class="llvm_keyword">define</span> <span class="llvm_type">void</span> @devirtualizeDirect()() {
  %a = <span class="llvm_keyword">alloca</span> %struct.A, <span class="llvm_keyword">align</span> 8
  %c = <span class="llvm_keyword">alloca</span> %struct.C, <span class="llvm_keyword">align</span> 8
  %1 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.A* %a, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">store</span> <span class="llvm_type">i32</span> (...)** <span class="llvm_keyword">bitcast</span> (<span class="llvm_type">i8</span>** <span class="llvm_keyword">getelementptr</span> inbounds ([3 x <span class="llvm_type">i8</span>*]* @vtable for A, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i64</span> 2) <span class="llvm_keyword">to</span> <span class="llvm_type">i32</span> (...)**), <span class="llvm_type">i32</span> (...)*** %1, <span class="llvm_keyword">align</span> 8
  <span class="llvm_keyword">call</span> <span class="llvm_type">void</span> @A::foo()(%struct.A* %a)
  %2 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.C* %c, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">store</span> <span class="llvm_type">i32</span> (...)** <span class="llvm_keyword">bitcast</span> (<span class="llvm_type">i8</span>** <span class="llvm_keyword">getelementptr</span> inbounds ([3 x <span class="llvm_type">i8</span>*]* @vtable for A, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i64</span> 2) <span class="llvm_keyword">to</span> <span class="llvm_type">i32</span> (...)**), <span class="llvm_type">i32</span> (...)*** %2, <span class="llvm_keyword">align</span> 8
  %3 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.C* %c, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">call</span> <span class="llvm_type">void</span> @A::foo()(%struct.A* %3)
  <span class="llvm_keyword">ret</span> <span class="llvm_type">void</span>
}

<span class="llvm_keyword">define</span> <span class="llvm_type">void</span> @devirtualizeRef()() {
  %a = <span class="llvm_keyword">alloca</span> %struct.A, <span class="llvm_keyword">align</span> 8
  %c = <span class="llvm_keyword">alloca</span> %struct.C, <span class="llvm_keyword">align</span> 8
  %1 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.A* %a, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">store</span> <span class="llvm_type">i32</span> (...)** <span class="llvm_keyword">bitcast</span> (<span class="llvm_type">i8</span>** <span class="llvm_keyword">getelementptr</span> inbounds ([3 x <span class="llvm_type">i8</span>*]* @vtable for A, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i64</span> 2) <span class="llvm_keyword">to</span> <span class="llvm_type">i32</span> (...)**), <span class="llvm_type">i32</span> (...)*** %1, <span class="llvm_keyword">align</span> 8
  <span class="llvm_keyword">call</span> <span class="llvm_type">void</span> @A::foo()(%struct.A* %a)
  %2 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.C* %c, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">store</span> <span class="llvm_type">i32</span> (...)** <span class="llvm_keyword">bitcast</span> (<span class="llvm_type">i8</span>** <span class="llvm_keyword">getelementptr</span> inbounds ([3 x <span class="llvm_type">i8</span>*]* @vtable for A, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i64</span> 2) <span class="llvm_keyword">to</span> <span class="llvm_type">i32</span> (...)**), <span class="llvm_type">i32</span> (...)*** %2, <span class="llvm_keyword">align</span> 8
  %3 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.C* %c, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0
  <span class="llvm_keyword">call</span> <span class="llvm_type">void</span> @A::foo()(%struct.A* %3)
  <span class="llvm_keyword">ret</span> <span class="llvm_type">void</span>
}</span><font size="2"><span><span class="llvm_keyword"></span>

<span class="llvm_keyword">define</span> <span class="llvm_type">void</span> @nodevirtualize()() {
  %1 = <span class="llvm_keyword">tail</span> <span class="llvm_keyword">call</span> %struct.C* @GetRef()()
  %2 = <span class="llvm_keyword">getelementptr</span> inbounds %struct.C* %1, <span class="llvm_type">i64</span> 0, <span class="llvm_type">i32</span> 0
  %3 = <span class="llvm_keyword">bitcast</span> %struct.C* %1 <span class="llvm_keyword">to</span> <span class="llvm_type">void</span> (%struct.A*)***
  %4 = <span class="llvm_keyword">load</span> <span class="llvm_type">void</span> (%struct.A*)*** %3, <span class="llvm_keyword">align</span> 8
  %5 = <span class="llvm_keyword">load</span> <span class="llvm_type">void</span> (%struct.A*)** %4, <span class="llvm_keyword">align</span> 8
  <span class="llvm_keyword">tail</span> <span class="llvm_keyword">call</span> <span class="llvm_type">void</span> %5(%struct.A* %2)
  <span class="llvm_keyword">ret</span> <span class="llvm_type">void</span>
}</span></font></pre>As expected Clang successfully devirtualize the call in the first example, and even succeeds in the second which MSVC didn't.<br><br>However it fails to handle the 3rd case (or so I assume from the pointer meddling), which seems very much like the second (`cr.a` is necessarily a `A` whatever the dynamic type of `cr`).<br>
<br><br>I therefore have 2 questions:<br><br>1/ I tried unsuccesfully to obtain the LLVM IR on my own PC (using MSYS), however clang refuses to emit it:<br><br><div style="margin-left: 40px;">$ clang -emit-llvm devirtualize.cpp -o devirtualize.o<br>
clang: error: 'i686-pc-mingw32': unable to pass LLVM bit-code files to linker<br><br></div>How could I get the LLVM IR ? (under textual representation, but I can use llvm-dis if I get bytecode I think)<br><br><br>
2/ Does anyone have any inkling as to where the devirtualization occur in Clang ? I'd like to have a look but for now I never explored past Sema and this seems something more like CodeGen.<br><br>Thanks :)<br><br>-- Matthieu<br>