Tom Aernoudt via llvm-dev
2016-Feb-29 16:01 UTC
[llvm-dev] Jit: use @llvm.lifetime.end to optimize away stores to globals used as temporaries
Hi all, Is it allowed to use the @llvm.lifetime.end intrinsic to optimize away stores to global variables that are used as temporaries? eg I want use the Jit engine to generate code for the following function 'f': struct State { int a; int tmp; int b; }; void f0(State* s) { s->tmp = s->a; } void f1(State* s) { s->b = s->tmp; } void f(State* s) { f0(s); f1(s); } The Jit engine generates the following code: define void @_Z1fP5State(%struct.State* %s) #1 { %1 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 0 %2 = load i32, i32* %1, align 4, !tbaa !1 %3 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 1 store i32 %2, i32* %3, align 4, !tbaa !6 %4 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 2 store i32 %2, i32* %4, align 4, !tbaa !7 ret void } Which gives (on x86_64): _Z1fP5State: movl (%rdi), %eax movl %eax, 4(%rdi) movl %eax, 8(%rdi) retq The 'tmp' state variable is only needed during execution of the function 'f'. After the function has finished to value can be discarded. How can I tell the optimizer to optimize away to stores to the 'tmp' variable? Can I use the @llvm.lifetime.end intrinsic to do this? Eg If I change the function 'f' as follows: extern "C" void llvm_lifetime_end(unsigned long long, void*); void cleanup(State* s) { // llvm_lifetime_end will be replaced with @llvm.lifetime.end llvm_lifetime_end(sizeof(s->tmp), (void*)&s->tmp); } void f(State* s) { f0(s); f1(s); cleanup(s); } And then use a pass to replace the function llvm_lifetime_end with the intrinsic @llvm.lifetime.end. I get the following code: define void @_Z1fP5State(%struct.State* nocapture %s) #0 { %1 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 0 %2 = load i32, i32* %1, align 4, !tbaa !1 %3 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 1 %4 = getelementptr inbounds %struct.State, %struct.State* %s, i64 0, i32 2 store i32 %2, i32* %4, align 4, !tbaa !7 %5 = bitcast i32* %3 to i8* tail call void @llvm.lifetime.end(i64 4, i8* %5) ret void } _Z1fP5State: movl (%rdi), %eax movl %eax, 8(%rdi) retq Is this a supported usecase of the @llvm.lifetime.end intrinsic? ie is it allowed to use the intrinsic on a global variable instead of a local variable? Or is there a better way to get the same result? Is it ok to only have an @llvm.lifetime.end intrinsic, without a matching @llvm.lifetime.start intrinisic? If not is there another way to tell the optimizer to optimize away the stores to the 'tmp' state variable? Thanks, Tom -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160229/933885d2/attachment.html>
Nat! via llvm-dev
2016-Feb-29 17:34 UTC
[llvm-dev] Jit: use @llvm.lifetime.end to optimize away stores to globals used as temporaries
Tom Aernoudt via llvm-dev schrieb:> Hi all, > > The 'tmp' state variable is only needed during execution of the function > 'f'. >That's what you say, because you called it 'tmp' :), but the compiler sees no difference to 'a' or 'b'. As soon as s->tmp would "escape" f() the compiler will have to write it. I think you may want to keep external state and temporal context separate e.g. struct State { int a; int b; } struct Temp { int tmp; } static void f0( State *s, Temp *t) { t->tmp = s->a; } ... void f(State* s) { Temp tmp; f0( s, &tmp); f1( s, &tmp); } Using @llvm.lifetime.end is IMO a deadend, since this should just signify that the alloca is gone. Ciao Nat!
Tom Aernoudt via llvm-dev
2016-Mar-01 09:00 UTC
[llvm-dev] Jit: use @llvm.lifetime.end to optimize away stores to globals used as temporaries
> From: Nat! [mailto:nat at mulle-kybernetik.com] > Tom Aernoudt via llvm-dev schrieb: > > Hi all, > > > > The 'tmp' state variable is only needed during execution of the function > > 'f'. > > > That's what you say, because you called it 'tmp' :), but the compiler > sees no difference to 'a' or 'b'. As soon as s->tmp would "escape" f() > the compiler will have to write it. > > I think you may want to keep external state and temporal context separate > > e.g. > > struct State { > int a; > int b; > } > > > struct Temp { > int tmp; > } > > static void f0( State *s, Temp *t) > { > t->tmp = s->a; > } > > ... > > void f(State* s) > > { > Temp tmp; > > f0( s, &tmp); > f1( s, &tmp); > } > > Using @llvm.lifetime.end is IMO a deadend, since this should just > signify that the alloca is gone. >In the real code, the state variables ('tmp',...) do not have trivial constructors. Constructing them as local variables in the function 'f' adds a lot of extra code that may not always be optimized away. It will also require a lot of change to existing code. Using the @llvm.lifetime.end intrinsic seems to do exactly what we want to achieve, ie optimize away Store instructions to the 'tmp' variables with no/minimal changes to existing code (only the runtime generated functions 'f' have to be changed to call the @llvm.lifetime.end intrinsics). Is using the intrinsic on something else as an Alloca allowed? Or is the behavior I see just a side effect of the current implementation and may it break in the future? Regards, Tom