ARMv8 浮点输出内联汇编
ARMv8 floating point output inline assembly
为了将两个整数相加,我写:
int sum;
asm volatile("add %0, x3, x4" : "=r"(sum) : :);
如何使用两个浮点数来做到这一点?
我试过了:
float sum;
asm volatile("fadd %0, s3, s4" : "=r"(sum) : :);
但是它给我一个错误:
Error: operand 1 should be a SIMD vector register -- `fadd x0,s3,s4'
有什么想法吗?
"=r"
是 GP 整数寄存器的约束。
The GCC manual 声称 "=w"
是 AArch64 上 FP / SIMD 寄存器的约束。 但是如果你尝试这样做,你会得到 v0
而不是 s0
,后者不会 assemble。 我不知道这里有什么解决方法,你可能应该在 gcc bugzilla 上报告手册中记录的约束不适用于标量 FP。
On Godbolt 我试过这个来源:
float foo()
{
float sum;
#ifdef __aarch64__
asm volatile("fadd %0, s3, s4" : "=w"(sum) : :); // AArch64
#else
asm volatile("fadds %0, s3, s4" : "=t"(sum) : :); // ARM32
#endif
return sum;
}
double dsum()
{
double sum;
#ifdef __aarch64__
asm volatile("fadd %0, d3, d4" : "=w"(sum) : :); // AArch64
#else
asm volatile("faddd %0, d3, d4" : "=w"(sum) : :); // ARM32
#endif
return sum;
}
clang7.0(内置 assembler)要求 asm 实际上有效。但是对于 gcc,我们只编译为 asm,而 Godbolt 没有针对非 x86 的 "binary mode"。
# AArch64 gcc 8.2 -xc -O3 -fverbose-asm -Wall
# INVALID ASM, errors if you try to actually assemble it.
foo:
fadd v0, s3, s4 // sum
ret
dsum:
fadd v0, d3, d4 // sum
ret
clang 生成相同的 asm,其内置 assembler 错误为:
<source>:5:18: error: invalid operand for instruction
asm volatile("fadd %0, s3, s4" : "=w"(sum) : :);
^
<inline asm>:1:11: note: instantiated into assembly here
fadd v0, s3, s4
^
在 32 位 ARM 上,=t"
用于单个工作,但 "=w"
用于(手册说您应该使用双精度)还为您提供 s0
和 gcc。不过,它适用于 clang。您必须使用 -mfloat-abi=hard
和 -mcpu=
带有 FPU 的东西,例如-mcpu=cortex-a15
# clang7.0 -xc -O3 -Wall--target=arm -mcpu=cortex-a15 -mfloat-abi=hard
# valid asm for ARM 32
foo:
vadd.f32 s0, s3, s4
bx lr
dsum:
vadd.f64 d0, d3, d4
bx lr
但是 gcc 失败了:
# ARM gcc 8.2 -xc -O3 -fverbose-asm -Wall -mfloat-abi=hard -mcpu=cortex-a15
foo:
fadds s0, s3, s4 @ sum
bx lr @
dsum:
faddd s0, d3, d4 @ sum @@@ INVALID
bx lr @
所以你可以使用 =t
作为 gcc 的 single 就好了,但是对于 double
大概你需要一个 %something0
修饰符来打印寄存器名称作为 d0
s0
,输出 "=w"
。
显然,如果您也添加约束来指定输入操作数,而不是读取 s3 和 s4 中的任何内容,那么这些 asm 语句只会对学习语法之外的任何事情有用。
因为寄存器在 AArch64 中可以有多个名称(v0、b0、h0、s0、d0 都指的是同一个寄存器)所以需要在打印字符串中添加一个输出修饰符:
float foo()
{
float sum;
asm volatile("fadd %s0, s3, s4" : "=w"(sum) : :);
return sum;
}
double dsum()
{
double sum;
asm volatile("fadd %d0, d3, d4" : "=w"(sum) : :);
return sum;
}
将产生:
foo:
fadd s0, s3, s4 // sum
ret
dsum:
fadd d0, d3, d4 // sum
ret
ARMv7 双:%P
修饰符
GCC 开发人员在 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89482#c4 告诉我正确的 ARMv7 双打未记录修饰符也许我应该停止偷懒,有一天 grep GCC:
main.c
#include <assert.h>
int main(void) {
double my_double = 1.5;
__asm__ (
"vmov.f64 d0, 1.0;"
"vadd.f64 %P[my_double], %P[my_double], d0;"
: [my_double] "+w" (my_double)
:
: "d0"
);
assert(my_double == 2.5);
}
编译并运行:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-gcc -O3 -std=c99 -ggdb3 -march=armv7-a -marm \
-pedantic -Wall -Wextra -o main.out main.c
qemu-arm -L /usr/arm-linux-gnueabihf main.out
反汇编包含:
0x00010320 <+4>: 08 7b b7 ee vmov.f64 d7, #120 ; 0x3fc00000 1.5
0x00010324 <+8>: 00 0b b7 ee vmov.f64 d0, #112 ; 0x3f800000 1.0
0x00010328 <+12>: 00 7b 37 ee vadd.f64 d7, d7, d0
在 Ubuntu 16.04、GCC 5.4.0、QEMU 2.5.0 中测试。
源码定义点
为了将两个整数相加,我写:
int sum;
asm volatile("add %0, x3, x4" : "=r"(sum) : :);
如何使用两个浮点数来做到这一点? 我试过了:
float sum;
asm volatile("fadd %0, s3, s4" : "=r"(sum) : :);
但是它给我一个错误:
Error: operand 1 should be a SIMD vector register -- `fadd x0,s3,s4'
有什么想法吗?
"=r"
是 GP 整数寄存器的约束。
The GCC manual 声称 "=w"
是 AArch64 上 FP / SIMD 寄存器的约束。 但是如果你尝试这样做,你会得到 v0
而不是 s0
,后者不会 assemble。 我不知道这里有什么解决方法,你可能应该在 gcc bugzilla 上报告手册中记录的约束不适用于标量 FP。
On Godbolt 我试过这个来源:
float foo()
{
float sum;
#ifdef __aarch64__
asm volatile("fadd %0, s3, s4" : "=w"(sum) : :); // AArch64
#else
asm volatile("fadds %0, s3, s4" : "=t"(sum) : :); // ARM32
#endif
return sum;
}
double dsum()
{
double sum;
#ifdef __aarch64__
asm volatile("fadd %0, d3, d4" : "=w"(sum) : :); // AArch64
#else
asm volatile("faddd %0, d3, d4" : "=w"(sum) : :); // ARM32
#endif
return sum;
}
clang7.0(内置 assembler)要求 asm 实际上有效。但是对于 gcc,我们只编译为 asm,而 Godbolt 没有针对非 x86 的 "binary mode"。
# AArch64 gcc 8.2 -xc -O3 -fverbose-asm -Wall
# INVALID ASM, errors if you try to actually assemble it.
foo:
fadd v0, s3, s4 // sum
ret
dsum:
fadd v0, d3, d4 // sum
ret
clang 生成相同的 asm,其内置 assembler 错误为:
<source>:5:18: error: invalid operand for instruction
asm volatile("fadd %0, s3, s4" : "=w"(sum) : :);
^
<inline asm>:1:11: note: instantiated into assembly here
fadd v0, s3, s4
^
在 32 位 ARM 上,=t"
用于单个工作,但 "=w"
用于(手册说您应该使用双精度)还为您提供 s0
和 gcc。不过,它适用于 clang。您必须使用 -mfloat-abi=hard
和 -mcpu=
带有 FPU 的东西,例如-mcpu=cortex-a15
# clang7.0 -xc -O3 -Wall--target=arm -mcpu=cortex-a15 -mfloat-abi=hard
# valid asm for ARM 32
foo:
vadd.f32 s0, s3, s4
bx lr
dsum:
vadd.f64 d0, d3, d4
bx lr
但是 gcc 失败了:
# ARM gcc 8.2 -xc -O3 -fverbose-asm -Wall -mfloat-abi=hard -mcpu=cortex-a15
foo:
fadds s0, s3, s4 @ sum
bx lr @
dsum:
faddd s0, d3, d4 @ sum @@@ INVALID
bx lr @
所以你可以使用 =t
作为 gcc 的 single 就好了,但是对于 double
大概你需要一个 %something0
修饰符来打印寄存器名称作为 d0
s0
,输出 "=w"
。
显然,如果您也添加约束来指定输入操作数,而不是读取 s3 和 s4 中的任何内容,那么这些 asm 语句只会对学习语法之外的任何事情有用。
因为寄存器在 AArch64 中可以有多个名称(v0、b0、h0、s0、d0 都指的是同一个寄存器)所以需要在打印字符串中添加一个输出修饰符:
float foo()
{
float sum;
asm volatile("fadd %s0, s3, s4" : "=w"(sum) : :);
return sum;
}
double dsum()
{
double sum;
asm volatile("fadd %d0, d3, d4" : "=w"(sum) : :);
return sum;
}
将产生:
foo:
fadd s0, s3, s4 // sum
ret
dsum:
fadd d0, d3, d4 // sum
ret
ARMv7 双:%P
修饰符
GCC 开发人员在 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89482#c4 告诉我正确的 ARMv7 双打未记录修饰符也许我应该停止偷懒,有一天 grep GCC:
main.c
#include <assert.h>
int main(void) {
double my_double = 1.5;
__asm__ (
"vmov.f64 d0, 1.0;"
"vadd.f64 %P[my_double], %P[my_double], d0;"
: [my_double] "+w" (my_double)
:
: "d0"
);
assert(my_double == 2.5);
}
编译并运行:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-gcc -O3 -std=c99 -ggdb3 -march=armv7-a -marm \
-pedantic -Wall -Wextra -o main.out main.c
qemu-arm -L /usr/arm-linux-gnueabihf main.out
反汇编包含:
0x00010320 <+4>: 08 7b b7 ee vmov.f64 d7, #120 ; 0x3fc00000 1.5
0x00010324 <+8>: 00 0b b7 ee vmov.f64 d0, #112 ; 0x3f800000 1.0
0x00010328 <+12>: 00 7b 37 ee vadd.f64 d7, d7, d0
在 Ubuntu 16.04、GCC 5.4.0、QEMU 2.5.0 中测试。
源码定义点