【问题标题】:Operand type mismatch in x87 inline assembly in a Linux kernel moduleLinux内核模块中x87内联汇编中的操作数类型不匹配
【发布时间】:2016-05-18 17:48:23
【问题描述】:

我真的很想在 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'

我们将不胜感激。

【问题讨论】:

  • 哇哇哇?这不是推荐问题,只是标题不好。投票重新开放。
  • 标题已经过编辑,以更接近问题的内容,而不是简单的教程推荐。
  • 同意。这是一个合法的问题。
  • 您的输出 =r 和输入 r 约束当前指示使用通用整数寄存器作为 fldfst 的参数。我认为您可能需要将其更改为两个输入 m(内存)约束。
  • 旁注:当您在内核中执行浮点运算时,您必须特别注意保存/恢复 FP 环境,以防止上下文切换弄乱用户空间应用程序。有特殊的内核调用可以做到这一点[或者你禁用中断等并手动保存/恢复]

标签: c linux-kernel kernel inline-assembly x87


【解决方案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;
}

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

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


或者完全避免内联汇编,使用可以内联的 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 恢复成本更低。

【讨论】:

  • 这被标记:error: output operand 0 must use ‘&’ constraint。你需要=&t吗? gcc 版本 5.3.1
  • 我没有在我的盒子上看到它(gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04))。它甚至会产生正确的结果。但是,它也适用于'&',因此将其添加到我的答案中。
  • 我已经反汇编了“m”版本 [Iwillnotexist 建议] 和你的。你的有更多的包装代码。我没有分析哪个是正确的/不正确的[如果有的话]
  • @CraigEstey 那是因为tf 约束强加了一个(人为)约束,即输入必须已经在x87 堆栈寄存器中,而我的没有。为了让它和我的一样好,但使用 tf 我会删除明确的 fldfst 指令。
  • 这被标记:error: impossible constraint in asm 带有或不带有 & 符号。 GCC 是 gcc (Debian 4.7.2-5) 4.7.2
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 2019-02-17
  • 2019-04-01
  • 2012-11-20
  • 1970-01-01
  • 2018-02-11
相关资源
最近更新 更多