rust compiler-optimization

¿Por qué el compilador Rust no optimiza el código suponiendo que dos referencias mutables no pueden tener alias?



compiler-optimization (1)

Rust originalmente noalias atributo noalias de LLVM, pero esto causó un código mal compilado . Cuando todas las versiones de LLVM compatibles ya no compilan mal el código, se volverá a habilitar .

Si agrega -Zmutable-noalias=yes a las opciones del compilador, obtendrá el ensamblado esperado:

adds: mov eax, dword ptr [rsi] add eax, eax add dword ptr [rdi], eax ret

En pocas palabras, Rust puso el equivalente de la palabra clave restrict de C restrict todas partes , mucho más frecuente que cualquier programa de C. Esto ejerció casos de esquina de LLVM más de lo que pudo manejar correctamente. Resulta que los programadores C y C ++ simplemente no usan restrict tanta frecuencia como &mut se usa en Rust.

Esto ha sucedido varias veces . Un historial no autorizado, basado en las etiquetas de los commits relevantes (que ignora cualquier backport que pueda haber ocurrido para las versiones de parche)

  • Rust 1.0 a 1.7 - noalias habilitadas
  • Rust 1.8 a 1.27 - noalias deshabilitado
  • Rust 1.28 a 1.30 - noalias habilitadas
  • Rust 1.31 a través de ??? - noalias desactivadas

Problemas relacionados con el óxido

Hasta donde yo sé, el alias de referencia / puntero puede dificultar la capacidad del compilador para generar código optimizado, ya que deben garantizar que el binario generado se comporte correctamente en el caso en que las dos referencias / punteros realmente alias. Por ejemplo, en el siguiente código C,

void adds(int *a, int *b) { *a += *b; *a += *b; }

cuando se compila con la clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) con el indicador -O3 , emite

0000000000000000 <adds>: 0: 8b 07 mov (%rdi),%eax 2: 03 06 add (%rsi),%eax 4: 89 07 mov %eax,(%rdi) # the first time 6: 03 06 add (%rsi),%eax 8: 89 07 mov %eax,(%rdi) # the second time a: c3 retq

Aquí el código se almacena nuevamente en (%rdi) dos veces en caso de alias int *a e int *b .

Cuando explícitamente le decimos al compilador que estos dos punteros no pueden alias con la palabra clave restrict :

void adds(int * restrict a, int * restrict b) { *a += *b; *a += *b; }

Luego, el sonido emitirá una versión más optimizada del código binario.

0000000000000000 <adds>: 0: 8b 06 mov (%rsi),%eax 2: 01 c0 add %eax,%eax 4: 01 07 add %eax,(%rdi) 6: c3 retq

Dado que Rust se asegura (excepto en un código inseguro) de que dos referencias mutables no pueden tener alias, creo que el compilador debería poder emitir la versión más optimizada del código.

Cuando rustc 1.35.0 con el código a continuación y lo rustc 1.35.0 con rustc 1.35.0 con -C opt-level=3 --emit obj

#![crate_type = "staticlib"] #[no_mangle] fn adds(a: &mut i32, b: &mut i32) { *a += *b; *a += *b; }

genera

0000000000000000 <adds>: 0: 8b 07 mov (%rdi),%eax 2: 03 06 add (%rsi),%eax 4: 89 07 mov %eax,(%rdi) 6: 03 06 add (%rsi),%eax 8: 89 07 mov %eax,(%rdi) a: c3 retq

Esto no aprovecha la garantía de que b no pueden tener alias.

¿Es esto porque el compilador Rust actual todavía está en desarrollo y aún no ha incorporado el análisis de alias para hacer la optimización?

¿Es esto porque todavía existe la posibilidad de que b puedan tener un alias, incluso en Rust seguro?