Linux内核模块中x87内联汇编中的操作数类型不匹配

问题描述 投票:4回答:1

我真的想在Linux内核模块中使用浮点运算,只是为了它。我不想做任何花哨的事情,只需使用x87 trig指令和/或sqrt指令,然后将结果赋值给变量。就是这样。到目前为止,我已经尝试过:

float sqroot(float arg){
    float returnValue;
    asm(
     "fld %1\n"
     "fsqrt\n"
     "fst %0"
     :"=r"(returnValue) 
     : "r"(arg)
    );
    return returnValue;
}

这失败了,产生了以下错误:

Error: operand type mismatch for `fld'
Error: operand type mismatch for `fst'

任何和所有帮助将不胜感激。

c linux-kernel kernel inline-assembly x87
1个回答
1
投票

使用内核模块中的x87将“正常工作”,但会静默破坏用户空间x87 / MMX状态。 Why am I able to perform floating point operations inside a Linux kernel module?

你需要kernel_fpu_begin() / kernel_fpu_end()来保证这个安全。


而不是从内联asm加载/存储,请求输入并在x87寄存器堆栈的顶部生成输出,并让编译器在需要时发出加载/存储指令。编译器已经知道如何做到这一点,你只需要为sqrt指令本身使用内联asm,你可以用这种方式向编译器描述:

static inline
float sqroot(float arg) {
    asm("fsqrt"  : "+t"(arg) );
    return arg;
}

(请参阅此on the Godbolt compiler explorer的编译器生成的asm)

寄存器约束必须告诉块使用浮点寄存器。


或者完全避免内联asm,使用可以内联的GNU C内置函数

您需要使用-fno-math-errno作为内置实际内联作为fsqrtsqrtss,而不会回退到call sqrtf输入将导致NaN。

static inline
float sqroot_builtin(float arg) {
    return __builtin_sqrtf(arg);
}

对于x86-64,我们得到sqrtss %xmm0, %xmm0 / ret,而对于i386,我们得到fld / fsqrt / ret。 (参见上面的Godbolt链接)。恒定传播通过__builtin_sqrt和其他优化工作。


编辑:纳入@ iwillnotexist-idontexist的观点(重新加载)。

另外,如果是我,我会在声明中添加static inline并将其放入头文件中。这将允许编译器更智能地管理寄存器并避免堆栈帧开销。

(我也很想在整个过程中将float更改为double。否则,您将丢弃实际浮点指令中使用的额外精度。虽然如果最终经常将值存储为float,则会有一个额外的cvtpd2ps指令.OTOH,例如,如果你向printf传递论据,这实际上避免了cvtps2pd。)

但Linux内核kprintf无论如何都没有为double进行转换。

如果使用-mfpmath=387(32位代码的默认值)进行编译,则内联后值将保留在80位x87寄存器中。但是,对于使用64位默认值-mfpmath=sse的64位代码,这将导致在加载回XMM寄存器时四舍五入到float

kernel_fpu_begin()保存了完整的FPU状态,避免了SSE寄存器,只使用x87不会使它或最终的FPU恢复到任何更便宜的用户空间。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.