Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling FFTW's in-place real-to-complex transform without violating strict aliasing rules

I wish to call fftw's in-place real-to-complex transform function, which has the following signature:

fftw_plan fftw_plan_dft_r2c_1d(
    int n,             // transform length
    double* in,        // pointer to input array
    fftw_complex* out, // pointer to output array
    unsigned flags     // flags
);

The documentation says that I should indicate that I wish to perform an in-place transform by passing in aliasing pointers for the in and out parameters.


QUESTION: How can in and out alias without violating strict aliasing rules?


I am open to GCC-specific extensions (i.e., using unions to do type-punning, even though the standard declares this to be undefined behavior). Even if this extension is permitted, a union cannot contain dynamically-sized arrays (which is a must in this applications - I do not know the transform length in advance). Does anyone have any ideas? Thanks in advance.

like image 723
SumDood Avatar asked Sep 01 '19 21:09

SumDood


2 Answers

I'll challenge the premise: Don't worry about strict aliasing too much.

Make an array of double and pass a pointer to it to in. reinterpret_cast the pointer to fftw_complex * and pass it to out.

Read the resulting doubles from this array (as pairs of real and imaginary components of complex numbers).


Yes, fftw_plan_dft_r2c_1d will probably break strict aliasing under the hood if called this way.

But since it's in a separate translation unit, and caller doesn't violate strict aliasing, your compiler has no way to tell if strict aliasing was indeed violated.

fftw_complex is essentially a struct fftw_complex {double re, im;};, so everything should work just fine.

For extra safety you can add:

static_assert(sizeof(fftw_complex) == 2 * sizeof(double) && alignof(fftw_complex) <= alignof(double));
like image 95
HolyBlackCat Avatar answered Oct 06 '22 18:10

HolyBlackCat


According to this link fftw_complex is the following typedef:

typedef double fftw_complex[2];

And by the pre-C++20 rules fftw_complex* may alias double* because of this ([basic.lval]p8.6):

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
...
— an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union)

Array is an aggregate and our array contains doubles thus it is allowed to alias a double pointer. Hence, no strict aliasing rule violation happens in the fftw_plan_dft_r2c_1d function and you can safely use it.

Note, however, that this paragraph is removed from the C++20 standard and it is debated that it should be removed from the C standard as well. But since it is not removed yet and GCC & clang actually respect it I guess it is safe to assume that the behavior won't change with C++20 implementation. And MSVC, to my knowledge, doesn't take advantage of SAR at all.

like image 21
ixSci Avatar answered Oct 06 '22 16:10

ixSci