De vez en cuando, el código C ++ no funcionará cuando se compila con algún nivel de optimización. Puede ser que el compilador haga una optimización que rompa el código o puede ser un código que contenga un comportamiento indefinido que permita al compilador hacer lo que sienta.
Supongamos que tengo un fragmento de código que se rompe cuando se compila solo con un nivel de optimización más alto. ¿Cómo sé si es el código o el compilador y qué hago si es el compilador?
optimization
compiler
diente filoso
fuente
fuente
Respuestas:
Diría que es una apuesta segura que, en la gran mayoría de los casos, es su código, no el compilador, el que está roto. E incluso en el caso extraordinario cuando se trata del compilador, probablemente esté utilizando alguna característica de lenguaje oscuro de una manera inusual, para la cual el compilador específico no está preparado; en otras palabras, lo más probable es que cambie su código para que sea más idiomático y evite el punto débil del compilador.
En cualquier caso, si puede demostrar que encontró un error del compilador (según las especificaciones del idioma), informe a los desarrolladores del compilador, para que puedan solucionarlo en algún momento.
fuente
Igual que de costumbre, como con cualquier otro error: realiza un experimento controlado. Limite el área sospechosa, desactive las optimizaciones para todo lo demás y comience a variar las optimizaciones aplicadas a ese fragmento de código. Una vez que obtenga una reproducibilidad del 100%, comience a variar su código e introduzca cosas que podrían romper ciertas optimizaciones (por ejemplo, introducir un posible alias de puntero, insertar llamadas externas con posibles efectos secundarios, etc.). Mirar el código de ensamblaje en un depurador también podría ayudar.
fuente
Examine el código de ensamblaje resultante y vea si hace lo que su fuente está pidiendo. Recuerde que las probabilidades son muy altas de que realmente es su código el culpable de una manera no obvia.
fuente
En más de 30 años de programación, el número de errores genuinos del compilador (generación de código) que he encontrado sigue siendo solo ~ 10. El número de errores propios (y de otras personas) que he encontrado y corregido en el mismo período es probablemente > 10,000. Mi "regla de oro" es que la probabilidad de que un error determinado se deba al compilador es <0.001.
fuente
Comencé a escribir un comentario y luego decidí que era demasiado largo y demasiado al grano.
Yo diría que es su código el que está roto. En el caso poco probable de que haya descubierto un error en el compilador, debe informarlo a los desarrolladores del compilador, pero ahí es donde termina la diferencia.
La solución es identificar la construcción ofensiva y refactorizarla para que haga la misma lógica de manera diferente. Eso probablemente resolvería el problema, ya sea que el error esté de su lado o en el compilador.
fuente
fuente
int + int
desbordamiento exactamente como si se compilara en una instrucción ADD de hardware. Funcionó bien cuando se compiló con una versión anterior de GCC, pero no cuando se compiló con el compilador más nuevo. Aparentemente, la buena gente de GCC decidió que, dado que el resultado de un desbordamiento de enteros no está definido, su optimizador podría funcionar bajo el supuesto de que nunca sucede. Optimizó una rama importante directamente del código.Si desea saber si es su código o el compilador, debe conocer perfectamente la especificación de C ++.
Si la duda persiste, debe conocer perfectamente el ensamblaje x86.
Si no está de humor para aprender ambos a la perfección, entonces es casi seguro que su compilador resuelve de manera diferente dependiendo del nivel de optimización.
fuente
Obtener un error de compilación en el código estándar o un error de compilación interno es más probable que los optimizadores estén equivocados. Pero he oído hablar de compiladores que optimizan los bucles incorrectamente olvidando algunos efectos secundarios que causa un método.
No tengo sugerencias sobre cómo saber si eres tú o el compilador. Puedes probar con otro compilador.
Un día me preguntaba si era mi código o no y alguien me sugirió valgrind. Pasé los 5 o 10 minutos para ejecutar mi programa con él (creo
valgrind --leak-check=yes myprog arg1 arg2
que lo hice pero jugué con otras opciones) e inmediatamente me mostró UNA línea que se ejecuta en un caso específico, que era el problema. Luego, mi aplicación funcionó sin problemas desde entonces sin fallas, errores o comportamientos extraños. valgrind u otra herramienta como esta es una buena manera de saber si es su código.Nota al margen: una vez me pregunté por qué el rendimiento de mi aplicación apestaba. Resultó que todos mis problemas de rendimiento también estaban en una línea. Yo escribi
for(int i=0; i<strlen(sz); ++i) {
. El sz fue unos pocos mb. Por alguna razón, el compilador se ejecutaba cada vez, incluso después de la optimización. Una línea puede ser un gran problema. De actuaciones a choquesfuente
Una situación cada vez más común es que los compiladores rompen el código escrito para dialectos de C que soporta comportamientos no obligatorios por el Estándar, y permite que el código dirigido a esos dialectos sea más eficiente de lo que podría ser un código estrictamente conforme. En tal caso, sería injusto describir como código "roto" que sería 100% confiable en los compiladores que implementaron el dialecto de destino, o describir como "roto" el compilador que procesa un dialecto que no admite la semántica requerida . En cambio, los problemas surgen simplemente del hecho de que el lenguaje procesado por los compiladores modernos con optimizaciones habilitadas está divergiendo de los dialectos que solían ser populares (y todavía son procesados por muchos compiladores con optimizaciones deshabilitadas, o por algunos incluso con optimizaciones habilitadas).
Por ejemplo, se escribe una gran cantidad de código para dialectos que reconocen como legítimos una serie de patrones de alias de puntero no obligatorios por la interpretación de gcc del Estándar, y hace uso de dichos patrones para permitir que una traducción directa del código sea más legible y eficiente de lo que sería posible bajo la interpretación de gcc del Estándar C. Tal código puede no ser compatible con gcc, pero eso no implica que esté roto. Simplemente se basa en extensiones que gcc solo admite con optimizaciones deshabilitadas.
fuente
Aísle el punto problemático y compare el comportamiento observado con lo que debería suceder de acuerdo con las especificaciones del lenguaje. Definitivamente no es fácil, pero eso es lo que tienes que hacer para saber (y no solo asumir ).
Probablemente no sería tan meticuloso. Más bien, le pediría al foro de soporte del compilador / lista de correo. Si realmente es un error en el compilador, entonces podrían solucionarlo. Probablemente sería mi código de todos modos. Por ejemplo, las especificaciones de idioma con respecto a la visibilidad de la memoria en subprocesos pueden ser bastante intuitivas, y pueden ser evidentes solo cuando se usan algunos indicadores de optimización específicos, en algún hardware específico (!). La especificación podría definir un comportamiento indefinido, por lo que podría funcionar con algún compilador / algunos indicadores, y no funcionaría con otro, etc.
fuente
Lo más probable es que su código tenga un comportamiento indefinido (como explicaron otros, es mucho más probable que tenga errores en su código que en el compilador, incluso si los compiladores de C ++ son tan complejos que sí tienen errores; incluso la especificación de C ++ tiene errores de diseño) . Y UB puede estar aquí incluso si el ejecutable compilado funciona (por mala suerte).
Por lo tanto, debe leer el blog de Lattner Lo que todo programador de C debe saber sobre el comportamiento indefinido (la mayoría se aplica también a C ++ 11).
La herramienta valgrind y las
-fsanitize=
opciones de instrumentación recientes para GCC (o Clang / LLVM ) también deberían ser útiles. Y, por supuesto, habilite todas las advertencias:g++ -Wall -Wextra
fuente