使用 gcc -O1 优化检查 unsigned int 中是否设置了第 31 位
Check to see if bit 31 is set in an unsigned int with gcc -O1 optimization
我正在使用 gcc 为 32 位处理器编译 C 代码。它在 -O0
优化下工作正常,但是在 -O1
下(也尝试过 -Ofast
)它会产生不正确的输出。
void foo()
{
volatile unsigned int *reg = (volatile unsigned int *)0x1000;
unsigned int reg_value;
unsigned int busy;
do {
reg_value = *reg;
busy = (reg_value & 0x80000000U);
} while (busy == 0);
}
使用 -O1
编译器生成:
1030cea6 <foo>:
1030cea6: a1 00 10 00 00 mov 0x1000,%eax
1030ceab: 85 c0 test %eax,%eax
1030cead: 79 f7 jns 1030cea6 <foo>
1030ceaf: c3 ret
此输出的问题是 'test %eax,%eax' 检查所有 32 位,而不仅仅是位 31。
使用 -O0
编译器生成:
10312b6d: 55 push %ebp
10312b6e: 89 e5 mov %esp,%ebp
10312b70: 83 ec 10 sub [=12=]x10,%esp
10312b73: c7 45 fc 00 10 00 00 movl [=12=]x1000,-0x4(%ebp)
10312b7a: 8b 45 fc mov -0x4(%ebp),%eax
10312b7d: 8b 00 mov (%eax),%eax
10312b7f: 89 45 f8 mov %eax,-0x8(%ebp)
10312b82: 8b 45 f8 mov -0x8(%ebp),%eax
10312b85: 25 00 00 00 80 and [=12=]x80000000,%eax
10312b8a: 89 45 f4 mov %eax,-0xc(%ebp)
10312b8d: 83 7d f4 00 cmpl [=12=]x0,-0xc(%ebp)
10312b91: 74 e7 je 10312b7a <foo+0xd>
10312b93: 90 nop
10312b94: c9 leave
10312b95: c3 ret
这个输出看起来不错,因为 and [=19=]x80000000,%eax
将检查限制在第 31 位。
如果我更改代码以检查第 30 位而不是第 31 位 (busy = (reg_value & 0x40000000U)
),-O1
会产生正确的输出:
1030cea6: a1 00 10 00 00 mov 0x1000,%eax
1030ceab: a9 00 00 00 40 test [=13=]x40000000,%eax
1030ceb0: 74 f4 je 1030cea6 <foo>
1030ceb2: c3 ret
我猜这与签名有关,但是我的变量都是无符号的。
我的问题是如何使用 -O1
?
生成正确的编译器输出(实际上将检查限制为仅第 31 位)
这是一个完全正确的优化。 test eax, eax
会将 SF(符号标志)设置为 eax
的最高有效位;如果 SF = 0,jns
将跳转,因此函数将在未设置 eax
的 MSB 时循环(这正是您想要的)。
所有汇编器输出都是正确的。 jns
如果未设置符号则执行 goto。 test %eax,%eax; 85 c0
比 test [=12=]x80000000,%eax; a9 00 00 00 80
短 - 不错的编译器工作。
我正在使用 gcc 为 32 位处理器编译 C 代码。它在 -O0
优化下工作正常,但是在 -O1
下(也尝试过 -Ofast
)它会产生不正确的输出。
void foo()
{
volatile unsigned int *reg = (volatile unsigned int *)0x1000;
unsigned int reg_value;
unsigned int busy;
do {
reg_value = *reg;
busy = (reg_value & 0x80000000U);
} while (busy == 0);
}
使用 -O1
编译器生成:
1030cea6 <foo>:
1030cea6: a1 00 10 00 00 mov 0x1000,%eax
1030ceab: 85 c0 test %eax,%eax
1030cead: 79 f7 jns 1030cea6 <foo>
1030ceaf: c3 ret
此输出的问题是 'test %eax,%eax' 检查所有 32 位,而不仅仅是位 31。
使用 -O0
编译器生成:
10312b6d: 55 push %ebp
10312b6e: 89 e5 mov %esp,%ebp
10312b70: 83 ec 10 sub [=12=]x10,%esp
10312b73: c7 45 fc 00 10 00 00 movl [=12=]x1000,-0x4(%ebp)
10312b7a: 8b 45 fc mov -0x4(%ebp),%eax
10312b7d: 8b 00 mov (%eax),%eax
10312b7f: 89 45 f8 mov %eax,-0x8(%ebp)
10312b82: 8b 45 f8 mov -0x8(%ebp),%eax
10312b85: 25 00 00 00 80 and [=12=]x80000000,%eax
10312b8a: 89 45 f4 mov %eax,-0xc(%ebp)
10312b8d: 83 7d f4 00 cmpl [=12=]x0,-0xc(%ebp)
10312b91: 74 e7 je 10312b7a <foo+0xd>
10312b93: 90 nop
10312b94: c9 leave
10312b95: c3 ret
这个输出看起来不错,因为 and [=19=]x80000000,%eax
将检查限制在第 31 位。
如果我更改代码以检查第 30 位而不是第 31 位 (busy = (reg_value & 0x40000000U)
),-O1
会产生正确的输出:
1030cea6: a1 00 10 00 00 mov 0x1000,%eax
1030ceab: a9 00 00 00 40 test [=13=]x40000000,%eax
1030ceb0: 74 f4 je 1030cea6 <foo>
1030ceb2: c3 ret
我猜这与签名有关,但是我的变量都是无符号的。
我的问题是如何使用 -O1
?
这是一个完全正确的优化。 test eax, eax
会将 SF(符号标志)设置为 eax
的最高有效位;如果 SF = 0,jns
将跳转,因此函数将在未设置 eax
的 MSB 时循环(这正是您想要的)。
所有汇编器输出都是正确的。 jns
如果未设置符号则执行 goto。 test %eax,%eax; 85 c0
比 test [=12=]x80000000,%eax; a9 00 00 00 80
短 - 不错的编译器工作。