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 clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
con la -O3
bandera, 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 (%rdi)
dos veces en mayúsculas int *a
y minúsculas int *b
.
Cuando explícitamente le decimos al compilador que estos dos punteros no pueden alias con la restrict
palabra clave:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Entonces Clang 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 pruebo con el siguiente código y lo compilo 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 a
y b
no puede 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 hay una posibilidad de que a
y b
podría alias, incluso en Rust seguro?
unsafe
código, no se permiten alias de referencias mutables y dan como resultado un comportamiento indefinido. Puede tener alias de punteros sin formato, pero elunsafe
código no le permite ignorar las reglas estándar de Rust. Es solo un error común y, por lo tanto, vale la pena señalarlo.+=
operaciones en el cuerpo deadds
pueden reinterpretarse como*a = *a + *b + *b
. Si los punteros no lo hacen alias, que pueden, incluso se puede ver lo que equivale ab* + *b
en la segunda lista asm:2: 01 c0 add %eax,%eax
. Pero si hacen un alias, no pueden, porque para cuando agregue*b
por segunda vez, contendrá un valor diferente al de la primera vez (el que almacena en línea4:
de la primera lista de asm).Respuestas:
Rust originalmente hizo permitirá LLVM de
noalias
atributo, pero este código miscompiled causado . 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, obtiene el ensamblado esperado:En pocas palabras, Rust puso el equivalente de la
restrict
palabra clave de C en todas partes , mucho más frecuente que cualquier programa habitual 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 conrestrict
tanta frecuencia como&mut
se usa en Rust.Esto ha sucedido varias veces .
noalias
habilitadonoalias
deshabilitadonoalias
habilitadonoalias
deshabilitadoProblemas relacionados con el óxido
Caso actual
Caso anterior
Otro
fuente
restrict
y compilan mal tanto en Clang como en GCC. No se limita a lenguajes que no son "lo suficientemente C ++", a menos que cuente C ++ en ese grupo .noalias
en cuenta completamente los punteros al ejecutar. Creó nuevos punteros basados en punteros de entrada, copiando incorrectamente elnoalias
atributo a pesar de que los nuevos punteros tenían un alias.