繁体   English   中英

是什么导致了这个错误:SSE 寄存器返回,SSE 被禁用?

[英]What is causing this error: SSE register return with SSE disabled?

我是 kernel 开发的新手,我需要编写一个 Linux kernel 模块来执行多个矩阵乘法(我在 x64_6 平台上工作) 我正在尝试对这些操作使用定点值,但是在编译期间,编译器遇到此错误:

error: SSE register return with SSE disabled

我对 SSE 或这个问题了解不多,但根据我的发现以及对这个问题的大多数答案,它与 kernel 空间中浮点 (FP) 算法的使用有关,这似乎不是一个好主意(因此使用定点算术)。 这个错误对我来说似乎很奇怪,因为我很确定我没有使用任何 FP 值或操作,但是它不断弹出并且在某些方面对我来说似乎很奇怪。 例如,我有这段代码:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

const int scale = 16;
#define DOUBLE_TO_FIXED(x) ((x) * (1 << scale))
#define FIXED_TO_DOUBLE(x) ((x) / (1 << scale))
#define MULT(x, y) ((((x) >> 8) * ((y) >> 8)) >> 0)
#define DIV(x, y) (((x) << 8) / (y) << 8)

#define OUTPUT_ROWS 6
#define OUTPUT_COLUMNS 2

struct matrix {
    int rows;
    int cols;
    double *data;
};

double outputlayer_weights[OUTPUT_ROWS * OUTPUT_COLUMNS] = 
{ 
         0.7977986,  -0.77172316,
        -0.43078753,  0.67738613,
        -1.04312621,  1.0552227 ,
        -0.32619684,  0.14119884,
        -0.72325027,  0.64673559,
         0.58467862, -0.06229197
};

...

void matmul (struct matrix *A, struct matrix *B, struct matrix *C) {
    int i, j, k, a, b, sum, fixed_prod;
    
    if (A->cols != B->rows) {
        return;
    }

    for (i = 0; i < A->rows; i++) {

        for (j = 0; j < B->cols; j++) {
            sum = 0;
            for (k = 0; k < A->cols; k++) {
                a = DOUBLE_TO_FIXED(A->data[i * A->rows + k]);
                b = DOUBLE_TO_FIXED(B->data[k * B->rows + j]);
                fixed_prod = MULT(a, b);
                sum += fixed_prod;
            }
            /* Commented the following line, causes error */
            //C->data[i * C->rows + j] = sum;
        }
    }
}


...

static int __init insert_matmul_init (void)
{
    printk(KERN_INFO "INSERTING MATMUL");
    return 0;
}

static void __exit insert_matmul_exit (void)
{
    printk(KERN_INFO "REMOVING MATMUL");
}

module_init (insert_matmul_init);
module_exit (insert_matmul_exit);

它编译没有错误(我遗漏了我发现与问题无关的代码)。 我确保对任何容易出错的行进行注释,以达到可以无错误编译程序的程度,并且我正在尝试逐个解决它们。 但是,取消注释此行时:

C->data[i * C->rows + j] = sum;

我在之前的(未修改的)代码行中收到此错误消息:

 error: SSE register return with SSE disabled
 sum += fixed_prod;
 ~~~~^~~~~~~~~~~~~

据我了解,至少在本节中没有发生 FP 操作,因此我需要帮助找出可能导致此错误的原因。 也许我的定点实现有缺陷(我也不是这方面的专家),或者我遗漏了一些明显的东西。 以防万一,我在用户空间程序中测试了相同的逻辑(使用浮点值),它似乎工作正常。 在任何一种情况下,我们将不胜感激解决此问题的任何帮助。 提前致谢!

编辑:我已经包含了matrix的定义和一个示例矩阵。 我一直在使用默认的 kbuild 命令来构建外部模块,这是我的 Makefile 的样子:

obj-m = matrix_mult.o
KVERSION = $(shell uname -r)

all:
    make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules

Linux 在 x86 上使用-mgeneral-regs-only编译 kernel 代码,这会在使用 FP 或 SIMD 执行任何操作的函数中产生此错误。 (通过内联asm除外,因为编译器看不到 FP 指令,只有汇编器可以看到。)

据我了解,至少在本节中没有发生 FP 操作,因此我需要帮助找出可能导致此错误的原因。

GCC 在启用优化时优化整个功能,并且您function 内部使用 FP。 您正在使用宏进行 FP 乘法和截断转换为 integer 并将结果分配给int ,因为您最终提供的 MCVE 显示包含double *datastruct matrix

如果您停止编译器使用 FP 指令(例如 Linux 通过使用-mgeneral-regs-only构建),它会拒绝编译您的文件而不是执行软件浮点。

唯一奇怪的是,它将错误归结为 integer +=而不是编译为mulsdcvttsd2si的语句之一

如果禁用优化( -O0 -mgeneral-regs-only ),您将获得更明显的相同错误位置( https://godbolt.org/z/Tv5nG6nd4 ):

<source>: In function 'void matmul(matrix*, matrix*, matrix*)':
<source>:9:33: error: SSE register return with SSE disabled
    9 | #define DOUBLE_TO_FIXED(x) ((x) * (1 << scale))
      |                            ~~~~~^~~~~~~~~~~~~~~
<source>:46:21: note: in expansion of macro 'DOUBLE_TO_FIXED'
   46 |                 a = DOUBLE_TO_FIXED(A->data[i * A->rows + k]);
      |                     ^~~~~~~~~~~~~~~

If you really want to know what's going on with the GCC internals, you could dig into it with -fdump-tree-... options, eg on the Godbolt compiler explorer there's a dropdown for GCC Tree / RTL output that would let you look在各种分析器通过后,在函数逻辑的 GIMPLE 或 RTL 内部表示。


但是如果你只是想知道是否有办法让这个 function 工作,显然不是,除非你编译一个没有-mgeneral-registers-only的文件。 以这种方式编译的文件中的所有函数只能由在调用之前使用kernel_fpu_begin()的调用者调用。 (和kernel_fpu_end之后)。

您不能在 function 中安全地使用kernel_fpu_begin以允许它使用 SSE / x87 寄存器 在优化后调用 function 之前,它可能已经损坏了用户空间 FPU state。 出现此错误的症状不是故障,它正在破坏用户空间 state ,因此不要假设碰巧工作=正确。 此外,根据 GCC 的优化方式,代码生成可能适用于您的版本,但可能会因更早或更晚的 GCC 或 clang 版本而损坏。 我有点期望这个 function 顶部的kernel_fpu_begin()会在编译器对 FP 指令执行任何操作之前被调用,但这并不意味着它是安全和正确的。

另请参阅在包含 kernel_fpu_begin() 的文件上的 Linux Kernel 中生成和优化 FP/SIMD 代码?

显然-msse2会覆盖-mgeneral-regs-only ,因此这可能只是-mno-mmx -mno-sse的别名,并且任何选项都会禁用 x87。 因此,您可以在 function 上使用__attribute__((target("sse2")))而不更改其构建选项,但这将是 x86 特定的。 当然, -mgeneral-regs-only也是如此。 并且没有-mno-general-regs-only选项来覆盖内核的正常 CFLAGS。

如果您确实认为这里值得使用kernel_fpu_begin (而不是全程使用定点),我没有关于设置构建选项的最佳方式的具体建议。

显然,如果您确实保存/恢复 FPU state,您不妨将其用于循环,而不是使用 FP 转换为定点并返回。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM