[LLVMdev] lifetime.start/end clarification

Arnaud A. de Grandmaison arnaud.degrandmaison at arm.com
Wed Nov 5 10:54:48 PST 2014


 

 

From: Philip Reames [mailto:listmail at philipreames.com] 
Sent: 05 November 2014 19:21
To: Arnaud De Grandmaison; LLVM Developers Mailing List
Subject: Re: [LLVMdev] lifetime.start/end clarification

 

 

On 11/04/2014 03:59 AM, Arnaud A. de Grandmaison wrote:

The LRM (http://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic)
essentially  states that:

- ptr is dead before a call to "lifetime.start size, ptr"

- ptr is dead after a call to "lifetime.end size, ptr"

 

This is all good and fine, and the expected use case is that all
"lifetime.end size, ptr" markers are matched with a preceding
"lifetime.start size, ptr" in the CFG.

 

What is supposed to happen when a "lifetime.end size, ptr" is not matched
with a "lifetime.start size, ptr" ? I think there are a few possible
answers:

- the memory area pointed to by ptr is assumed to be alive since function
entry

- the memory area pointed to by ptr is assumed to be dead since function
entry, as it has not been marked alive

- this is an unexpected situation

My reading of the current spec is that your first option is the only valid
interpretation.

My interpretation of the spec wording would be that each path in the program
may contain either a lifetime.start, a lifetime.end, or both.  If the path
contains no marker, the location is assumed live (unless proven otherwise).
The lifetime markers segment the path into live, and dead regions, but only
on that specific path.  To reason about global properties, the optimizer
must form "for-all-paths" facts.  (Dominance being the easiest form of this
generally.)



You worded the above as your interpretation of the spec. I agree your
interpretation is the sanest one. 


Just to note, this is rather different than the "invariant.*" family.  In
particular, the invariant family requires the "start" as an argument to the
"end" intrinsic.




 

I think this ambiguity should be cleared in the LRM, because today's
implicit assumption may be broken at any time.

I believe the existing implementation matches the semantics I specified
above.  If you find a case where it doesn't, that's a bug.  



I checked the stack coloring (the only real user of the lifetime markers),
and it seems to match this semantics.

 

This is not a theoretical question: clang can generate such cases. For
example, the following testcase:

struct X {

  void doSomething();

  char b[33];

};

 

void bar(X &);

void baz();

 

void test(int i) {

  if (i==9) {

    X x;

    x.doSomething();

label:

    bar(x);

  } else {

    baz();

    if (i==0)

      goto label;

  }

}

 

Produces:

 

%struct.X = type { [33 x i8] }

 

define void @_Z4testi(i32 %i) {

entry:

  %x = alloca %struct.X, align 1

  %cmp = icmp eq i32 %i, 9

  br i1 %cmp, label %if.then, label %if.else

 

if.then:                                          ; preds = %entry

  %0 = getelementptr inbounds %struct.X* %x, i64 0, i32 0, i64 0

  call void @llvm.lifetime.start(i64 33, i8* %0)

  call void @_ZN1X11doSomethingEv(%struct.X* %x)

  br label %label

 

label:                                            ; preds =
%if.else.label_crit_edge, %if.then

  %.pre-phi = phi i8* [ %.pre, %if.else.label_crit_edge ], [ %0, %if.then ]

  call void @_Z3barR1X(%struct.X* dereferenceable(33) %x)

  call void @llvm.lifetime.end(i64 33, i8* %.pre-phi)

  br label %if.end3

 

if.else:                                          ; preds = %entry

  tail call void @_Z3bazv()

  %cmp1 = icmp eq i32 %i, 0

  br i1 %cmp1, label %if.else.label_crit_edge, label %if.end3

 

if.else.label_crit_edge:                          ; preds = %if.else

  %.pre = getelementptr inbounds %struct.X* %x, i64 0, i32 0, i64 0

  br label %label

 

if.end3:                                          ; preds = %if.else, %label

  ret void

}

 

Note that the path thru if.else.label_crit_edge has no lifetime start.

This seems fine to me.  The optimizer can (soundly) conclude that %p is dead
after the "lifetime.end" (for the two instructions), and dead before the
"lifetime.start" (for the *single* instruction in that basic block, *not*
for the previous BB).  This seems like the proper result for this example,
am I missing something?



With the clarification you made on the semantics, the above IR is correct,
but could be improved when clang generates it: the label_crit_edge block
should contain a lifetime.start for the alloca.


Philip
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141105/3d4720dd/attachment.html>


More information about the llvm-dev mailing list