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