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.