our special-purpose target has load/store instructions which do not handle classical pointers to the accessed data in memory as argument, but instead we use a configuration of structured data volumes, and our load and store instructions are intrinsics which get indices in different data volume dimensions as argument.
For example, we might have the following loads and stores (IR roughly simplified for readability):
%1 = call float @spu.load(volume_0, 1, 2, 3) // arithmetic... call float @spu.store(%0, volume_1, 4, 5, 6) // arithmetic... %2 = call float @spu.load(volume_0, 0, 0, 0) // arithmetic... %3 = call float @spu.load(volume_0, 1, 2, 3)
%1 from volume 0 with (x, y, z) coordinates (1, 2, 3), does some arithmetic, stores
%0 to volume 1 at coordinates (4, 5, 6) and so on.
We have marked the intrinsics with
IntrWriteMem so that in principle it is clear that they access memory.
Now, we want to do IR optimizations, e.g. eliminating redundant loads since the read memory has not changed. But since our intrinsics are no normal pointer based accesses, I think the standard LLVM passes for code simplification cannot prove that the accesse are redundant, since our intrinsics are seen as “black box global memory accesses”.
In the above example, it is clear from the semantics of our target that the store to volume 1 will not change any data in volume 0. Hence, The value loaded in
%1 could be reused for
%3 since the second time we do
call float @spu.load(volume_0, 1, 2, 3) this will load the same value again. However,
%2 will have to be loaded since it loads from different coordinates (0, 0, 0) instead of (1, 2, 3) in data volume 0.
Would it be possible to e.g. use MemorySSA in some way? If we could tell the underlying LLVM infrastructure how to figure out whether two memory accesses refer to the same data or not, it could be possible for us to employ existing passes in LLVM, which would be much easier for us than developing this ourselves.
Any hints would be greatly appreciated.