为什么GCC 4.8.2抱怨除了在严格溢出? [英] Why is GCC 4.8.2 complaining about addition under strict overflow?

查看:200
本文介绍了为什么GCC 4.8.2抱怨除了在严格溢出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个code( bits.c

 的#include<&ASSERT.H GT;
#包括LT&;&inttypes.h GT;
#包括LT&;&stdio.h中GT;静态uint64_t中pick_bits(无符号字符*字节,为size_t为nbytes,INT LO,INT喜)
{
  断言(字节= 0&放大器;!&放大器;为nbytes大于0&放大器;&放大器;为nbytes&下; = 8);
  断言(LO> = 0&放大器;&安培; LO< 64);
  断言(喜> = 0&放大器;&安培;喜< 64安培;&安培;喜> = LO);
  uint64_t中结果为0;
  的for(int i =的nbytes - 1; I> = 0;我 - )
    结果=(结果<< 8)|字节[I]
  结果>> = LO;
  结果&安培; =(UINT64_C(1) - ;≤(喜 - LO + 1)) - 1;
  返回结果;
}INT主要(无效)
{
  unsigned char型D1 [8] =\\ xA5 \\ XB4 \\ XC3 \\ XD2 \\ XE1 \\ XF0 \\ X96 \\的x87
  对于(INT U = 0; U< 64; U + = 4)
  {
    uint64_t中V = pick_bits(D1,的sizeof(D1),U,U + 3);
    的printf(采位%2D ..%2D给出了0x%PRIX64\\ n,U,U + 3,V);
  }
  返回0;
}

在严格的警告(使用GCC 4.8.2一个Ubuntu 12.04衍生建)编译:

  $ gcc的-g -O3 -std = C99 -Wall -Wextra -Wmissing的原型-Wstrict的原型\\
> -WOLD式高清-WOLD式声明-Werror bits.c -o位
1:0文件从bits.c包括:
bits.c:在函数'主':
bits.c:9:35:错误:假设签署溢出不会假定当(X + C)出现< X是始终为假[-Werror =严格的溢出]
   断言(喜> = 0&放大器;&安培;喜< 64安培;&安培;喜> = LO);
                                   ^
CC1:所有的警告被视为错误

我不解:GCC是如何抱怨加法?有没有增加在该行(即使preprocessed)!在preprocessed输出的相关部分是:

 #4bits.c2静态uint64_t中pick_bits(无符号字符*字节,为size_t为nbytes,INT LO,INT喜)
{
  ((字节= 0&放大器;&放大器;为nbytes大于0&放大器;&放大器;为nbytes&下; = 8)(无效)(0):!?!__assert_fail(字节= 0&放大器;&放大器;为nbytes大于0&放大器;&放大器;为nbytes&下; = 8,bits.c,7,__ preTTY_FUNCTION__));
  ((LO> = 0&放大器;&放大器; LO&下; 64)(无效)(0):__assert_fail(LO> = 0&放大器;&放大器; LO&下; 64,bits.c,8, __ preTTY_FUNCTION__));
  ((喜> = 0&放大器;&安培;喜< 64安培;&安培;喜> = LO)(无效)(0):__assert_fail(喜> = 0&放大器;&安培;喜< 64安培;&安培;喜> = LO,bits.c,9,__ preTTY_FUNCTION__));
  uint64_t中结果为0;
  的for(int i =的nbytes - 1; I> = 0;我 - )
    结果=(结果<< 8)|字节[I]
  结果>> = LO;
  结果&安培; =(1UL&L​​T;<(HI - LO + 1)) - 1;
  返回结果;
}

很显然,我可以添加 -Wno严溢共进晚餐preSS的警告,但我不明白为什么警告被认为是适用于这code摆在首位。

(我注意到,错误的是据说在函数'主':,但那是因为它能够积极地内联函数的code成


进一步观察

在回答引起了一些意见:


  • 出现问题的原因内联的。

  • 删除静态不足以避免该问题。

  • 从单独编译功能

  • 添加 __ __属性((noinline始终))工作过。

  • 使用 -O2 优化避免了这个问题了。

补充问题

这在我看来就像由GCC编译器可疑行为。


  • 是否值得海合会队作为一个可能的错误报告?


汇编输出

命令:

  $ gcc的-g -O3 -std = C99 -Wall -Wextra -Wmissing的原型-Wstrict的原型\\
> -WOLD式高清-WOLD式声明-Werror -S \\
> -Wno严格溢bits.c
$

汇编(顶部):

  .filebits.c
    。文本
.Ltext0:
    .section伪.rodata.str1.8,AMS,@ PROGBITS,1
    .align伪8
.LC0:
    .string采位%2D ..%2D给出了0x%LX \\ n
    .section伪.text.startup,斧头,@ PROGBITS
    .p2align 4日,15
    .globl主
    .TYPE为主,@function
主要:
.LFB8:
    .file 1bits.c
    1的.loc 19 0
    .cfi_startproc
.LVL0:
    pushq%RBP
    .cfi_def_cfa_offset 16
    .cfi_offset 6,-16
.LBB8:
.LBB9:
    1的.loc 23 0
    MOVL $ 3%EDX
.LBB10:
.LBB11:
    1的.loc 13 0
    movabsq $ -8676482779388332891,RBP%
.LBE11:
.LBE10:
.LBE9:
.LBE8:
    1的.loc 19 0
    pushq%RBX
    .cfi_def_cfa_offset 24
    .cfi_offset 3,-24
.LBB22:
    1的.loc 21 0
    xorl%EBX,EBX%
.LBE22:
    1的.loc 19 0
    SUBQ $ 8%RSP
    .cfi_def_cfa_offset 32
    JMP .L2
.LVL1:
    .p2align 4日,10
    .p2align 3
.L3:
    莱亚尔3(%RBX),EDX%
.LVL2:
.L2:
.LBB23:
.LBB20:
.LBB16:
.LBB12:
    1的.loc 13 0
    MOVL%EBX,ECX%
    MOVQ%RBP,RAX%
.LBE12:
.LBE16:
    1的.loc 24 0
    MOVL%EBX,ESI%
.LBB17:
.LBB13:
    1的.loc 13 0
    SHRQ%CL,RAX%
.LBE13:
.LBE17:
    1的.loc 24 0
    MOVL $ .LC0,EDI%
.LBE20:
    1的.loc 21 0
    ADDL $ 4%EBX
.LVL3:
.LBB21:
.LBB18:
.LBB14:
    1的.loc 13 0
    MOVQ%RAX,RCX%
.LBE14:
.LBE18:
    1的.loc 24 0
    xorl%EAX,EAX%
.LBB19:
.LBB15:
    1的.loc 14 0
    和L $ 15%ECX
.LBE15:
.LBE19:
    1的.loc 24 0
    调用printf
.LVL4:
.LBE21:
    1的.loc 21 0
    CMPL $ 64%EBX
    JNE .L3
.LBE23:
    1的.loc 27 0
    addq $ 8%RSP
    .cfi_def_cfa_offset 24
    xorl%EAX,EAX%
    popq%RBX
    .cfi_def_cfa_offset 16
.LVL5:
    popq%RBP
    .cfi_def_cfa_offset 8
    RET
    .cfi_endproc
.LFE8:
    .size为主,。,主
    。文本
...


解决方案

它内联函数,然后生成错误。你可以看到自己:

  __ __属性((noinline始终))
静态uint64_t中pick_bits(无符号字符*字节,为size_t为nbytes,INT LO,INT喜)

在我的系统,原来的版本生成相同的警告信息,但 noinline始终版本没有。

然后GCC优化了喜> = LO ,因为它是真正 U + 3> = U ,因为它不够好揣摩那个 U + 3 不溢出会产生警告。一个耻辱。

文档

从GCC文档,第3.8节:


  

这是假定有符号数溢出不会发生的优化是完全安全的,如果所涉及的变量的值是这样的溢出从不使用,其实发生。 因此这个警告可以很容易地给假阳性:约code,它实际上不是一个问题的警告为帮助聚焦在重要问题上,有几个警告级别的定义。估计循环多少次迭代需要,特别是确定是否一个循环将在所有被执行时,在没有发出警告的使用未定义的符号溢出的。


着重号。我个人的建议是使用 -Wno错误=严格的溢出,或者使用的#pragma 禁用在警告违规code。

Consider this code (bits.c):

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>

static uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi)
{
  assert(bytes != 0 && nbytes > 0 && nbytes <= 8);
  assert(lo >= 0 && lo < 64);
  assert(hi >= 0 && hi < 64 && hi >= lo);
  uint64_t result = 0;
  for (int i = nbytes - 1; i >= 0; i--)
    result = (result << 8) | bytes[i];
  result >>= lo;
  result &= (UINT64_C(1) << (hi - lo + 1)) - 1;
  return result;
}

int main(void)
{
  unsigned char d1[8] = "\xA5\xB4\xC3\xD2\xE1\xF0\x96\x87";
  for (int u = 0; u < 64; u += 4)
  {
    uint64_t v = pick_bits(d1, sizeof(d1), u, u+3);
    printf("Picking bits %2d..%2d gives 0x%" PRIX64 "\n", u, u+3, v);
  }
  return 0;
}

When compiled with stringent warnings (using GCC 4.8.2 built for an Ubuntu 12.04 derivative):

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition -Wold-style-declaration -Werror  bits.c -o bits
In file included from bits.c:1:0:
bits.c: In function ‘main’:
bits.c:9:35: error: assuming signed overflow does not occur when assuming that (X + c) < X is always false [-Werror=strict-overflow]
   assert(hi >= 0 && hi < 64 && hi >= lo);
                                   ^
cc1: all warnings being treated as errors

I'm puzzled: how is GCC complaining about an addition? There are no additions in that line (even when preprocessed)! The relevant section of the preprocessed output is:

# 4 "bits.c" 2

static uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi)
{
  ((bytes != 0 && nbytes > 0 && nbytes <= 8) ? (void) (0) : __assert_fail ("bytes != 0 && nbytes > 0 && nbytes <= 8", "bits.c", 7, __PRETTY_FUNCTION__));
  ((lo >= 0 && lo < 64) ? (void) (0) : __assert_fail ("lo >= 0 && lo < 64", "bits.c", 8, __PRETTY_FUNCTION__));
  ((hi >= 0 && hi < 64 && hi >= lo) ? (void) (0) : __assert_fail ("hi >= 0 && hi < 64 && hi >= lo", "bits.c", 9, __PRETTY_FUNCTION__));
  uint64_t result = 0;
  for (int i = nbytes - 1; i >= 0; i--)
    result = (result << 8) | bytes[i];
  result >>= lo;
  result &= (1UL << (hi - lo + 1)) - 1;
  return result;
}

Clearly, I can add -Wno-strict-overflow to suppress that warning, but I don't understand why the warning is considered to apply to this code in the first place.

(I note that the error is reputedly In function ‘main’:, but that is because it is able to aggressively inline the code of the function into main.)


Further observations

Some observations triggered by the answers:

  • The problem occurs because of the inlining.
  • Removing the static is not sufficient to avoid the problem.
  • Compiling the function separately from main works.
  • Adding the __attribute__((noinline)) works too.
  • Using -O2 optimization avoids the issue, too.

Subsidiary question

This looks to me like dubious behaviour by the GCC compiler.

  • Is it worth reporting to the GCC team as a possible bug?

Assembler output

Command:

$ gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Wold-style-declaration -Werror -S \
> -Wno-strict-overflow bits.c
$

Assembler (top section):

    .file   "bits.c"
    .text
.Ltext0:
    .section    .rodata.str1.8,"aMS",@progbits,1
    .align 8
.LC0:
    .string "Picking bits %2d..%2d gives 0x%lX\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB8:
    .file 1 "bits.c"
    .loc 1 19 0
    .cfi_startproc
.LVL0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
.LBB8:
.LBB9:
    .loc 1 23 0
    movl    $3, %edx
.LBB10:
.LBB11:
    .loc 1 13 0
    movabsq $-8676482779388332891, %rbp
.LBE11:
.LBE10:
.LBE9:
.LBE8:
    .loc 1 19 0
    pushq   %rbx
    .cfi_def_cfa_offset 24
    .cfi_offset 3, -24
.LBB22:
    .loc 1 21 0
    xorl    %ebx, %ebx
.LBE22:
    .loc 1 19 0
    subq    $8, %rsp
    .cfi_def_cfa_offset 32
    jmp .L2
.LVL1:
    .p2align 4,,10
    .p2align 3
.L3:
    leal    3(%rbx), %edx
.LVL2:
.L2:
.LBB23:
.LBB20:
.LBB16:
.LBB12:
    .loc 1 13 0
    movl    %ebx, %ecx
    movq    %rbp, %rax
.LBE12:
.LBE16:
    .loc 1 24 0
    movl    %ebx, %esi
.LBB17:
.LBB13:
    .loc 1 13 0
    shrq    %cl, %rax
.LBE13:
.LBE17:
    .loc 1 24 0
    movl    $.LC0, %edi
.LBE20:
    .loc 1 21 0
    addl    $4, %ebx
.LVL3:
.LBB21:
.LBB18:
.LBB14:
    .loc 1 13 0
    movq    %rax, %rcx
.LBE14:
.LBE18:
    .loc 1 24 0
    xorl    %eax, %eax
.LBB19:
.LBB15:
    .loc 1 14 0
    andl    $15, %ecx
.LBE15:
.LBE19:
    .loc 1 24 0
    call    printf
.LVL4:
.LBE21:
    .loc 1 21 0
    cmpl    $64, %ebx
    jne .L3
.LBE23:
    .loc 1 27 0
    addq    $8, %rsp
    .cfi_def_cfa_offset 24
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 16
.LVL5:
    popq    %rbp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE8:
    .size   main, .-main
    .text
...

解决方案

It's inlining the function, and then generating the error. You can see for yourself:

__attribute__((noinline))
static uint64_t pick_bits(unsigned char *bytes, size_t nbytes, int lo, int hi)

On my system, the original version generates the same warning, but the noinline version does not.

GCC then optimizes out hi >= lo, because it's really u+3 >= u, and generates a warning because it's not good enough at figuring out that u+3 doesn't overflow. A shame.

Documentation

From the GCC documentation, section 3.8:

An optimization that assumes that signed overflow does not occur is perfectly safe if the values of the variables involved are such that overflow never does, in fact, occur. Therefore this warning can easily give a false positive: a warning about code that is not actually a problem. To help focus on important issues, several warning levels are defined. No warnings are issued for the use of undefined signed overflow when estimating how many iterations a loop requires, in particular when determining whether a loop will be executed at all.

Emphasis added. My personal recommendation is to use -Wno-error=strict-overflow, or to use a #pragma to disable the warning in the offending code.

这篇关于为什么GCC 4.8.2抱怨除了在严格溢出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆