Open Quuxplusone opened 4 years ago
More reduced example: https://godbolt.org/z/GKerhj
int size;
struct Array2DRef {
int* data;
};
static __attribute__((noinline)) void
sink(const Array2DRef highhigh, const Array2DRef lowhigh, Array2DRef highpass) {
for(int row = 0; row < size; ++row) {
// highpass does not alias highhigh/lowhigh !
// this should nicely vectorize.
highpass.data[row] = 24 * highhigh.data[row] + 18 * lowhigh.data[row];
}
}
void foo(const Array2DRef highhigh,
const Array2DRef lowhigh,
int* highpass_storage) {
highpass_storage = new int[size];
Array2DRef highpass;
highpass.data = highpass_storage;
sink(highhigh, lowhigh, highpass);
}
In this example
sink()
is static, we know all it's call sites.We should be able to tell in all the call sites, it's third argument (
highpass.data
) does not alias with any other pointers used insink()
(namelyhighhigh.data
andlowhigh.data
), because we should be able to see howhighpass.data
is being set to a fresh noalias pointer.https://godbolt.org/z/32edkj
include // for assert
include
include // for vector
include // for vector
include // for vector
template class Array2DRef {
int _pitch = 0;
T* _data = nullptr;
friend Array2DRef; // We need to be able to convert to const version.
inline T& operator[](int row) const;
public: using value_type = T; using cvless_value_type = typename std::remove_cv::type;
int width = 0, height = 0;
Array2DRef() = default;
Array2DRef(T* data, int dataWidth, int dataHeight, int dataPitch = 0);
// Conversion from Array2DRef to Array2DRef.
template <class T2, typename = std::enable_if_t<std::is_same<
typename std::remove_const::type, T2>::value>>
Array2DRef(Array2DRef RHS) { // NOLINT google-explicit-constructor
_data = RHS._data;
_pitch = RHS._pitch;
width = RHS.width;
height = RHS.height;
}
template <typename AllocatorType = typename std::vector::allocator_type>
static Array2DRef
create(std::vector<cvless_value_type, AllocatorType> storage, int width,
int height) {
storage->resize(width height);
return {storage->data(), width, height};
}
inline T& operator()(int row, int col) const; };
template
Array2DRef::Array2DRef(T data, const int dataWidth, const int dataHeight,
const int dataPitch / = 0 */)
: _data(data), width(dataWidth), height(dataHeight) {
assert(width >= 0);
assert(height >= 0);
_pitch = (dataPitch == 0 ? dataWidth : dataPitch);
}
template T& Array2DRef::operator[](const int row) const {
assert(_data);
assert(row >= 0);
assert(row < height);
return _data[row * _pitch];
}
template
T& Array2DRef::operator()(const int row, const int col) const {
assert(col >= 0);
assert(col < width);
return (&(operator))[col];
}
static attribute((noinline)) void sink(const Array2DRef highhigh, const Array2DRef lowhigh, Array2DRef highpass) {
for(int row = 0; row < highpass.height; ++row) {
for(int col = 0; col < highpass.width; ++col) {
// highpass does not alias highhigh/lowhigh !
// this should nicely vectorize.
highpass(row, col) = 24 highhigh(row, col) + 18 lowhigh (row, col);
}
}
}
void foo(int width, int height, const Array2DRef highhigh, const Array2DRef lowhigh, std::vector& highpass_storage) {
Array2DRef highpass = Array2DRef::create(&highpass_storage, width, height);
sink(highhigh, lowhigh, highpass);
}