Зачем в голой функции Rust лишние инструкции ASM?

Я оборачиваю низкоуровневый ABI в Rust, используя функцию naked. Вот мой код и соответствующая разборка

#![feature(asm)]
#![feature(naked_functions)]

struct MyStruct {
    someVar: i64, // not important
                  // ...
}

impl MyStruct {
    #[naked]
    extern "C" fn wrap(&self) {
        unsafe {
            asm!("NOP" :::: "volatile");
            // not sure if the volatile option is needed, but I
            // figured it wouldn't hurt
        }
    }
}

Разобрано с LLDB:

ABIWrap`ABIWrap::{{impl}}::wrap:
  * 0x100001310 <+0>:  movq   %rdi, -0x10(%rbp)
  * 0x100001314 <+4>:  movq   %rsi, -0x8(%rbp)
  * 0x100001318 <+8>:  movq   -0x10(%rbp), %rax
  * 0x10000131c <+12>: movq   -0x8(%rbp), %rcx
  * 0x100001320 <+16>: movq   %rax, -0x20(%rbp)
  * 0x100001324 <+20>: movq   %rcx, -0x18(%rbp)
    0x100001328 <+24>: nop    
    0x100001329 <+25>: retq   
    0x10000132a <+26>: nopw   (%rax,%rax)

Меня смущают 6 строк, предшествующих NOP (я пометил *). Не должна ли директива naked оставить, за неимением лучшего термина, голую функцию?

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

Есть ли способ избавиться от этих 6 предыдущих инструкций? Я часто коллирую против ABI, и предыдущий способ, которым я коллировал против него, вызывал приличное количество накладных расходов. Я хочу убедиться, что регистры, содержащие какие-либо важные значения, не перезаписываются.

Боковое примечание: нужна ли «изменчивая» опция? Я не был уверен, но все равно добавил.


person Chase Walden    schedule 15.02.2017    source источник
comment
Я не знаю, но просто для проверки работоспособности: вы декомпилировали релизную сборку, да?   -  person Simon Whitehead    schedule 15.02.2017
comment
Нет, я декомпилирую отладочную сборку   -  person Chase Walden    schedule 15.02.2017
comment
Для моего собственного любопытства, что это за ABI и на какой платформе?   -  person Shepmaster    schedule 15.02.2017
comment
Устрашающе похоже на то, что он прыгает обратно в какое-то теневое пространство, чтобы захватить некоторые данные, которые указывает 64-битный Windows ABI ... хотя сложно сказать. @ChaseWalden также для моего любопытства (я не изучал внутреннюю работу «голых» функций в Rust), что происходит, когда вы не встраиваете функцию в реализацию структуры. Что, если это просто чистая функция? Мне интересно, тянет ли эта прелюдия self откуда-то.   -  person Simon Whitehead    schedule 15.02.2017
comment
@Шепмастер, ты прав. это асм! не асм. Я перепечатал свой код в своем вопросе, а не скопировал и вставил. Это асм! в моем фактическом коде просто небольшая опечатка в моем фактическом вопросе. Кроме того, ABI — это старый пакет, который я написал для взаимодействия с одним из моих встроенных проектов. Он не очень чистый, и я не выпускал его, так как он предназначен только для одной платы, которую я сделал. Я пытаюсь использовать ABI, который я уже реализовал, так как его переписывание потребовало бы большого количества рефакторинга прошивки платы и библиотеки интерфейса.   -  person Chase Walden    schedule 15.02.2017
comment
@SimonWhitehead, я подозреваю, что вы правы. Я думаю, что это может иметь какое-то отношение к прелюдии к себе, но мне было интересно, есть ли способ полностью избавиться от этого.   -  person Chase Walden    schedule 15.02.2017
comment
После дальнейших исследований похоже, что это может быть ошибка (или это функция?). Не прямое совпадение с этим сценарием, но похожее. Вот заявка на выпуск github.com/rust-lang/rust/issues/34043   -  person Chase Walden    schedule 15.02.2017
comment
@ChaseWalden: я думаю, вы нашли свой ответ, нет смысла в голых функциях, принимающих аргументы, поскольку использование этих аргументов требует соглашения о том, как они передаются (ABI), что противоречит атрибуту naked, который указывает не принимать никаких АБИ.   -  person Matthieu M.    schedule 15.02.2017
comment
В Rust вы можете указать ABI для использования при вызове голой функции. Это то, что extern "C" делает в примере. Это влияет только на код, сгенерированный на месте вызова. Разработчик функции должен следовать объявленному ABI внутри функции. Важно отметить, что если вы не укажете ABI для голой функции, вы даже не сможете вызвать ее изнутри Rust, поскольку Rust не знает, что делать. на месте вызова.   -  person Joshua Little    schedule 08.05.2021


Ответы (1)


Поработав с этим еще (и подумав, как эффективно разобрать мою сборку релиза), я обнаружил, что дополнительные инструкции добавляются только во время сборки отладки (или, по крайней мере, при -O0).

При компиляции кода с -O2 я обнаружил, что вся сборка встроена, но это легко исправить с помощью директивы #[inline(never)]. Теперь аргументы передаются правильно, без дополнительных инструкций, разрушающих мои регистры :)

Теперь мне просто нужно, чтобы код запускал только -O2 только для этих функций, а не для остальной части отладочной сборки...

person Chase Walden    schedule 16.02.2017
comment
Может стоит открыть тему по этому поводу :) - person Matthieu M.; 16.02.2017
comment
@MatthieuM: Возможно ... Я все еще пытаюсь понять, какова цель перемещения аргументов в стек. Я почти начинаю думать, что Саймон Уайтхед в приведенной выше ветке комментариев предположил, что вызываемый объект хранит значения в теневом пространстве, что странно, поскольку я на Mac. - person Chase Walden; 19.02.2017