c - gcc -O 优化 : Help me understand the effect

标签 c gcc

因此,我正在学习 C,目前正在学习计算机系统:程序员的视角第 3 版和相关实验。 我现在正在做第一个实验,我必须为其实现(并因此实现)以下功能。

/* 
* fitsBits - return 1 if x can be represented as an 
*  n-bit, two's complement integer.
*   1 <= n <= 32
*   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
*   Legal ops: ! ~ & ^ | + << >>
*   Max ops: 15
*   Rating: 2
*/
int fitsBits(int x, int n) {
  int sign_bit = (x >> 31 & 1);
  int minus_one = ~1+1;
  int n_minus_one = n + minus_one;
  return  (!(x >> n_minus_one) & !sign_bit)
           | (!(~(x >> n_minus_one)) & sign_bit);
}

此函数将针对以下测试函数运行 _a_lot_ 测试用例。

int test_fitsBits(int x, int n)
{
  int TMin_n = -(1 << (n-1));
  int TMax_n = (1 << (n-1)) - 1;
  return x >= TMin_n && x <= TMax_n;
}

现在,奇怪的事情发生了:默认情况下,代码是使用以下标志编译的 -O -Wall -m32

针对测试函数运行我的代码会产生以下断言失败:错误:测试 fitsBits(-2147483648[0x80000000],32[0x20]) 失败... ...给出 1[0x1]。应该是 0[0x0]

看来我的代码是正确的,测试代码是伪造的。经过进一步调查,test_function 似乎产生了以下中间结果:

> Tmin:-2147483648 
> TMax_n:2147483647 
> x: -2147483648 
> x >= TMin_n: 1
> x <= TMax_n: 0
> result: 0

显然 -2147483648 <= 2147483647,但比较结果不知何故产生了 0。

如果我在没有 -O 标志的情况下编译这个程序,所有测试都会成功通过。 有人可以阐明这种行为吗?

编辑:抱歉,汇编代码布局很糟糕,不知道如何快速修复

Assembly Code without -O;

.section    __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl  _test_fitsBits
.align  4, 0x90
_test_fitsBits:                         ## @test_fitsBits
.cfi_startproc
## BB#0:
pushq   %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq    %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
xorl    %eax, %eax
movb    %al, %cl
movl    $1, %eax
xorl    %edx, %edx
movl    %edi, -4(%rbp)
movl    %esi, -8(%rbp)
movl    -8(%rbp), %esi
subl    $1, %esi
movb    %cl, -17(%rbp)          ## 1-byte Spill
movl    %esi, %ecx
                                    ## kill: CL<def> ECX<kill>
movl    %eax, %esi
shll    %cl, %esi
subl    %esi, %edx
movl    %edx, -12(%rbp)
movl    -8(%rbp), %edx
subl    $1, %edx
movl    %edx, %ecx
                                    ## kill: CL<def> ECX<kill>
shll    %cl, %eax
subl    $1, %eax
movl    %eax, -16(%rbp)
movl    -4(%rbp), %eax
cmpl    -12(%rbp), %eax
movb    -17(%rbp), %cl          ## 1-byte Reload
movb    %cl, -18(%rbp)          ## 1-byte Spill
jl  LBB0_2
## BB#1:
movl    -4(%rbp), %eax
cmpl    -16(%rbp), %eax
setle   %cl
movb    %cl, -18(%rbp)          ## 1-byte Spill
LBB0_2:
movb    -18(%rbp), %al          ## 1-byte Reload
andb    $1, %al
movzbl  %al, %eax
popq    %rbp
retq
.cfi_endproc

.globl  _main
.align  4, 0x90
_main:                                  ## @main
.cfi_startproc
## BB#0:
pushq   %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq    %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
xorl    %eax, %eax
movl    $0, -4(%rbp)
movl    %edi, -8(%rbp)
movq    %rsi, -16(%rbp)
popq    %rbp
retq
.cfi_endproc


.subsections_via_symbols

带-O的汇编代码:

.section    __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl  _test_fitsBits
.align  4, 0x90
_test_fitsBits:                         ## @test_fitsBits
.cfi_startproc
## BB#0:
pushq   %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq    %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
                                    ## kill: ESI<def> ESI<kill>       RSI<def>
leal    -1(%rsi), %ecx
movl    $1, %eax
                                    ## kill: CL<def> CL<kill> ECX<kill>
shll    %cl, %eax
movl    %eax, %ecx
negl    %ecx
cmpl    %edi, %eax
setg    %al
cmpl    %ecx, %edi
setge   %cl
andb    %al, %cl
movzbl  %cl, %eax
popq    %rbp
retq
.cfi_endproc

.globl  _main
.align  4, 0x90
_main:                                  ## @main
.cfi_startproc
## BB#0:
pushq   %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq    %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
xorl    %eax, %eax
popq    %rbp
retq
.cfi_endproc


.subsections_via_symbols

最佳答案

int TMin_n = -(1 << (n-1));
int TMax_n = (1 << (n-1)) - 1;

在具有 32 位 int 的系统上上面的两个移位表达式在 n 时调用未定义的行为是 32。当 int是 32 位然后 1 << 31 UB 在 C 中是 1 << 31int 中不可表示.

关于c - gcc -O 优化 : Help me understand the effect,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33880870/

相关文章:

gcc - 使用 Clang 进行基本的源到源转换

c++ - 不与共享库共享所有类

c - 如何链接到其中包含 main 的对象?

我可以将 Intel 语法的 x86 程序集与 GCC 一起使用吗?

c++ - 在 C++/G++ 中是否有*任何*方法来获取 C 样式数组的长度?

c - SDL双重免费或损坏错误小代码段

c - C中奇怪的整数比较

C - 创建和使用结构数组?错误 : expected identifier or '(' before '[' token

Objective-C/C 常量标量值声明

c - 是否可以使用 getopt_long 来解析类似于 C 程序中的命令行参数的字符串数组?