[PATCH] D93376: [LangRef] Clarify the semantics of lifetime intrinsics

Juneyoung Lee via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 18 01:08:41 PST 2020


aqjune added a comment.

In D93376#2462245 <https://reviews.llvm.org/D93376#2462245>, @jdoerfert wrote:

> Why wouldn't/shouldn't it mean exactly what it means for stack allocations?
> "The memory region specified in the lifetime start is dead until a lifetime start makes that memory accessible, ..."
> Could you explain what is nontrivial about the interaction with free?



>> We're leaving a construct something that is really hard to be used. In perspective of the frontend, simply placing malloc()/free() or memset(undef) or their own marker at the place where lifetime.start/lifetime.end are supposed to be would have been a better choice.
>
> I'm not so sure about that. memset(undef) is, for example, not recognized commonly while `hasOnlyLifetimeUsers` (or similar) is a common check. Moving malloc/free may come with a cost.  Placing the malloc/free in a loop is a totally different story than placing lifetime markers in there. Imagine you have a heap allocation in a loop and the FE (or something else) places the malloc earlier instead and uses lifetime markers to assure the middle end that there are no loop carried dependences via this memory region. (Also, we should implement this transformation, it could not only save time wrt. allocations but also allow vectorization, especially given the lifetime markers.)

Because in case of stack allocated objects, lifetime.start/end determines the object's location as well. Two allocas with disjoint lifetimes should be able to be overlapped.
For a heap allocation, malloc()/free() should determine the address, and I wanted to say that it isn't clear whether frontend writers will expect lifetime intrinsics to determine the disjointness; it is unclear.

The optimization makes sense, but won't simply putting memset(undef) at the end of the loop do the trick? The backend pipeline (e.g. CodeGenPrepare) can remove the redundant memset.

> I disagree that syntactic constraints are needed, in a generic sense, and I find they often make the IR harder to work with. I'm not sure I understand your example but I guess if you inline the call the lifetime argument could be syntactically something else, right? So it might become syntactically useful even if it wasn't before. The PHI case is the opposite. It started to be syntactically useful, i.a., alloca arguments, but after sinking there might not be a single alloca but a phi picking one. Arguably, the information is no different. A user could easily determine all the underlying objects and filter the allocas.

It's because it can introduce UB. Consider this example:

  p = alloca i32
  store 0, p // analysis will say this is perfectly fine
  f(p) // lifetime.start(p) is hidden in f(p)

After inlining:

  p = alloca i32
  store 0, p
  lifetime.start(p) // this makes the above store raise UB 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D93376/new/

https://reviews.llvm.org/D93376



More information about the llvm-commits mailing list