Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

182
Views
Optimizaciones no conformes de volátiles en gcc 11.1

Mientras escribía una respuesta sobre cómo los compiladores deben tratar volatile , creo que me he topado con un error de gcc y me gustaría que alguien lo verifique antes de informarlo.

Escribí una función simple como esta:

 int foo (int a, int b, int c) { b = a + 1; c = b + 1; a = c + 1; return a; }

Sin optimizaciones, esto da como resultado una gran cantidad de movimientos de datos sin sentido de un lado a otro. Con las optimizaciones, el compilador simplemente toma el registro donde se almacenó a , luego agrega 3 y devuelve ese resultado. Hablar x86 lea eax, [rdi+3] y ret . Esto es lo esperado, hasta ahora todo bien.

Para demostrar la secuenciación y el acceso volátil, cambié el ejemplo a esto:

 int foo (int a, int b, int c) { b = a + 1; c = *(volatile int*)&b + 1; a = c + 1; return a; }

Aquí hay un acceso lvalue del contenido de b que está calificado como volátil y, por lo que puedo decir, el compilador no tiene permitido optimizar ese acceso 1) . Desde gcc 4.1.2 (y probablemente antes) hasta gcc 10.3 obtengo un comportamiento conforme (lo mismo en clang). El código de máquina x86 se ve así incluso con -O3 :

 foo: add edi, 1 mov DWORD PTR [rsp-4], edi mov eax, DWORD PTR [rsp-4] add eax, 2 ret

Luego intento lo mismo en gcc 11.1 y más allá, ahora obtengo:

 foo: lea eax, [rdi+3] ret

https://godbolt.org/z/e5x74z3Kb

ARM gcc 11.1 hace algo similar.

¿Es esto un error del compilador?


1) Referencias: ISO/IEC 9899:2018 5.1.2.3, particularmente §2, §4 y §6.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

Pasar la dirección a una función no en línea hace que GCC respete las conversiones volatile para accesos posteriores (y tal vez anteriores, no verificados) a una función arg o local. https://godbolt.org/z/cssveev7n

Dupliqué la línea c = y el asm contiene dos cargas de b gracias al volatile cast, usando GCC trunk.

 void bar(void*); int foo (int a, int b, int c) { bar(&b); // b's address has now "escaped" - potentially globally visible b = a + 1; c = *(volatile int*)&b + 1; c = *(volatile int*)&b + 1; // both accesses present. a = c + 1; return a; }
 # GCC trunk -O3 -fverbose-asm call bar # mov DWORD PTR [rsp+12], ebx # b, tmp89 mov eax, DWORD PTR [rsp+12] # _2, MEM[(volatile int *)&b] mov eax, DWORD PTR [rsp+12] # _3, MEM[(volatile int *)&b] ... add eax, 2 ret

Entonces, esto parece inocente, excepto tal vez en algunos casos de uso de microbenchmark; no va a romper atómicos enrollados a mano usando moldes como estos, como las macros READ_ONCE / WRITE_ONCE del kernel de Linux .

Todavía podría decirse que viola las reglas ISO C , si es legal alias un int simple con un volatile int . Si no, es solo el comportamiento de definición de GCC, por lo que depende de GCC. Publico esto más como un punto de datos que como un argumento en cualquier dirección sobre ese aspecto de la pregunta.

over 4 years ago · Santiago Trujillo Report

0

Según C18 5.1.2.3/6, los accesos a objetos volátiles (estrictamente de acuerdo con las reglas de la máquina abstracta) son parte del comportamiento observable del programa, que todas las implementaciones conformes deben reproducir. El término "acceso" en este contexto incluye lecturas y escrituras.

C18 5.1.2.3/2 y /4 refuerzan que los accesos volátiles son efectos secundarios necesarios , excluidos de la regla de que las implementaciones están permitidas para evitar producir efectos secundarios innecesarios.

Lo único que veo para GCC sería un argumento de que aunque (volatile int*)&b es un lvalue con un tipo calificado como volatile , puede probar que el objeto que designa ( b ) no es en realidad un "objeto volátil", que de hecho no lo es si vas por su declaración. Y eso es consistente con el comportamiento observado de GCC 11.2 para esta versión de la función:

 int foo (int a, int b, int c) { volatile int bv = a + 1; c = bv + 1; a = c + 1; return a; }

, que produce el mismo ensamblado que las versiones anteriores de GCC para el código original ( godbolt ).

No está claro si esto constituye un error en el sentido de no conformidad con el estándar del lenguaje, pero ciertamente GCC está frustrando la aparente intención del programador.

over 4 years ago · Santiago Trujillo Report

0

Escuché a un equipo de compiladores argumentar de manera convincente (bueno, casi me quedo dormido, así que obtuve un esquema aproximado) que fuera de un objeto del tamaño de una palabra con alcance externo, volátil era una decoración sin sentido. Además, el compilador proporcionó algún tipo de comportamiento tradicional en torno a objetos atribuidos sin sentido como una conveniencia para las personas que trabajan con código heredado. Esta interpretación se basó en una reducción absurda del estándar C que es mejor que correcto, es técnicamente correcto , el estándar de oro de los alpha-geeks.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!