Rust: Vuelva a habilitar las anotaciones de noalias de forma predeterminada una vez que LLVM ya no las compila mal

Creado en 6 oct. 2018  ·  33Comentarios  ·  Fuente: rust-lang/rust

Este problema rastrea la eliminación del -Zmutable-alias=no predeterminado introducido en https://github.com/rust-lang/rust/pull/54639 debido a un error en LLVM. cc @nagisa

(¿ Deja vu? )

A-LLVM A-codegen C-tracking-issue I-slow T-compiler

Comentario más útil

Lo reduje a un simple caso de prueba C (compile en -O3 y -O0 y compare la salida):

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

__attribute__((always_inline))
static inline void copy(int *restrict a, int *restrict b) {
    assert(a != b);
    *b = *a;
    *a = 7;
}

__attribute__((noinline))
void floppy(int mat[static 2], size_t idxs[static 3]) {
    for (int i = 0; i < 3; i++) {
        copy(&mat[i%2], &mat[idxs[i]]);
    }
}

int main() {
    int mat[3] = {10, 20};
    size_t idxs[3] = {1, 0, 1};
    floppy(mat, idxs);
    printf("%d %d\n", mat[0], mat[1]);
}

Tenga en cuenta que si elimina restrict , el equivalente en C de noalias , el comportamiento es correcto. Sin embargo, incluso entonces, el assert(a != b) pasa, lo que demuestra que no puede ocurrir UB debido a llamarlo con restrict .

Lo que esta pasando es:

  1. copy () se inserta, lo que resulta en algo como:
for (int i = 0; i < 3; i++) {
    mat[idxs[i]] = mat[i%2]; mat[i%2] = 7;
}
  1. LLVM desenrolla el ciclo:
mat[idxs[0]] = mat[0]; mat[0] = 7; /* from copy(&mat[0%2], &mat[idxs[0]]) */
mat[idxs[1]] = mat[1]; mat[1] = 7; /* from copy(&mat[1%2], &mat[idxs[1]]) */
mat[idxs[2]] = mat[0]; mat[0] = 7; /* from copy(&mat[2%2], &mat[idxs[2]]) */
  1. LLVM piensa que mat[0] no puede alias con mat[idxs[1]] o mat[1] , ergo no se puede haber cambiado entre mat[0] = 7; y mat[idxs[2]] = mat[0]; , ergo es seguro para numeración de valor global para optimizar este último a mat[idxs[2]] = 7; .

Pero mat[0] hace alias con mat[idxs[1]] , porque idxs[1] == 0 . Y no prometimos que no lo haría, porque en la segunda iteración cuando &mat[idxs[1]] se pasa a copy , el otro argumento es &mat[1] . Entonces, ¿por qué LLVM cree que no puede?

Bueno, tiene que ver con la forma en que copy está insertado. El atributo de función noalias se convierte en !alias.scope y !noalias metadatos en las instrucciones de carga y almacenamiento, como:

  %8 = load i32, i32* %0, align 4, !tbaa !8, !alias.scope !10, !noalias !13
  store i32 %8, i32* %7, align 4, !tbaa !8, !alias.scope !13, !noalias !10
  store i32 7, i32* %0, align 4, !tbaa !8, !alias.scope !10, !noalias !13

Normalmente, si una función se inserta varias veces, cada copia obtiene sus propios ID únicos para alias.scope y noalias, lo que indica que cada llamada representa su propia relación de 'desigualdad' * entre el par de argumentos marcados noalias ( restrict a nivel C), que pueden tener valores diferentes para cada llamada.

Sin embargo, en este caso, primero la función se inserta en el bucle, luego el código en línea se duplica cuando se desenrolla el bucle, y esta duplicación no cambia los ID. Debido a esto, LLVM cree que ninguno de los a puede alias con ninguno de los b , lo cual es falso, porque a de la primera y tercera llamada alias con b de la segunda llamada (todos apuntando a &mat[0] ).

Sorprendentemente, GCC también compila mal esto, con una salida diferente. (clang y GCC en -O0 generan 7 10 ; clang en -O3 genera 7 7 ; GCC en -O3 genera 10 7 .) Uh, realmente espero no haberlo hecho arruinar algo y agregar UB después de todo, pero no veo cómo ...

* Es un poco más complicado que eso, pero en este caso, dado que copy no usa ninguna aritmética de puntero y escribe en ambos punteros, la desigualdad a != b es necesaria y suficiente para que una llamada no ser UB.

Todos 33 comentarios

Todavía estoy trabajando para resolver el problema subyacente. El boleto interesante es https://github.com/rust-lang/rust/issues/54462.

Usando la reproducción mínima de @nagisa :


Caso de prueba minimizado sin código inseguro (¡asegúrese de compilar con 1 unidad de codegen!):

fn linidx(row: usize, col: usize) -> usize {
    row * 1 + col * 3
}

fn swappy() -> [f32; 12] {
    let mut mat = [1.0f32, 5.0, 9.0, 2.0, 6.0, 10.0, 3.0, 7.0, 11.0, 4.0, 8.0, 12.0];

    for i in 0..2 {
        for j in i+1..3 {
            if mat[linidx(j, 3)] > mat[linidx(i, 3)] {
                    for k in 0..4 {
                            let (x, rest) = mat.split_at_mut(linidx(i, k) + 1);
                            let a = x.last_mut().unwrap();
                            let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
                            ::std::mem::swap(a, b);
                    }
            }
        }
    }

    mat
}

fn main() {
    let mat = swappy();
    assert_eq!([9.0, 5.0, 1.0, 10.0, 6.0, 2.0, 11.0, 7.0, 3.0, 12.0, 8.0, 4.0], mat);
}

Pude bisecar los pases de optimización de LLVM para encontrar el que causa el error.

La ejecución de este comando da como resultado un ejecutable funcional (reemplace bug.rs con el nombre del archivo en el que guardó la reproducción).

rustc -Z no-parallel-llvm -C codegen-units=1 -O -Z mutable-noalias=yes -C llvm-args=-opt-bisect-limit=2260 bug.rs

Mientras se ejecuta este comando, se produce un ejecutable roto (el `` assert_eq '' falla):

rustc -Z no-parallel-llvm -C codegen-units=1 -O -Z mutable-noalias=yes -C llvm-args=-opt-bisect-limit=2261 bug.rs

Salida bisecta LLVM

Para este archivo, la optimización 2261 corresponde a Global Value Numbering on function (_ZN3bug6swappy17hdcc51d0e284ea38bE)

Bisecar las revisiones de LLVM (usando llvmlab bisect) lo reduce a r305936-r305938, presumiblemente r305938:

[BasicAA] Utilice MayAlias ​​en lugar de PartialAlias ​​como respaldo.

Tenga en cuenta que este es un cambio bastante antiguo, de junio de 2017.

Editar: Mirando la descripción de la confirmación, parece probable que el error existiera antes de eso, pero fue enmascarado por BasicAA impidiendo que se ejecuten los pases de alias posteriores, que es lo que corrigió la confirmación. El caso implica verificar el alias entre un par de instrucciones getelementptr donde el compilador sabe que tienen la misma dirección base pero no conoce las compensaciones.

Edit2: Además, pasar -enable-scoped-noalias=false como una opción LLVM evita la compilación incorrecta. (Esto no es sorprendente ya que desactiva el manejo de noalias por completo, pero por si acaso ayuda ...)

Desde un vistazo al IR anterior a GVN, siento que la causa raíz aquí podría estar en el desenrollado del bucle, dependiendo de si mi comprensión de cómo funcionan las anotaciones de alias de LLVM es correcta.

Considere un código como

int *a, *b;
for (int i = 0; i < 4; i++) {
    a[i & 1] = b[i & 1];
}

donde a[i & 1] y b[i & 1] no tienen un alias dentro de una sola iteración , pero a y b en general pueden tener un alias.

En LLVM IR, esto sería algo como:

define void @test(i32* %addr1, i32* %addr2) {
start:
    br label %body

body:
    %i = phi i32 [ 0, %start ], [ %i2, %body ]
    %j = and i32 %i, 1
    %addr1i = getelementptr inbounds i32, i32* %addr1, i32 %j
    %addr2i = getelementptr inbounds i32, i32* %addr2, i32 %j

    %x = load i32, i32* %addr1i, !alias.scope !2
    store i32 %x, i32* %addr2i, !noalias !2

    %i2 = add i32 %i, 1
    %cmp = icmp slt i32 %i2, 4
    br i1 %cmp, label %body, label %end

end:
    ret void
}

!0 = !{!0}
!1 = !{!1, !0}
!2 = !{!1}

Si ejecutamos esto a través de -loop-unroll obtenemos:

define void @test(i32* %addr1, i32* %addr2) {
start:
  br label %body

body:                                             ; preds = %start
  %x = load i32, i32* %addr1, !alias.scope !0
  store i32 %x, i32* %addr2, !noalias !0
  %addr1i.1 = getelementptr inbounds i32, i32* %addr1, i32 1
  %addr2i.1 = getelementptr inbounds i32, i32* %addr2, i32 1
  %x.1 = load i32, i32* %addr1i.1, !alias.scope !0
  store i32 %x.1, i32* %addr2i.1, !noalias !0
  %x.2 = load i32, i32* %addr1, !alias.scope !0
  store i32 %x.2, i32* %addr2, !noalias !0
  %addr1i.3 = getelementptr inbounds i32, i32* %addr1, i32 1
  %addr2i.3 = getelementptr inbounds i32, i32* %addr2, i32 1
  %x.3 = load i32, i32* %addr1i.3, !alias.scope !0
  store i32 %x.3, i32* %addr2i.3, !noalias !0
  ret void
}

!0 = !{!1}
!1 = distinct !{!1, !2}
!2 = distinct !{!2}

Observe cómo las cuatro copias del bucle utilizan metadatos de alias en el mismo dominio de alias. En lugar de ser noalias dentro de una sola iteración, es noalias en toda la función.

Finalmente, -scoped-noalias -gvn nos da:

define void @test(i32* %addr1, i32* %addr2) {
start:
  %x = load i32, i32* %addr1, !alias.scope !0
  store i32 %x, i32* %addr2, !noalias !0
  %addr1i.1 = getelementptr inbounds i32, i32* %addr1, i32 1
  %addr2i.1 = getelementptr inbounds i32, i32* %addr2, i32 1
  %x.1 = load i32, i32* %addr1i.1, !alias.scope !0
  store i32 %x.1, i32* %addr2i.1, !noalias !0
  store i32 %x, i32* %addr2, !noalias !0
  store i32 %x.1, i32* %addr2i.1, !noalias !0
  ret void
}

!0 = !{!1}
!1 = distinct !{!1, !2}
!2 = distinct !{!2}

Y esto dará como resultado resultados incorrectos si a = b + 1 .

Es posible reproducir este problema desde C con el siguiente código:

#include "stdio.h"

void copy(int * restrict to, int * restrict from) {
    *to = *from;
}

void test(int *a, int *b) {
    for (int i = 0; i < 4; i++) {
        copy(&b[i & 1], &a[i & 1]);
    }
}

int main() {
    int ary[] = {0, 1, 2};
    test(&ary[1], &ary[0]);
    printf("%d %d %d\n", ary[0], ary[1], ary[2]);
    return 1;
}

Con Clang 6.0 esto imprime 2 2 2 a -O0 y 1 2 2 a -O3 . No estoy seguro de si este código es legal bajo la semántica restrict en C, pero creo que debería ser legal bajo la semántica noalias más estricta de LLVM.

Lo reduje a un simple caso de prueba C (compile en -O3 y -O0 y compare la salida):

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

__attribute__((always_inline))
static inline void copy(int *restrict a, int *restrict b) {
    assert(a != b);
    *b = *a;
    *a = 7;
}

__attribute__((noinline))
void floppy(int mat[static 2], size_t idxs[static 3]) {
    for (int i = 0; i < 3; i++) {
        copy(&mat[i%2], &mat[idxs[i]]);
    }
}

int main() {
    int mat[3] = {10, 20};
    size_t idxs[3] = {1, 0, 1};
    floppy(mat, idxs);
    printf("%d %d\n", mat[0], mat[1]);
}

Tenga en cuenta que si elimina restrict , el equivalente en C de noalias , el comportamiento es correcto. Sin embargo, incluso entonces, el assert(a != b) pasa, lo que demuestra que no puede ocurrir UB debido a llamarlo con restrict .

Lo que esta pasando es:

  1. copy () se inserta, lo que resulta en algo como:
for (int i = 0; i < 3; i++) {
    mat[idxs[i]] = mat[i%2]; mat[i%2] = 7;
}
  1. LLVM desenrolla el ciclo:
mat[idxs[0]] = mat[0]; mat[0] = 7; /* from copy(&mat[0%2], &mat[idxs[0]]) */
mat[idxs[1]] = mat[1]; mat[1] = 7; /* from copy(&mat[1%2], &mat[idxs[1]]) */
mat[idxs[2]] = mat[0]; mat[0] = 7; /* from copy(&mat[2%2], &mat[idxs[2]]) */
  1. LLVM piensa que mat[0] no puede alias con mat[idxs[1]] o mat[1] , ergo no se puede haber cambiado entre mat[0] = 7; y mat[idxs[2]] = mat[0]; , ergo es seguro para numeración de valor global para optimizar este último a mat[idxs[2]] = 7; .

Pero mat[0] hace alias con mat[idxs[1]] , porque idxs[1] == 0 . Y no prometimos que no lo haría, porque en la segunda iteración cuando &mat[idxs[1]] se pasa a copy , el otro argumento es &mat[1] . Entonces, ¿por qué LLVM cree que no puede?

Bueno, tiene que ver con la forma en que copy está insertado. El atributo de función noalias se convierte en !alias.scope y !noalias metadatos en las instrucciones de carga y almacenamiento, como:

  %8 = load i32, i32* %0, align 4, !tbaa !8, !alias.scope !10, !noalias !13
  store i32 %8, i32* %7, align 4, !tbaa !8, !alias.scope !13, !noalias !10
  store i32 7, i32* %0, align 4, !tbaa !8, !alias.scope !10, !noalias !13

Normalmente, si una función se inserta varias veces, cada copia obtiene sus propios ID únicos para alias.scope y noalias, lo que indica que cada llamada representa su propia relación de 'desigualdad' * entre el par de argumentos marcados noalias ( restrict a nivel C), que pueden tener valores diferentes para cada llamada.

Sin embargo, en este caso, primero la función se inserta en el bucle, luego el código en línea se duplica cuando se desenrolla el bucle, y esta duplicación no cambia los ID. Debido a esto, LLVM cree que ninguno de los a puede alias con ninguno de los b , lo cual es falso, porque a de la primera y tercera llamada alias con b de la segunda llamada (todos apuntando a &mat[0] ).

Sorprendentemente, GCC también compila mal esto, con una salida diferente. (clang y GCC en -O0 generan 7 10 ; clang en -O3 genera 7 7 ; GCC en -O3 genera 10 7 .) Uh, realmente espero no haberlo hecho arruinar algo y agregar UB después de todo, pero no veo cómo ...

* Es un poco más complicado que eso, pero en este caso, dado que copy no usa ninguna aritmética de puntero y escribe en ambos punteros, la desigualdad a != b es necesaria y suficiente para que una llamada no ser UB.

Je, parece que corrí con

Ese es un gran momento ^^ Llegamos a la misma conclusión con casi el mismo caso de prueba reducido al mismo tiempo :)

Para solucionar esto, probablemente algo como https://github.com/llvm-mirror/llvm/blob/54d4881c352796b18bfe7314662a294754e3a752/lib/Transforms/Utils/InlineFunction.cpp#L801 también debe realizarse en LoopUnrollPass.

Envié un informe de error de LLVM para este problema en https://bugs.llvm.org/show_bug.cgi?id=39282.

Y, solo mencionando esto para completar, envié un informe de error a GCC ya que también compiló mal mi caso de prueba C: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87609

Triage: si estoy leyendo esto correctamente, se aceptó la corrección de LLVM (https://reviews.llvm.org/D9375). No estoy seguro de lo que eso significa para realmente fusionarse con LLVM, o cuándo sucedió eso; alguien más conocedor del proceso de revisión de LLVM debería verificar si el problema está solucionado ahora (y para qué versiones).

No está fusionado, el proceso de revisión fue un poco extraño y la persona que lo aprobó ya no revisa los parches.

Se ha pedido que se realicen pruebas con el conjunto de parches de "restricción total". Probablemente sería valioso si alguien intentara volver a habilitar noalias en Rust sobre un llvm que tiene este conjunto de parches aplicado.

Estoy dispuesto a intentar hacer eso.
Tengo acceso a servidores moderadamente potentes (48 núcleos HT, 128G de RAM) y creo que puedo construir todo correctamente con el parche.
Una vez que tenga una cadena de herramientas que funcione, ¿qué cajas recomendaría probar?

Terminé de fusionar todas las confirmaciones específicas de rust en el master upstream de llvm y luego apliqué el parche .
Aquí está la rama resultante: https://github.com/PaulGrandperrin/llvm-project/tree/llvm-master-with-rustlang-patches-and-D69542
Ahora intentaré compilar la cadena de herramientas y probarla.

Primero asegúrese de revertir: https://github.com/rust-lang/rust/pull/54639 para que Rust realmente emita noalias.

Una buena primera cosa que puedes probar es algo como:

pub fn adds(a: &mut i32, b: &mut i32) {
    *a += *b;
    *a += *b;
}

y confirme que se compila en algo como:

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

y no

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

A continuación, asegúrese de que el código de https://github.com/rust-lang/rust/issues/54462#issue -362850708 ya no se compile incorrectamente.

Tenga en cuenta que @jrmuizel # 54639 Qué incluyen @nagisa 's reproductor mínima para # 54462 como una nueva prueba compilador. ¿Quizás una reversión completa no está en orden?

No creo que sea importante incluirlo o no porque AFAIK, solo se trata de cambiar alguna marca predeterminada (que se puede anular con -Zmutable-noalias=yes ) y puedo compilar manualmente el archivo de prueba mencionado.

Para que lo sepas, todavía estoy tratando de compilar LLVM, pero a partir de ahora recibo un error de compilación con o sin los parches específicos de rust aplicados (es decir, solo el maestro llvm de upstream + el parche también falla):

In file included from /usr/include/c++/8/cmath:45,
                 from /opt/rust/src/llvm-project/llvm/include/llvm-c/DataTypes.h:28,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/Support/DataTypes.h:16,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/ADT/Hashing.h:47,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/ADT/ArrayRef.h:12,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/Transforms/Utils/NoAliasUtils.h:16,
                 from /opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp:13:
/opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp: In function ‘void llvm::cloneNoAliasScopes(llvm::ArrayRef<llvm::MetadataAsValue*>, llvm::DenseMap<llvm::MDN
ode*, llvm::MDNode*>&, llvm::DenseMap<llvm::MetadataAsValue*, llvm::MetadataAsValue*>&, llvm::StringRef, llvm::LLVMContext&)’:
/opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp:174:30: error: no matching function for call to ‘llvm::AliasScopeNode::AliasScopeNode(double)’
         llvm::AliasScopeNode SNAN(MD);
                              ^~~~
In file included from /opt/rust/src/llvm-project/llvm/include/llvm/IR/TrackingMDRef.h:16,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/IR/DebugLoc.h:17,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/IR/Instruction.h:21,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/IR/BasicBlock.h:22,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/IR/Instructions.h:27,
                 from /opt/rust/src/llvm-project/llvm/include/llvm/Transforms/Utils/NoAliasUtils.h:22,
                 from /opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp:13:
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1446:12: note: candidate: ‘llvm::AliasScopeNode::AliasScopeNode(const llvm::MDNode*)’
   explicit AliasScopeNode(const MDNode *N) : Node(N) {}
            ^~~~~~~~~~~~~~
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1446:12: note:   no known conversion for argument 1 from ‘double’ to ‘const llvm::MDNode*’
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1445:3: note: candidate: ‘constexpr llvm::AliasScopeNode::AliasScopeNode()’
   AliasScopeNode() = default;
   ^~~~~~~~~~~~~~
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1445:3: note:   candidate expects 0 arguments, 1 provided
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1441:7: note: candidate: ‘constexpr llvm::AliasScopeNode::AliasScopeNode(const llvm::AliasScopeNode&)’
 class AliasScopeNode {
       ^~~~~~~~~~~~~~
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1441:7: note:   no known conversion for argument 1 from ‘double’ to ‘const llvm::AliasScopeNode&’
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1441:7: note: candidate: ‘constexpr llvm::AliasScopeNode::AliasScopeNode(llvm::AliasScopeNode&&)’
/opt/rust/src/llvm-project/llvm/include/llvm/IR/Metadata.h:1441:7: note:   no known conversion for argument 1 from ‘double’ to ‘llvm::AliasScopeNode&&’
/opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp:177:31: error: request for member ‘getName’ in ‘__builtin_nans(((const char*)""))’, which is of non-class ty
pe ‘double’
         auto ScopeName = SNAN.getName();
                               ^~~~~~~
/opt/rust/src/llvm-project/llvm/lib/Transforms/Utils/NoAliasUtils.cpp:187:39: error: request for member ‘getDomain’ in ‘__builtin_nans(((const char*)""))’, which is of non-class
type ‘double’
             const_cast<MDNode *>(SNAN.getDomain()), Name);
                                       ^~~~~~~~~
[ 75%] Building CXX object lib/Target/Hexagon/CMakeFiles/LLVMHexagonCodeGen.dir/RDFCopy.cpp.o
make[2]: *** [lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/build.make:635: lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/NoAliasUtils.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....

¿Quizás el maestro LLVM ya no está sincronizado con el parche (lo que podría suceder considerando qué tan grande es el parche y qué tan rápido se mueve el maestro LLVM) y necesita compilar contra una revisión anterior del maestro LLVM? Definitivamente valdría la pena hacer ping al autor del parche sobre esto.

Eso es exactamente lo que estoy haciendo, tratando de encontrar una revisión anterior que se compile con el parche :-)

Ahora estoy seguro de que el problema no es con llvm / master sino con el parche.
La confirmación más antigua de llvm / master que todavía es compatible con el parche es https://github.com/llvm/llvm-project/commit/5b99c189b3bfc0faa157f7ca39652c0bb8c315a7 pero incluso tan atrás el parche no se compila.
Estoy demasiado cansado y perezoso para intentar entender C ++ ahora, lo intentaré de nuevo mañana.
Mientras tanto, ¿alguien puede ponerse en contacto con el autor del parche para pedir ayuda?

No creo que pueda usar fácilmente el LLVM maestro con Rust (después de construirlo) sin parchear rustllvm . AFAIK, solo admite versiones 6-9 en este momento.

@ mati865 Primero intenté aplicar el parche en la bifurcación llvm-9 de rust, pero tampoco fue trivial ...

El parche aparentemente está basado en la parte superior de llvm / llvm-project @ 82d3ba87d06f9e2abc6e27d8799587d433c56630. ¿Se adapta a ti si lo solicitas además?

@jrmuizel gracias. ¡Lo intentaré!
Mientras tanto, he podido adaptar con éxito rustllvm para compilar con llvm master.

Haga ping a

https://reviews.llvm.org/D68484

Hablando de manera realista, ¿cuál es la línea de tiempo estimada y las posibilidades generales de que este parche se fusione, considerando su tamaño?

@MSxDOS Le gustaría preguntar a los desarrolladores de LLVM. En general, esperaría que el tamaño de un parche importe menos que el deseo de los propietarios de verlo fusionado, por lo que la pregunta es cuánto quiere LLVM que aterrice.

Aquí está el último estado que he visto: https://reviews.llvm.org/D69542#1836439

Supongo que en algún momento dejará de ser relevante si https://github.com/bytecodealliance/cranelift~~ https://github.com/bjorn3/rustc_codegen_cranelift funciona.

@leeoniya , no hay un plan a corto plazo para usar cranelift para las compilaciones opcionales. cranelift no tiene mucho trabajo de optimización que sería necesario para hacerlo posible.

Me sorprendió descubrir cuán conservador es el compilador con respecto a suponer que podría haber alias sin esta opción. Por ejemplo:

fn baz(s: &mut S) {
    if s.y < 10 {
        s.x = foo();
    }

    if s.y < 5 {
        s.x = foo();
    }
}

Debido a que accede a los miembros de la estructura a través de un &mut , asume que s.x y s.y pueden alias, por lo que requiere dos accesos a la memoria en lugar de uno para s.y . Eso es realmente desafortunado, cuando se considera cuántas veces las lecturas / escrituras de miembros a través de &mut deben estar intercaladas en un programa típico.

Editar: según algunas pruebas, esto no parece afectar a todas esas lecturas / escrituras, lo cual no es sorprendente, porque probablemente mataría el rendimiento si lo hiciera. Aún así, el uso de -Z mutable-noalias corrige el doble acceso a la memoria en el ejemplo anterior, por lo que algunos casos pueden romperse.

@PaulGrandperrin hay una nueva versión de este parche en https://reviews.llvm.org/D69542 basada en llvm @ 9fb46a452d4e5666828c95610ceac8dcd9e4ce16. ¿Estás dispuesto a intentar que vuelva a funcionar?

¿Fue útil esta página
0 / 5 - 0 calificaciones