plotter compilador c gcc

compilador - gcc wikipedia



¿Por qué GCC 4.8.2 se queja de la adición bajo un desbordamiento estricto? (2)

Considere este código ( 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; }

Cuando se compila con advertencias estrictas (usando GCC 4.8.2 construido para un derivado Ubuntu 12.04):

$ 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

Estoy perplejo: ¿cómo se queja GCC por una adición? ¡No hay adiciones en esa línea (incluso cuando está preprocesado)! La sección relevante de la salida preprocesada es:

# 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; }

Claramente, puedo agregar -Wno-strict-overflow para suprimir esa advertencia, pero no entiendo por qué se considera que la advertencia se aplica a este código en primer lugar.

(Tengo en cuenta que el error se supone que está In function ''main'': pero eso se debe a que es capaz de insertar agresivamente el código de la función en main ).

Observaciones adicionales

Algunas observaciones desencadenadas por las respuestas:

  • El problema ocurre debido a la línea de entrada.
  • Eliminar la static no es suficiente para evitar el problema.
  • Compilando la función por separado de las obras main .
  • Agregar el __attribute__((noinline)) también funciona.
  • El uso de la optimización de -O2 evita el problema, también.

Pregunta subsidiaria

Esto me parece un comportamiento dudoso del compilador de GCC.

  • ¿Vale la pena informar al equipo de GCC como un posible error?

Salida del ensamblador

Mando:

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

Ensamblador (sección superior):

.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 ...


Está subrayando la función y luego generando el error. Puedes verlo por ti mismo:

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

En mi sistema, la versión original genera la misma advertencia, pero la versión noinline no.

GCC luego optimiza la salida hi >= lo , porque es realmente u+3 >= u , y genera una advertencia porque no es lo suficientemente bueno para darse cuenta de que u+3 no se desborda. Es una pena.

Documentación

De la documentación de GCC, sección 3.8:

Una optimización que asume que el desbordamiento firmado no ocurre es perfectamente seguro si los valores de las variables involucradas son tales que el desbordamiento nunca ocurre, de hecho, ocurre. Por lo tanto, esta advertencia puede dar un falso positivo: una advertencia sobre el código que no es realmente un problema. Para ayudar a enfocarse en asuntos importantes, se definen varios niveles de advertencia. No se emiten advertencias para el uso de desbordamiento firmado indefinido al estimar cuántas iteraciones requiere un bucle, en particular al determinar si se ejecutará un bucle.

Énfasis añadido. Mi recomendación personal es usar -Wno-error=strict-overflow , o usar un #pragma para desactivar la advertencia en el código ofensivo.


Mi suposición es que gcc está incorporando pick_bits y compilando con el conocimiento de que hi == lo+3 que le permite asumir que hi >= lo siempre es verdadero siempre que lo sea lo suficientemente bajo como para que lo+3 no se desborde.