Rust: Повторно включить аннотации noalias по умолчанию, как только LLVM больше не будет их неправильно компилировать

Созданный на 6 окт. 2018  ·  33Комментарии  ·  Источник: rust-lang/rust

Эта проблема отслеживает отмену значения по умолчанию -Zmutable-alias=no введенного в https://github.com/rust-lang/rust/pull/54639 из-за ошибки в LLVM. cc @nagisa

( Дежавю? )

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

Самый полезный комментарий

Я свел его к простому тесту C (скомпилируйте с -O3 и -O0 и сравните вывод):

#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]);
}

Обратите внимание, что если вы удалите restrict , эквивалент C noalias , поведение будет правильным. Но даже тогда assert(a != b) проходит, доказывая, что UB не может произойти из-за вызова его с помощью restrict .

Что происходит:

  1. copy () становится встроенным, в результате получается что-то вроде:
for (int i = 0; i < 3; i++) {
    mat[idxs[i]] = mat[i%2]; mat[i%2] = 7;
}
  1. LLVM разворачивает цикл:
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 считает, что mat[0] не может быть псевдонимом с mat[idxs[1]] или mat[1] , следовательно, его нельзя изменить между mat[0] = 7; и mat[idxs[2]] = mat[0]; , следовательно, это безопасно для глобальная нумерация значений для оптимизации последнего до mat[idxs[2]] = 7; .

Но mat[0] имеет псевдоним с mat[idxs[1]] , потому что idxs[1] == 0 . И мы не обещали, что этого не произойдет, потому что на второй итерации, когда &mat[idxs[1]] передается в copy , другим аргументом является &mat[1] . Так почему же LLVM думает, что это невозможно?

Ну, это связано со способом встраивания copy . Атрибут noalias function превращается в метаданные !alias.scope и !noalias в инструкциях загрузки и сохранения, например:

  %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

Обычно, если функция встроена несколько раз, каждая копия получает свои собственные уникальные идентификаторы для alias.scope и noalias, что указывает на то, что каждый вызов представляет свое собственное отношение «неравенство» * между парой аргументов, отмеченных noalias ( restrict на уровне C), которые могут иметь разные значения для каждого вызова.

Однако в этом случае сначала функция встроена в цикл, затем встроенный код дублируется при развертывании цикла - и это дублирование не меняет идентификаторы. Из-за этого LLVM считает, что ни один из a может быть псевдонимом с каким-либо из b , что неверно, потому что a из псевдонимов первого и третьего вызовов с b из второго вызова (все указывают на &mat[0] ).

Удивительно, но GCC также неправильно компилирует это с другим выводом. (clang и GCC при -O0 выводят 7 10 ; clang at -O3 выводят 7 7 ; GCC при -O3 выводят 10 7 .) Я очень надеюсь, что не облажаться и добавить UB все-таки, но я не понимаю, как ...

* Это немного сложнее, но в этом случае, поскольку copy не использует арифметические операции с указателями и выполняет запись в оба указателя, неравенство a != b необходимо и достаточно для того, чтобы вызов не выполнялся. быть UB.

Все 33 Комментарий

Я все еще работаю над выяснением основной проблемы. Интересный билет - https://github.com/rust-lang/rust/issues/54462.

Используя минимальное воспроизведение @nagisa :


Минимизированный тестовый пример без небезопасного кода (убедитесь, что скомпилирован с 1 модулем кодогенератора!):

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);
}

Мне удалось разделить проходы оптимизации LLVM пополам, чтобы найти тот, который вызывает ошибку.

Выполнение этой команды приводит к работающему исполняемому файлу (замените bug.rs именем файла, в котором вы сохранили репродукцию).

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

Во время выполнения этой команды исполняемый файл не работает (assert_eq не работает):

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

Вывод LLVM пополам

Для этого файла оптимизация 2261 соответствует Global Value Numbering on function (_ZN3bug6swappy17hdcc51d0e284ea38bE)

Разделение ревизий LLVM пополам (с использованием llvmlab bisect) сужает его до r305936-r305938, предположительно r305938:

[BasicAA] Используйте MayAlias ​​вместо PartialAlias ​​для отката.

Обратите внимание, что это довольно старое изменение с июня 2017 года.

Изменить: глядя на описание фиксации, кажется вероятным, что ошибка существовала до этого, но была замаскирована BasicAA, предотвращая запуск последующих передач псевдонима, что и было исправлено фиксацией. Этот случай включает проверку псевдонима между парой инструкций getelementptr когда компилятор знает, что у них один и тот же базовый адрес, но не знает смещения.

Edit2: Кроме того, передача -enable-scoped-noalias=false в качестве параметра LLVM предотвращает неправильную компиляцию. (Это неудивительно, поскольку это полностью отключает обработку noalias, но на всякий случай это помогает ...)

Глядя на IR до GVN, я чувствую, что основная причина здесь может заключаться в развертывании цикла, в зависимости от того, правильно ли я понимаю, как работают аннотации псевдонимов LLVM.

Рассмотрим код вроде

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

где a[i & 1] и b[i & 1] не являются псевдонимами в пределах одной итерации , но a и b в целом могут быть псевдонимами.

В LLVM IR это будет примерно так:

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}

Если мы пропустим это через -loop-unroll мы получим:

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}

Обратите внимание, как все четыре копии цикла используют метаданные псевдонима в одном и том же домене. Вместо того, чтобы быть noalias внутри одной итерации, это noalias для всей функции.

Наконец, -scoped-noalias -gvn дает нам:

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}

И это приведет к неверным результатам, если a = b + 1 .

Эту проблему можно воспроизвести из C с помощью следующего кода:

#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;
}

В Clang 6.0 это печатает 2 2 2 в -O0 и 1 2 2 в -O3 . Я не уверен, что этот код законен для семантики restrict в C, но я думаю, что он должен быть законным при более строгой семантике noalias LLVM.

Я свел его к простому тесту C (скомпилируйте с -O3 и -O0 и сравните вывод):

#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]);
}

Обратите внимание, что если вы удалите restrict , эквивалент C noalias , поведение будет правильным. Но даже тогда assert(a != b) проходит, доказывая, что UB не может произойти из-за вызова его с помощью restrict .

Что происходит:

  1. copy () становится встроенным, в результате получается что-то вроде:
for (int i = 0; i < 3; i++) {
    mat[idxs[i]] = mat[i%2]; mat[i%2] = 7;
}
  1. LLVM разворачивает цикл:
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 считает, что mat[0] не может быть псевдонимом с mat[idxs[1]] или mat[1] , следовательно, его нельзя изменить между mat[0] = 7; и mat[idxs[2]] = mat[0]; , следовательно, это безопасно для глобальная нумерация значений для оптимизации последнего до mat[idxs[2]] = 7; .

Но mat[0] имеет псевдоним с mat[idxs[1]] , потому что idxs[1] == 0 . И мы не обещали, что этого не произойдет, потому что на второй итерации, когда &mat[idxs[1]] передается в copy , другим аргументом является &mat[1] . Так почему же LLVM думает, что это невозможно?

Ну, это связано со способом встраивания copy . Атрибут noalias function превращается в метаданные !alias.scope и !noalias в инструкциях загрузки и сохранения, например:

  %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

Обычно, если функция встроена несколько раз, каждая копия получает свои собственные уникальные идентификаторы для alias.scope и noalias, что указывает на то, что каждый вызов представляет свое собственное отношение «неравенство» * между парой аргументов, отмеченных noalias ( restrict на уровне C), которые могут иметь разные значения для каждого вызова.

Однако в этом случае сначала функция встроена в цикл, затем встроенный код дублируется при развертывании цикла - и это дублирование не меняет идентификаторы. Из-за этого LLVM считает, что ни один из a может быть псевдонимом с каким-либо из b , что неверно, потому что a из псевдонимов первого и третьего вызовов с b из второго вызова (все указывают на &mat[0] ).

Удивительно, но GCC также неправильно компилирует это с другим выводом. (clang и GCC при -O0 выводят 7 10 ; clang at -O3 выводят 7 7 ; GCC при -O3 выводят 10 7 .) Я очень надеюсь, что не облажаться и добавить UB все-таки, но я не понимаю, как ...

* Это немного сложнее, но в этом случае, поскольку copy не использует арифметические операции с указателями и выполняет запись в оба указателя, неравенство a != b необходимо и достаточно для того, чтобы вызов не выполнялся. быть UB.

Хех, похоже, я гонялся с @nikic, чтобы найти такое же объяснение. Их тестовый пример немного лучше :)

Это действительно отличное время ^^ Мы пришли к такому же выводу с почти таким же сокращенным тестовым примером в то же время :)

Чтобы исправить это, возможно, что-то вроде https://github.com/llvm-mirror/llvm/blob/54d4881c352796b18bfe7314662a294754e3a752/lib/Transforms/Utils/InlineFunction.cpp#L801 необходимо также выполнить в LoopUnrollPass.

Я отправил отчет об ошибке LLVM для этой проблемы по адресу https://bugs.llvm.org/show_bug.cgi?id=39282.

И - просто упомянув это для полноты - я отправил отчет об ошибке в GCC, поскольку он также неправильно скомпилировал мой тестовый пример C: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87609

Сортировка: Если я правильно прочитал, исправление LLVM было принято (https://reviews.llvm.org/D9375). Я не уверен, что это значит для фактического слияния с LLVM и когда это произошло; Кто-то, более осведомленный о процессе ревизии LLVM, должен проверить, устранена ли проблема сейчас (и для каких версий).

Он не объединен, процесс проверки был немного странным, и человек, который одобрил его, больше не проверяет исправления.

Был призыв к тестированию с набором патчей «full restrict». Было бы, вероятно, полезно, если бы кто-нибудь попробовал повторно включить noalias в Rust поверх llvm, к которому применен этот набор патчей.

Я хочу попробовать это сделать.
У меня есть доступ к умеренно мощным серверам (48 ядер HT, 128 ГБ оперативной памяти), и я думаю, что смогу собрать все правильно с помощью патча.
Какие ящики вы бы порекомендовали попробовать, когда у меня будет рабочий набор инструментов?

Я закончил слияние всех коммитов, специфичных для ржавчины, на ведущем сервере llvm, а затем применил патч .
Вот получившаяся ветка: https://github.com/PaulGrandperrin/llvm-project/tree/llvm-master-with-rustlang-patches-and-D69542
Сейчас я попробую скомпилировать набор инструментов и попробовать

Сначала убедитесь, что вы вернулись: https://github.com/rust-lang/rust/pull/54639, чтобы Rust действительно генерировал noalias.

Первым делом можно попробовать что-нибудь вроде:

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

и убедитесь, что он компилируется примерно так:

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

и не

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

Затем убедитесь, что код из https://github.com/rust-lang/rust/issues/54462#issue -362850708 больше не компилируется неправильно.

@jrmuizel Обратите внимание , что # 54639 действительно включают @nagisa «s минимальный репродуктор для # 54462 в качестве нового теста компилятора. Может, полный откат не в порядке?

Я не думаю, что важно включать его или нет, потому что, AFAIK, речь идет только об изменении некоторого флага по умолчанию (который можно переопределить с помощью -Zmutable-noalias=yes ), и я могу вручную скомпилировать упомянутый тестовый файл.

Просто чтобы вы знали, я все еще пытаюсь создать LLVM, но на данный момент я получаю ошибку компиляции с примененными патчами, специфичными для ржавчины, или без них (то есть: только мастер llvm восходящего потока + патч тоже не работает):

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....

Может быть, мастер LLVM уже рассинхронизировался с патчем (что могло произойти, учитывая, насколько велик патч и как быстро движется мастер LLVM), и вам нужно использовать более старую версию мастера LLVM? Об этом определенно стоит поговорить с автором патча.

Это именно то, что я делаю, пытаясь найти более старую ревизию, которая собирается с патчем :-)

Теперь я уверен, что проблема не в llvm / master, а в патче.
Самая старая фиксация от llvm / master, которая все еще совместима с патчем, - это https://github.com/llvm/llvm-project/commit/5b99c189b3bfc0faa157f7ca39652c0bb8c315a7, но даже в этом случае патч не компилируется.
Я слишком устал и ленив, чтобы сейчас разбираться в C ++, завтра попробую еще раз.
А пока может кто-нибудь обратиться к автору патча за помощью?

Я не думаю, что вы сможете легко использовать мастер LLVM с Rust (после того, как вы его действительно соберете ), не исправляя

@ mati865 Сначала я попробовал наложить патч на вилку

Патч, по-видимому, переустановлен поверх llvm / llvm-project @ 82d3ba87d06f9e2abc6e27d8799587d433c56630. Будет ли это хорошо для вас, если вы подадите дополнительную заявку?

@jrmuizel спасибо, попробую!
Тем временем мне удалось успешно адаптировать rustllvm для сборки с помощью llvm master.

Пинг @PaulGrandperrin , есть обновления?

https://reviews.llvm.org/D68484

На самом деле, каковы предполагаемые сроки и общие шансы, что этот патч когда-либо будет объединен, учитывая его размер?

@MSxDOS Спросите разработчиков LLVM. В общем, я ожидал, что размер патча имеет меньшее значение, чем желание владельцев увидеть его слияние, поэтому возникает вопрос, насколько LLVM хочет, чтобы он приземлился.

Вот последний статус, который я видел: https://reviews.llvm.org/D69542#1836439

я предполагаю, что в какой-то момент это перестанет быть актуальным, если https://github.com/bytecodealliance/cranelift~~ https://github.com/bjorn3/rustc_codegen_cranelift сработает

@leeoniya , нет краткосрочного плана использовать кранлифт для опт билдов. Cranelift не требует той работы по оптимизации, которая могла бы сделать это возможным.

Я был удивлен, узнав, насколько консервативен компилятор в отношении предположения о возможном алиасинге без этой опции. Например:

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

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

Поскольку он обращается к членам структуры через &mut , предполагается, что s.x и s.y могут быть псевдонимами, поэтому для s.y требуется два доступа к памяти вместо одного. Это действительно прискорбно, если учесть, сколько раз в типичной программе необходимо чередовать чтение / запись члена через &mut .

Изменить: на основе некоторого тестирования, похоже, это не влияет на все такие чтения / записи, что неудивительно, потому что это, вероятно, убило бы производительность, если бы это было. Тем не менее, использование -Z mutable-noalias устраняет двойной доступ к памяти в приведенном выше примере, поэтому некоторые случаи могут быть остановлены.

@PaulGrandperrin есть новая версия этого патча на https://reviews.llvm.org/D69542, основанная на llvm @ 9fb46a452d4e5666828c95610ceac8dcd9e4ce16. Вы готовы попробовать запустить его снова?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги