Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiler panic while formatting code with inline assembly #6399

Open
sicikh opened this issue Nov 18, 2024 · 1 comment
Open

Compiler panic while formatting code with inline assembly #6399

sicikh opened this issue Nov 18, 2024 · 1 comment
Labels
bug Panic, non-idempotency, invalid code, etc.

Comments

@sicikh
Copy link

sicikh commented Nov 18, 2024

Command:

cargo +nightly fmt

Code:

use std::arch::asm;

use proptest::prelude::*;

fn main() {}

/// Вычислить d как результат (a * b / c), где при переполнении числа или при
/// делении на 0 результатом будет None.
fn calc_d(a: i16, b: i16, c: u8) -> Option<i16> {
    // Референсная реализация для использовании в тестировании свойств
    // (prop(-erty)test)
    a.checked_mul(b).and_then(|it| it.checked_div(c as i16))
}

/// Вычислить d как результат (a * b / c), где при переполнении числа или
/// при делении на 0 результатом будет None.
fn calc_d_asm(mut a: i16, b: i16, c: u8) -> Option<i16> {
    /// Флаги, указывающие на результат выполнения ассемблерной вставки
    ///
    /// Поскольку ABI Rust'а нестабильно, мы не можем напрямую из ассемблера
    /// мутировать переменную типа `Option<i16>`. Этот тип будет создаваться на
    /// основании этого флага.
    // тип перечисления будет занимать 1 беззнаковый байт (u8)
    #[repr(u8)]
    #[derive(Debug, Eq, PartialEq, Copy, Clone)]
    // мы не конструируем флаги напрямую, поэтому отключаем предупреждение
    #[allow(dead_code)]
    enum Flag {
        Ok = 0,
        Overflow = 1,
        DivByZero = 2,
    }

    // По умолчанию считаем, что вставка выполнится успешно.
    // Ассемблерная вставка будет менять этот флаг в случае ошибки.
    let mut flag = Flag::Ok as u8;

    unsafe {
        #[rustfmt::skip]
        asm!(
            // умножаем `a` (в регистре `ax`) на `b`, 
            // записываем результат в `ax` (где и хранится `a`)
            "imul ax, {b:x}",
            // если произошло переполнение, то прыгаем на метку 2
            // здесь и далее `f` - это `forward`, т. е. метка дальше в коде
            // без такой пометки значение интерпретируется как число, 
            // что некорректно для операции `jo`
            "jo 2f",
            // проверяем делитель на 0
            "cmp {c}, 0",
            // если делитель равен 0, то прыгаем на метку 3
            "je 3f",
        
            // Производим само деление:
            // Поскольку нам нужен `i16` как результат, одной операцией
            // `idiv` воспользоваться не получиться. 
            // Следует расширить `ax` до 32 бит (регистр `dx:ax`),
            // а `c` - до 16 бит, преобразовав 
            // беззнаковый байт `c` в положительное знаковое слово.
        
            // расширяем знаком `ax` до 32 бит `dx:ax`
            "cwd",
            // расширяем `c` до 16 бит, преобразовав его в `i16`,
            // выполняем деление `dx:ax` на `c`
            "idiv {c:x}",
            // прыгаем на конец вставки
            "jmp 4f",

            "2:",
            // Произошло переполнение: устанавливаем наш флаг,
            // чтобы вернуть None из функции, прыгаем на конец вставки
            "mov {flag}, 1",
            "jmp 4f",

            "3:",
            // Произошла попытка деления на 0: устанавливаем наш флаг,
            // чтобы вернуть None из функции
            "mov {flag}, 2",

            // Конец вставки
            "4:",
            // регистр `ax` с доступом на чтение и запись,
            // в который записывается значение переменной `a`
            inout("ax") a,
            // параметр под именем `b` с доступом на чтение
            b = in(reg) b,
            // параметр под именем `c` с доступом на чтение.
            // Поскольку компилятор Rust требует для значений класса `reg`
            // размерность не менее 16 бит, то мы преобразуем `c` в `u16` (noop).
            c = in(reg) c as u16,
            // использованный для временного хранения значения `a`, расширенного до 32 бит,
            // регистр `dx`.
            out("dx") _,
            // параметр под именем `flag` с доступом на чтение и запись,
            // в регистр которого записывается значение переменной `flag`,
            // а размерность регистра равна 1 байту
            flag = inout(reg_byte) flag,

            // опции и свойства ассемблерной вставки:
            // pure - вставка не имеет побочных эффектов 
            //  (её результат зависит только от входных параметров)
            // nomem - вставка не обращается к памяти
            // nostack - вставка не использует стек
            //
            // данные опции и свойства являются указанием для компилятора,
            // позволяющим ему оптимизировать код
            options(pure, nomem, nostack),
        );
    }

    // Реинтерпретируем значение флага (u8) как перечисление `Flag` (тоже u8)
    // Поскольку мы не записываем в `flag` ничего, кроме значений 0, 1 или 2,
    // то эта операция безопасна.
    let flag: Flag = unsafe { std::mem::transmute(flag) };

    // В зависимости от значения флага, возвращаем тот или иной результат
    match flag {
        // Если флаг равен `Flag::Ok`, то возвращаем результат вычисления, обёрнутый в `Some`
        Flag::Ok => Some(a),
        // Если флаг равен `Flag::Overflow` или `Flag::DivByZero`, то возвращаем `None`
        Flag::Overflow | Flag::DivByZero => None,
    }
}

/// Вычислить e как результат выражения (a - b) * (b - c), где при переполнении
/// числа результатом будет None.
fn calc_e(a: i16, b: i16, c: u8) -> Option<i16> {
    // Референсная реализация для использовании в тестировании свойств
    // (prop(-erty)test)
    let lhs = a.checked_sub(b)?;
    let rhs = b.checked_sub(c as i16)?;

    lhs.checked_mul(rhs)
}

fn calc_e_asm(_a: i16, _b: i16, _c: u8) -> Option<i16> {
    enum Flag {
        Ok = 0,
        OverflowLhs = 1,
        OverflowRhs = 2,
        OverflowMul = 3,
    }

    todo!()
}

// Тестирование свойств (property testing)
proptest! {
    #[test]
    // Для любых a, b, c из множеств i16, i16, u8 соответственно,
    // выполнить функцию `calc_d` (референсная реализация) и `calc_d_asm` (наша реализация) и
    // проверить, что их результаты равны.
    //
    // Если тест проходит, то `calc_d` и `calc_d_asm` эквивалентны, а так как `calc_d` работает
    // корректно, то и `calc_d_asm` работает корректно.
    fn test_d(a in any::<i16>(), b in any::<i16>(), c in any::<u8>()) {
        let d = calc_d(a, b, c);
        let d_asm = calc_d_asm(a, b, c);
        assert_eq!(d, d_asm);
    }

    // Для любых a, b, c из множеств i16, i16, u8 соответственно,
    // выполнить функцию `calc_e` (референсная реализация) и `calc_e_asm` (наша реализация) и
    // проверить, что их результаты равны.
    //
    // Если тест проходит, то `calc_e` и `calc_e_asm` эквивалентны, а так как `calc_e` работает
    // корректно, то и `calc_e_asm` работает корректно.
    // #[test]
    // fn test_e(a in any::<i16>(), b in any::<i16>(), c in any::<u8>()) {
    //     let e = calc_e(a, b, c);
    //     let e_asm = calc_e_asm(a, b, c);
    //     assert_eq!(e, e_asm);
    // }
}

Output:

thread 'main' panicked at /rust/deps/annotate-snippets-0.9.2/src/display_list/from_snippet.rs:275:9:
SourceAnnotation range `(85, 87)` is bigger than source length `57`
stack backtrace:
   0:     0x7faf4d294ffa - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h214716e6e0c5cd2c
   1:     0x7faf4da0468a - core::fmt::write::hbd48ce2ad7284a0a
   2:     0x7faf4ee08651 - std::io::Write::write_fmt::hb2eafdc8e5760cec
   3:     0x7faf4d294e52 - std::sys::backtrace::BacktraceLock::print::hbc5009a8dd7de74f
   4:     0x7faf4d297356 - std::panicking::default_hook::{{closure}}::he4a5a0eb6c634694
   5:     0x7faf4d2971a0 - std::panicking::default_hook::h6ba3c19e5efafdd4
   6:     0x7faf4c31eeb1 - std[d5e65f54d52c6b80]::panicking::update_hook::<alloc[543edef93a4acc51]::boxed::Box<rustc_driver_impl[5dcda6566a109f30]::install_ice_hook::{closure#0}>>::{closure#0}
   7:     0x7faf4d297a68 - std::panicking::rust_panic_with_hook::h773803f4ebfbed1e
   8:     0x7faf4d29783a - std::panicking::begin_panic_handler::{{closure}}::h09c6376b3729e5b0
   9:     0x7faf4d2954a9 - std::sys::backtrace::__rust_end_short_backtrace::h1dd30efd00c5bb69
  10:     0x7faf4d2974fc - rust_begin_unwind
  11:     0x7faf49ce49c0 - core::panicking::panic_fmt::hcd050e92ce3ec7a3
  12:     0x55f7e0dfb65d - <annotate_snippets[9b90caa08906aceb]::display_list::structs::DisplayList as core[47afb5b034596fb9]::convert::From<annotate_snippets[9b90caa08906aceb]::snippet::Snippet>>::from
  13:     0x55f7e0c1edf4 - <rustfmt_nightly[b45938c28a441e9d]::format_report_formatter::FormatReportFormatter as core[47afb5b034596fb9]::fmt::Display>::fmt
  14:     0x7faf4da0468a - core::fmt::write::hbd48ce2ad7284a0a
  15:     0x7faf4d289b3e - <&std::io::stdio::Stderr as std::io::Write>::write_fmt::h141d1231c6b074d3
  16:     0x7faf4d28a3f8 - std::io::stdio::_eprint::h337e8ced72dc4635
  17:     0x55f7e0b08811 - rustfmt[deac11de4eeee1a2]::format_and_emit_report::<std[d5e65f54d52c6b80]::io::stdio::Stdout>
  18:     0x55f7e0b0622f - rustfmt[deac11de4eeee1a2]::execute
  19:     0x55f7e0b0211c - rustfmt[deac11de4eeee1a2]::main
  20:     0x55f7e0af2a43 - std[d5e65f54d52c6b80]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  21:     0x55f7e0af4ad9 - std[d5e65f54d52c6b80]::rt::lang_start::<()>::{closure#0}
  22:     0x7faf4e9511c1 - std::rt::lang_start_internal::h5758e332aa2f8cc6
  23:     0x55f7e0b09868 - main
  24:     0x7faf48917e08 - <unknown>
  25:     0x7faf48917ecc - __libc_start_main
  26:     0x55f7e0ade1b9 - <unknown>
  27:                0x0 - <unknown>

error: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rustfmt/issues/new?labels=bug

note: please make sure that you have updated to the latest nightly

note: please attach the file at `/home/bezkonca/uni/sem-3/comptech/lab3/lab3-rs/rustc-ice-2024-11-18T10_38_07-2250081.txt` to your bug report

query stack during panic:
end of query stack

File /home/bezkonca/uni/sem-3/comptech/lab3/lab3-rs/rustc-ice-2024-11-18T10_32_31-2249440.txt:

thread 'main' panicked at /rust/deps/annotate-snippets-0.9.2/src/display_list/from_snippet.rs:275:9:
SourceAnnotation range `(85, 87)` is bigger than source length `57`
stack backtrace:
   0:     0x7fca35c26665 - std::backtrace::Backtrace::create::h0dfab4e59fc1562a
   1:     0x7fca34080175 - std::backtrace::Backtrace::force_capture::hce43afc2210a30d1
   2:     0x7fca3311f4f5 - std[d5e65f54d52c6b80]::panicking::update_hook::<alloc[543edef93a4acc51]::boxed::Box<rustc_driver_impl[5dcda6566a109f30]::install_ice_hook::{closure#0}>>::{closure#0}
   3:     0x7fca34097a68 - std::panicking::rust_panic_with_hook::h773803f4ebfbed1e
   4:     0x7fca3409783a - std::panicking::begin_panic_handler::{{closure}}::h09c6376b3729e5b0
   5:     0x7fca340954a9 - std::sys::backtrace::__rust_end_short_backtrace::h1dd30efd00c5bb69
   6:     0x7fca340974fc - rust_begin_unwind
   7:     0x7fca30ae49c0 - core::panicking::panic_fmt::hcd050e92ce3ec7a3
   8:     0x558443dab65d - <annotate_snippets[9b90caa08906aceb]::display_list::structs::DisplayList as core[47afb5b034596fb9]::convert::From<annotate_snippets[9b90caa08906aceb]::snippet::Snippet>>::from
   9:     0x558443bcedf4 - <rustfmt_nightly[b45938c28a441e9d]::format_report_formatter::FormatReportFormatter as core[47afb5b034596fb9]::fmt::Display>::fmt
  10:     0x7fca3480468a - core::fmt::write::hbd48ce2ad7284a0a
  11:     0x7fca34089b3e - <&std::io::stdio::Stderr as std::io::Write>::write_fmt::h141d1231c6b074d3
  12:     0x7fca3408a3f8 - std::io::stdio::_eprint::h337e8ced72dc4635
  13:     0x558443ab8811 - rustfmt[deac11de4eeee1a2]::format_and_emit_report::<std[d5e65f54d52c6b80]::io::stdio::Stdout>
  14:     0x558443ab72ff - rustfmt[deac11de4eeee1a2]::execute
  15:     0x558443ab211c - rustfmt[deac11de4eeee1a2]::main
  16:     0x558443aa2a43 - std[d5e65f54d52c6b80]::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
  17:     0x558443aa4ad9 - std[d5e65f54d52c6b80]::rt::lang_start::<()>::{closure#0}
  18:     0x7fca357511c1 - std::rt::lang_start_internal::h5758e332aa2f8cc6
  19:     0x558443ab9868 - main
  20:     0x7fca2f745e08 - <unknown>
  21:     0x7fca2f745ecc - __libc_start_main
  22:     0x558443a8e1b9 - <unknown>
  23:                0x0 - <unknown>


rustc version: 1.84.0-nightly (5ec7d6eee 2024-11-17)
platform: x86_64-unknown-linux-gnu

Comments:

It looks like #6392.

@ytmimi
Copy link
Contributor

ytmimi commented Nov 18, 2024

@sicikh thanks for the report. Any chance you can get the reproducible example down to a smaller size? rustfmt can work on code that doesn't compile so it should be easy to remove lines of code that don't contribute to the problem.

@ytmimi ytmimi added the bug Panic, non-idempotency, invalid code, etc. label Nov 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Panic, non-idempotency, invalid code, etc.
Projects
None yet
Development

No branches or pull requests

2 participants