I am trying to optimize the following code complex.cpp:
typedef struct {
float re;
float im;
} dcmplx;
dcmplx ComplexConv(int len, dcmplx *hat, dcmplx *buf)
{
int i;
dcmplx z, xout;
xout.re = xout.im = 0.0;
asm volatile (
"movs r3, #0\n\t"
".loop:\n\t"
"vldr s11, [%[hat], #4]\n\t"
"vldr s13, [%[hat]]\n\t"
"vneg.f32 s11, s11\n\t"
"vldr s15, [%[buf], #4]\n\t"
"vldr s12, [%[buf]]\n\t"
"vmul.f32 s14, s15, s13\n\t"
"vmul.f32 s15, s11, s15\n\t"
"adds %[hat], #8\n\t"
"vmla.f32 s14, s11, s12\n\t"
"vnmls.f32 s15, s12, s13\n\t"
"adds %[buf], #8\n\t"
"vadd.f32 s1, s1, s14\n\t"
"vadd.f32 s0, s0, s15\n\t"
"adds r3, r3, #1\n\t"
"cmp r3, r0\n\t"
"bne .loop\n\t"
: "=r"(xout)
: [hat]"r"(hat),[buf]"r"(buf)
: "s0","cc"
);
return xout;
}
When it is compiled with "arm-linux-gnueabihf-g++ -c complex.cpp -o complex.o -mfpu=neon", I got the following error: impossible constraint in 'asm'.
When I comment out "=r"(xout), the compile doesn't complain, but how can I get result of register 's0' into xout?
Besides, how it works if r0 contains return value but the return type is a complicate structure, since r0 is only a 32-bit? register.
The original c code I post here:
dcmplx ComplexConv(int len, dcmplx *hat, dcmplx *buf)
{
int i;
dcmplx z, xout;
xout.re = xout.im = 0.0;
for(int i = 0; i < len; i++) {
z = BI_dcmul(BI_dconjg(hat[i]),buf[i]);
xout = BI_dcadd(xout,z);
}
return xout;
}
dcmplx BI_dcmul(dcmplx x, dcmplx y)
{
dcmplx z;
z.re = x.re * y.re - x.im * y.im;
z.im = x.im * y.re + x.re * y.im;
return z;
}
dcmplx BI_dconjg(dcmplx x)
{
dcmplx y;
y.re = x.re;
y.im = -x.im;
return y;
}
dcmplx BI_dcadd(dcmplx x, dcmplx y)
{
dcmplx z;
z.re = x.re + y.re;
z.im = x.im + y.im;
return z;
}
Your inline assembly code makes a number of mistakes:
"=r"
) constraint. This is what gives you the error.len
is supposed to be an input.loop
that unnecessarily prevents the compiler from inlining your code in multiple places.I'm not going to bother to explain how you can fix all these mistakes, because you shouldn't be using inline assembly. You can write your code in C++ and let the compiler do the vectorization.
For example compiling following code, equivalent to your example C++ code, with GCC 4.9 and the
-O3 -funsafe-math-optimizations
options:generates the following assembly as its inner loop:
Based on your inline assembly code, it's likely that the compiler generated better than what you would've come up with if you tried to vectorize it yourself.
The
-funsafe-math-optimizations
is necessary because the NEON instructions aren't fully IEEE 754 conformant. As the GCC documentation states:I should also note that the compiler generates almost as good as code above if you don't roll your own complex type, like in the following example:
One advantage to using your own type however, is that you can improve the code compiler generates making one small change to how you declare
struct dcmplx
:By saying it needs to be 8-byte (64-bit) aligned, this allows the compiler to skip the check to see if it is suitably aligned and then fall back on the slower scalar implementation instead.
Now, hypothetically, lets say you were unsatisfied with how GCC vectorized your code and thought you could do better. Would this justify using inline assembly? No, the next thing to try are the ARM NEON intrinsics. Using intrinics is just like normal C++ programming, you don't have worry about a bunch of special rules you need to follow. For example here's how I converted the vectorized assembly above into this untested code that uses intrinsics:
Finally if this wasn't good enough and you needed to tweak out every bit of performance you could then it's still not a good idea to use inline assembly. Instead your last resort should be to use regular assembly instead. Since your rewriting most of the function in assembly, you might as well write it completely in assembly. That means you don't have worry about telling the compiler about everything you're doing in the inline assembly. You only need to conform to the ARM ABI, which can be tricky enough, but is a lot easier than getting everything correct with inline assembly.