Modeling volatile register read as intrinsic

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Modeling volatile register read as intrinsic

Justin Holewinski-2
= Background =

We are trying to introduce a target-dependent intrinsic that compiles to a read of a volatile register.  In this case, the register holds a perf counter value, but in general the register can hold any volatile value.  The problem we have is that it seems this cannot be modeled in LLVM currently.


= Issue =

The issue we are having is deciding how to represent the intrinsic in LLVM IR; specifically, the intrinsic properties.  We cannot use IntrNoMem since that does not model a volatile read of a value.  For example, if we have two calls to the intrinsic in a function, the optimizers will eliminate one of the reads and replace it with the value from the other read.

%i1 = tail call i32 @llvm.foo()
%i2 = tail call i32 @llvm.foo()
%i3 = add i32 %i1, %i2

becomes

%i1 = tail call i32 @llvm.foo()
%i2 = add i32 %i1, %i2

Since the read should be volatile, this is not what we want.  On the other hand, we do not want to use an empty set of properties since this is too conservative and prevents valid optimizations.  For example, if we do not use any Intr* properties, the optimizers cannot prove that the intrinsic does not modify memory.

store i32 42, i32* %ptr
%i1 = tail call i32 @llvm.foo()
%i2 = load i32* %ptr
...
ret i32 %i2

should be optimized to

store i32 42, i32* %ptr
%i1 = tail call i32 @llvm.foo()
...
ret i32 42

but this does not happen since the optimizers cannot prove that the intrinsic did not modify the value in memory at %ptr.

The IntrReadMem property gets us a bit closer, but still does not model the volatile property correctly.  The optimizers correctly see the operation as read-only and can optimize other memory accesses between intrinsic calls accordingly, but they also assume that the intrinsic reads the same memory and its return value will not change if a store is not seen between calls to the intrinsic.  This is the definition of 'readonly' in LLVM IR, which is not quite what we want.  We still have the same issue as using IntrNoMem in this regard:

%i1 = tail call i32 @llvm.foo()
%i2 = tail call i32 @llvm.foo()
%i3 = add i32 %i1, %i2

becomes

%i1 = tail call i32 @llvm.foo()
%i2 = add i32 %i1, %i2

unless there is a store instruction between the calls.  For correctness, we are better off using no properties (over-conservative).


Am I missing something here that could represent this kind of intrinsic?  It seems like we need a new function attribute readonly_volatile.

--

Thanks,

Justin Holewinski

_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Reply | Threaded
Open this post in threaded view
|

Re: Modeling volatile register read as intrinsic

Philip Reames-4

On 02/12/2014 05:48 AM, Justin Holewinski wrote:
= Background =

We are trying to introduce a target-dependent intrinsic that compiles to a read of a volatile register.  In this case, the register holds a perf counter value, but in general the register can hold any volatile value.  The problem we have is that it seems this cannot be modeled in LLVM currently.


= Issue =

The issue we are having is deciding how to represent the intrinsic in LLVM IR; specifically, the intrinsic properties.  We cannot use IntrNoMem since that does not model a volatile read of a value.  For example, if we have two calls to the intrinsic in a function, the optimizers will eliminate one of the reads and replace it with the value from the other read.

%i1 = tail call i32 @llvm.foo()
%i2 = tail call i32 @llvm.foo()
%i3 = add i32 %i1, %i2

becomes

%i1 = tail call i32 @llvm.foo()
%i2 = add i32 %i1, %i2

Since the read should be volatile, this is not what we want.  On the other hand, we do not want to use an empty set of properties since this is too conservative and prevents valid optimizations.  For example, if we do not use any Intr* properties, the optimizers cannot prove that the intrinsic does not modify memory.

store i32 42, i32* %ptr
%i1 = tail call i32 @llvm.foo()
%i2 = load i32* %ptr
...
ret i32 %i2

should be optimized to

store i32 42, i32* %ptr
%i1 = tail call i32 @llvm.foo()
...
ret i32 42

but this does not happen since the optimizers cannot prove that the intrinsic did not modify the value in memory at %ptr.

The IntrReadMem property gets us a bit closer, but still does not model the volatile property correctly.  The optimizers correctly see the operation as read-only and can optimize other memory accesses between intrinsic calls accordingly, but they also assume that the intrinsic reads the same memory and its return value will not change if a store is not seen between calls to the intrinsic.  This is the definition of 'readonly' in LLVM IR, which is not quite what we want.  We still have the same issue as using IntrNoMem in this regard:

%i1 = tail call i32 @llvm.foo()
%i2 = tail call i32 @llvm.foo()
%i3 = add i32 %i1, %i2

becomes

%i1 = tail call i32 @llvm.foo()
%i2 = add i32 %i1, %i2

unless there is a store instruction between the calls.  For correctness, we are better off using no properties (over-conservative).
I have to say this example surprised me.  This is not what I would expect a read-only annotation to enable. 

Am I missing something here that could represent this kind of intrinsic?  It seems like we need a new function attribute readonly_volatile.
I would prefer to see the current IntrReadMem modified to allow volatile reads.  Then a new IntrReadMemNonVolatile could be added to describe the current semantics in a less surprising way. 

Philip


_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev