@AHMED_RAPIRA

Почему wasm код не генерируется при компиляции rust кода с сырыми указателями?

С помощью cargo target=wasm32-unknown-unknown собираю rust no_std код:

#[no_mangle]
pub fn test() {
    let mut idx: i32 = 10;
    let ptr = &mut idx as *mut i32;

    let buffer = unsafe { core::slice::from_raw_parts_mut(ptr, 10) };

    for pixel in buffer.iter_mut() {
        *pixel = 0x7d2b7500;
    }
}


Получаю такой wasm бинарник:

(module
  (type (;0;) (func))
  (func (;0;) (type 0)
    nop)
  (memory (;0;) 16)
  (global (;0;) i32 (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "test" (func 0))
  (export "__data_end" (global 0))
  (export "__heap_base" (global 1)))


Моя функция не имеет содержимого (nop). Но теперь, если я передам сырой указатель из-вне в качестве аргумента функции test, код появляется:

#[no_mangle]
pub fn test(ptr: *mut i32) {
    let buffer = unsafe { core::slice::from_raw_parts_mut(ptr, 10) };

    for pixel in buffer.iter_mut() {
        *pixel = 0x7d2b7500;
    }
}


(module
  (type (;0;) (func (param i32)))
  (func (;0;) (type 0) (param i32)
    local.get 0
    i64.const 9019431323700000000
    i64.store offset=32 align=4
    local.get 0
    i64.const 9019431323700000000
    i64.store offset=24 align=4
    local.get 0
    i64.const 9019431323700000000
    i64.store offset=16 align=4
    local.get 0
    i64.const 9019431323700000000
    i64.store offset=8 align=4
    local.get 0
    i64.const 9019431323700000000
    i64.store align=4)
  (memory (;0;) 16)
  (global (;0;) i32 (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "test" (func 0))
  (export "__data_end" (global 0))
  (export "__heap_base" (global 1)))


Почему в первом случае код функции test не генерировался? Никакой документации по wasm32-unknown-unknown нет в сети, и тематических форумов нет, просто засада
  • Вопрос задан
  • 164 просмотра
Решения вопроса 1
bingo347
@bingo347
Crazy on performance...
Начну пожалуй с того, что в первом примере у Вас UB (undefined behavior):
#[no_mangle]
pub fn test() {
    // Вот тут на стеке выделено 4 байта
    let mut idx: i32 = 10;
    // Указатель на эти 4 байта
    let ptr = &mut idx as *mut i32;

    // По сути указатель на те же 4 байта, но на деле указатель на 40 байт
    let buffer = unsafe { core::slice::from_raw_parts_mut(ptr, 10) };

    // И тут мы работаем с этими 40 байтами,
    // но нормальные из них только 4 байта в начале,
    // а остальные смотрят в глубь стека и меняют его
    // то есть перетирают адрес возврата и данные на стеке вызвавшей функции
    for pixel in buffer.iter_mut() {
        *pixel = 0x7d2b7500;
    }
}
То есть после завершения данная функция должна не вернуться в вызывающую функцию, а передать управление непонятно куда.

Ну и еще один момент, это оптимизация, данная функция не имеет сайд эффектов и ничего не возвращает. После инлайна функции core::slice::from_raw_parts_mut и метода Iterator::iter_mut компилятор вполне может понять, что данный код не имеет сайд эффектов, а значит бесполезен и его можно вырезать. Вот собственно он его и вырезает.
Во втором же примере у нас есть сайд эффект - изменение данных по указателю пришедшему извне через аргументы.

P.S. Во втором примере кстати тоже можно увидеть работу оптимизации, цикл из 10 итераций, пишущих i32 в смещение по указателю, был заменен на обычные 5 записей i64 в смещение по указателю.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы