【问题标题】:Check to see if bit 31 is set in an unsigned int with gcc -O1 optimization使用 gcc -O1 优化检查是否在 unsigned int 中设置了第 31 位
【发布时间】:2021-09-06 13:48:06
【问题描述】:

我正在使用 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    $0x10,%esp
10312b73:   c7 45 fc 00 10 00 00    movl   $0x1000,-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    $0x80000000,%eax
10312b8a:   89 45 f4                mov    %eax,-0xc(%ebp)
10312b8d:   83 7d f4 00             cmpl   $0x0,-0xc(%ebp)
10312b91:   74 e7                   je     10312b7a <foo+0xd>
10312b93:   90                      nop
10312b94:   c9                      leave
10312b95:   c3                      ret

这个输出看起来不错,因为 and $0x80000000,%eax 将检查限制在第 31 位。

如果我将代码更改为检查位 30 而不是位 31 (busy = (reg_value &amp; 0x40000000U)),-O1 会产生正确的输出:

1030cea6:   a1 00 10 00 00          mov    0x1000,%eax
1030ceab:   a9 00 00 00 40          test   $0x40000000,%eax
1030ceb0:   74 f4                   je     1030cea6 <foo>
1030ceb2:   c3                      ret

我的猜测是这与签名有关,但是我的变量都是无符号的。

我的问题是如何使用-O1 生成正确的编译器输出(实际上将检查限制在第 31 位)?

【问题讨论】:

  • 为什么在使用 C 时标记 C++?他们不同的语言。另请注意,while (busy == 0); 是一个糟糕的检查方法,因为它称为轮询并且会消耗 100% 的 CPU
  • @phuclv fwiw,硬编码地址的使用表明这可能是低级 I/O 代码——在这种情况下,有时轮询是最快或唯一的选择。 (有时中断不存在,或者有时通过中断处理程序比等待 I/O 操作完成花费更多的周期)

标签: c assembly gcc x86 bit


【解决方案1】:

这是一个完全正确的优化。 test eax, eax 将 SF(符号标志)设置为 eax 的最高有效位;如果 SF = 0,jns 将跳转,因此该函数将在未设置 eax 的 MSB 时循环(这正是您想要的)。

【讨论】:

  • 我完全错过了这里的 SF 互动。这是完全有道理的。谢谢!
【解决方案2】:

所有汇编器输出都是正确的。 jns 如果未设置符号,则执行 goto。 test %eax,%eax; 85 c0test $0x80000000,%eax; a9 00 00 00 80 短 - 很好的编译器工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-08
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 2021-12-20
    • 2010-12-19
    • 2011-03-09
    相关资源
    最近更新 更多