[英]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 *data
的struct matrix
。
如果您停止编译器使用 FP 指令(例如 Linux 通过使用-mgeneral-regs-only
构建),它会拒绝编译您的文件而不是执行软件浮点。
唯一奇怪的是,它将错误归结为 integer +=
而不是编译为mulsd
和cvttsd2si
的语句之一
如果禁用优化( -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.