<html>
<head>
<base href="https://bugs.llvm.org/">
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW - clobbered return value from this-adjustment thunk returning a member pointer"
href="https://bugs.llvm.org/show_bug.cgi?id=39901">39901</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>clobbered return value from this-adjustment thunk returning a member pointer
</td>
</tr>
<tr>
<th>Product</th>
<td>clang
</td>
</tr>
<tr>
<th>Version</th>
<td>trunk
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>Linux
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>enhancement
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>LLVM Codegen
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>hans@chromium.org
</td>
</tr>
<tr>
<th>CC</th>
<td>llvm-bugs@lists.llvm.org, neeilans@live.com, richard-llvm@metafoo.co.uk
</td>
</tr></table>
<p>
<div>
<pre>Consider the following example, targeting 32-bit x86:
struct X;
typedef void (X::*memptr)();
struct A {
virtual memptr f();
};
struct B {
virtual memptr f();
};
struct C : A, B {
C();
memptr f() override __attribute__((noinline)) { return nullptr; };
};
C::C() {}
$ clang -cc1 -triple i686 -emit-llvm -o - a.cc -O3
[...]
define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret
%agg.result, %struct.C* %this) unnamed_addr #1 comdat align 2 {
entry:
%tmp = alloca { i32, i32 }, align 4
%0 = getelementptr inbounds %struct.C, %struct.C* %this, i32 -1, i32 1
%1 = bitcast %struct.B* %0 to %struct.C*
tail call void @_ZN1C1fEv({ i32, i32 }* nonnull sret %tmp, %struct.C* nonnull
%1)
ret void
}
Note that the sret value from @_ZN1C1fEv gets dropped!
The -O0 IR looks like this:
define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret
%agg.result, %struct.C* %this) unnamed_addr #0 comdat align 2 {
entry:
%retval = alloca { i32, i32 }, align 4
%this.addr = alloca %struct.C*, align 4
%tmp = alloca { i32, i32 }, align 4
store %struct.C* %this, %struct.C** %this.addr, align 4
%this1 = load %struct.C*, %struct.C** %this.addr, align 4
%0 = bitcast %struct.C* %this1 to i8*
%1 = getelementptr inbounds i8, i8* %0, i32 -4
%2 = bitcast i8* %1 to %struct.C*
tail call void @_ZN1C1fEv({ i32, i32 }* sret %tmp, %struct.C* %2)
%3 = load { i32, i32 }, { i32, i32 }* %tmp, align 4
store { i32, i32 } %3, { i32, i32 }* %retval, align 4
%4 = load { i32, i32 }, { i32, i32 }* %retval, align 4
store { i32, i32 } %4, { i32, i32 }* %agg.result, align 4
ret void
}
Note that the thunk is passing its %tmp alloca into the tail-called function.
That's not allowed: a tail-call function is not supposed to access alloca's
from the caller. Because of that, the subsequent copy from %tmp to %agg.result
gets optimized away.
Interestingly, this doesn't happen for all sret values, such as a regular
struct. I could only reproduce with a member pointer.</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>