Удаление добавленной инструкции «break» во встроенной сборке mips

#rust #mips #inline-assembly

Вопрос:

У меня есть следующая (упрощенная) функция, использующая встроенную сборку, нацеленную на mips:

 #[naked]
pub unsafe extern "C" fn test() {
    asm!(
        ".set noreorder",
        "jr $ra",
        "li $v0, 0x123",
        options(noreturn),
    )
}
 

Я ожидал, что это будет скомпилировано только в 2 указанные инструкции (в режиме выпуска), поскольку это голая функция, но break в конце добавляется инструкция:

 00000000 <test>:
   0:   03e00008        jr      ra
   4:   24020123        li      v0,291
   8:   0000000d        break
 

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

Есть ли какой-либо способ запретить rustc, llvm или ассемблеру генерировать эту дополнительную инструкцию в целом?

Я протестировал его на существующих целях, таких как mipsel-unknown-none , и он также создал break инструкцию, но я компилирую на следующей пользовательской цели, если это имеет значение:

 {
    "arch": "mips",
    "cpu": "mips1",
    "data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i32:32-n32-S32",
    "emit-debug-gdb-scripts": false,
    "executables": false,
    "features": " mips32, soft-float, noabicalls",
    "linker": "rust-lld",
    "linker-flavor": "ld.lld",
    "llvm-target": "mipsel-unknown-linux-gnu",
    "relocation-model": "static",
    "target-pointer-width": "32",
    "panic-strategy": "abort",
    "singlethread": true,
    "dynamic-linking": false,
    "function-sections": true
}
 

Я также использую a #![no_std] и #![no_core] staticlib crate с внедренными необходимыми элементами lang и просто компилирую с помощью cargo build --release --target=my-target.json

Редактировать: После предложения Питера Кордеса я попробовал то же самое в C с

 __attribute__((naked)) void test() {
    __asm__(
        ".set noreordern"
        "jr $ran"
        "li $v0, 0x123n"
    );
}
 

Скомпилировано с использованием

 clang -O3 test.c -c -o test.o -target mips-unknown-none
 

И результат

 00000000 <test>:
   0:   03e00008    jr  ra
   4:   24020123    li  v0,291
 

Без a break , так что, похоже, он был включен компилятором rust.

Комментарии:

1. Если вы удалите noreturn, возвращает ли он нормальную функцию return? Учитывая, что это голая функция, я не уверен. В любом случае, похоже, стоит попробовать.

2. @DavidWohlferd К сожалению, если я ее удалю, я получу предупреждение (в котором говорится, что оно скоро будет переведено в ошибку), в котором говорится, что asm в голых функциях должен иметь noreturn .

3. Интересно, делает ли clang это с помощью встроенного asm C для MIPS?

4. @PeterCordes На самом деле этого не происходит (см. Редактирование вопроса), поэтому кажется, что компилятор rust добавляет его в какой-то момент, я пойду проверю излучаемый llvm ir, чтобы увидеть, есть ли он.

5. На самом деле, как ни странно, если я создаю llvm-ir из rust, передаю его в llvm с llc test.ir -o test.s -mtriple=mips-unknown-none помощью, а затем собираю его с mips-unknown-gnu-as test.s -o test.o помощью, у него также нет инструкции break, я предполагаю, что это какой-то флаг, который rustc передает llvm, который вызывает это

Ответ №1:

Да! Выполните одно из:

  • Добавьте "trap_unreachable": false в свой target.json
  • Сборка с RUSTFLAGS=-Ztrap-unreachable=no помощью . (хотя только по ночам)

К сожалению, это не очень хорошо документировано. Дальнейшее чтение: PR, где была добавлена генерация команды trap , PR, где был добавлен trap-unreachable = no

Комментарии:

1. В первом она должна быть trap-unreachable (с дефисом вместо подчеркивания), но, кроме этого, это работает отлично! Спасибо за отслеживание реализации и пометку PR

2. Странно, что это вообще необходимо в naked функции; это особый случай, когда выполнение никогда не должно выпадать из нижней части инструкции asm.