On Wed, Oct 13, 2010 at 11:16 PM, Nick Lewycky <nicholas at mxc.ca> wrote:> Kenneth Uildriks wrote: >> >> On Wed, Oct 13, 2010 at 12:45 AM, Nick Lewycky<nicholas at mxc.ca> wrote: >>> >>> Kenneth Uildriks wrote: >>>>> >>>>> You're right, I hadn't thought this through. The whole point of making >>>>> them >>>>> local is to say that "I'm sure these callees won't modify that memory" >>>>> regardless of what functions actually get called, even indirectly. We >>>>> can't >>>>> know that they won't modify the vptr in advance, so invariant doesn't >>>>> work >>>>> here. Making it non-local just means that we would need to know the >>>>> static >>>>> call graph, which we don't because we haven't devirtualized yet so all >>>>> the >>>>> calls are indirect. >>>>> Nick >>>> >>>> So that means you're now saying that llvm.invariant.end ought to be >>>> left the way it is, right? >>> >>> I have no idea how to make use of llvm.invariant to help >>> devirtualization. >>> If no use or other use case for them can be found, maybe they should be >>> removed. >> >> Apply invariant to the vtpr as long as the corresponding instance >> pointer is in use within a function. With an invariant vtpr (and >> better invariant support, and a front-end that uses it), >> devirtualization happens more or less automatically with >> -std-compile-opts. > > That's not valid. Each function called through the pointer could change the > vptr. Yes, you can do this in a defined way in C++, for example, by calling > the destructor and then placement-new'ing an object of a derived type with > the same size in its place. > > NickBut unless you placement-new'd an object of the exact same type in its place, you're not allowed to use the original pointer to make any more virtual calls on it. At least that's how I understand John's message from a few days ago.
On Oct 14, 2010, at 5:08 AM, Kenneth Uildriks wrote:> On Wed, Oct 13, 2010 at 11:16 PM, Nick Lewycky <nicholas at mxc.ca> wrote: >> Kenneth Uildriks wrote: >>> >>> On Wed, Oct 13, 2010 at 12:45 AM, Nick Lewycky<nicholas at mxc.ca> wrote: >>>> >>>> Kenneth Uildriks wrote: >>>>>> >>>>>> You're right, I hadn't thought this through. The whole point of making >>>>>> them >>>>>> local is to say that "I'm sure these callees won't modify that memory" >>>>>> regardless of what functions actually get called, even indirectly. We >>>>>> can't >>>>>> know that they won't modify the vptr in advance, so invariant doesn't >>>>>> work >>>>>> here. Making it non-local just means that we would need to know the >>>>>> static >>>>>> call graph, which we don't because we haven't devirtualized yet so all >>>>>> the >>>>>> calls are indirect. >>>>>> Nick >>>>> >>>>> So that means you're now saying that llvm.invariant.end ought to be >>>>> left the way it is, right? >>>> >>>> I have no idea how to make use of llvm.invariant to help >>>> devirtualization. >>>> If no use or other use case for them can be found, maybe they should be >>>> removed. >>> >>> Apply invariant to the vtpr as long as the corresponding instance >>> pointer is in use within a function. With an invariant vtpr (and >>> better invariant support, and a front-end that uses it), >>> devirtualization happens more or less automatically with >>> -std-compile-opts. >> >> That's not valid. Each function called through the pointer could change the >> vptr. Yes, you can do this in a defined way in C++, for example, by calling >> the destructor and then placement-new'ing an object of a derived type with >> the same size in its place. > > But unless you placement-new'd an object of the exact same type in its > place, you're not allowed to use the original pointer to make any more > virtual calls on it. At least that's how I understand John's message > from a few days ago.Correct. Basically, the standard lets us assume that — if we know the dynamic type of an object at a certain point for some reason — that dynamic type is fixed when accessed through that pointer. Like the type-aliasing rules, this is probably one of those things that we need to be a little careful about taking advantage of, because even conscientious users are going to write their code around assumptions of what a "reasonable compiler" is going to optimize. John.
> Correct. Basically, the standard lets us assume that — if we know the dynamic > type of an object at a certain point for some reason — that dynamic > type is fixed when accessed through that pointer. > > Like the type-aliasing rules, this is probably one of those things that we need > to be a little careful about taking advantage of, because even conscientious > users are going to write their code around assumptions of what a > "reasonable compiler" is going to optimize. > > John.You've got a point there. I know I get a little cranky when my code *usually* works and I can't figure out why. But still... there's an awful lot of class and function templates out there that mainly seem to be there to force devirtualization. The Boost libraries are loaded with them. I'd really like to see the need for that go away. Not to mention that even in the cases where we can't narrow down a virtual call to one function, if we can narrow it down to a finite set, we can construct a better callgraph and take better advantage of interprocedural optimizations. Maybe after I finish working on invariant support, I'll take a look at clang's "undefined behavior checking" open project... that should encourage enabling more aggressive assumptions over the long run.