There are 3 code blocks:
A:
asm volatile(
"fmla v0.4s, v30.4s, v22.s[0] \r\n"
"fmla v1.4s, v31.4s, v22.s[0] \r\n"
"fmla v2.4s, v30.4s, v22.s[1] \r\n"
"fmla v3.4s, v31.4s, v22.s[1] \r\n"
"fmla v4.4s, v30.4s, v22.s[2] \r\n"
"fmla v5.4s, v31.4s, v22.s[2] \r\n"
"fmla v6.4s, v30.4s, v22.s[3] \r\n"
"fmla v7.4s, v31.4s, v22.s[3] \r\n"
"fmla v8.4s, v30.4s, v23.s[0] \r\n"
"fmla v9.4s, v31.4s, v23.s[0] \r\n"
"fmla v10.4s, v30.4s, v23.s[1] \r\n"
"fmla v11.4s, v31.4s, v23.s[1] \r\n"
"fmla v12.4s, v30.4s, v23.s[2] \r\n"
"fmla v13.4s, v31.4s, v23.s[2] \r\n"
"fmla v14.4s, v30.4s, v23.s[3] \r\n"
"fmla v15.4s, v31.4s, v23.s[3] \r\n"
:
:
: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");
B:
asm volatile(
"fmul v0.4s, v30.4s, v22.s[0] \r\n"
"fmul v1.4s, v31.4s, v22.s[0] \r\n"
"fmul v2.4s, v30.4s, v22.s[1] \r\n"
"fmul v3.4s, v31.4s, v22.s[1] \r\n"
"fmul v4.4s, v30.4s, v22.s[2] \r\n"
"fmul v5.4s, v31.4s, v22.s[2] \r\n"
"fmul v6.4s, v30.4s, v22.s[3] \r\n"
"fmul v7.4s, v31.4s, v22.s[3] \r\n"
"fmul v8.4s, v30.4s, v23.s[0] \r\n"
"fmul v9.4s, v31.4s, v23.s[0] \r\n"
"fmul v10.4s, v30.4s, v23.s[1] \r\n"
"fmul v11.4s, v31.4s, v23.s[1] \r\n"
"fmul v12.4s, v30.4s, v23.s[2] \r\n"
"fmul v13.4s, v31.4s, v23.s[2] \r\n"
"fmul v14.4s, v30.4s, v23.s[3] \r\n"
"fmul v15.4s, v31.4s, v23.s[3] \r\n"
:
:
: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");
C:
asm volatile(
"fmla v0.4s, v30.4s, v22.s[0] \r\n"
"fmla v1.4s, v31.4s, v22.s[0] \r\n"
"fmla v2.4s, v30.4s, v22.s[1] \r\n"
"fmla v3.4s, v31.4s, v22.s[1] \r\n"
"fmla v4.4s, v30.4s, v22.s[2] \r\n"
"fmla v5.4s, v31.4s, v22.s[2] \r\n"
"fmla v6.4s, v30.4s, v22.s[3] \r\n"
"fmla v7.4s, v31.4s, v22.s[3] \r\n"
"fmla v8.4s, v30.4s, v23.s[0] \r\n"
"fmla v9.4s, v31.4s, v23.s[0] \r\n"
"fmla v10.4s, v30.4s, v23.s[1] \r\n"
"fmla v11.4s, v31.4s, v23.s[1] \r\n"
"fmla v12.4s, v30.4s, v23.s[2] \r\n"
"fmla v13.4s, v31.4s, v23.s[2] \r\n"
"fmla v14.4s, v30.4s, v23.s[3] \r\n"
"fmul v15.4s, v31.4s, v23.s[3] \r\n"
:
:
: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15");
The only difference between A and B is FMLA vs. FMUL, and the only difference
between A and C is the last FMLA instruction is changed to FMUL
And there will be some codes to execute each block many times and record the time cost:
auto start=std::chrono::high_resolution_clock::now();
for(int i=0; i<1000*1000*1000; i++) {
<Block A or B or C>;
}
auto end=std::chrono::high_resolution_clock::now();
printf("%f\n", std::chrono::duration<double, std::milli>(end-start).count());
But the result is A and B use almost the same time, while C is much more slower than A and B (cost near 30% more time than A and B).
Test environment:
Raspbery Pi 3 with pi64 (https://github.com/bamarni/pi64 a 64bit Debian OS)
compiler: g++ 7.2.0
By the way, I have tested this on some other ARMv8 CPU:
SoC or Board CPU Micro-arch Result
Raspbery Pi 3 Cortex-A53 Slow
A MTK Soc Cortex-A53 Slow
Snapdragon 820 Kyro by Qualcomm Same
NVIDIA TX2 Cortex-A57 Same
NVIDIA TX2 Denver2 by NVIDIA Same
It seem's this problem will only appear on Cortex-A53 or on in-order micro-architecture.
I have read the deassembled code and it looks fine.