Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

обнуление массива без memset

#cpp #ассемблер


здравствуйте, не могу понять следующий выхлоп:

ex::ex():
movq    $0, (%rdi)
movq    $0, 992(%rdi)
movq    %rdi, %rcx
leaq    8(%rdi), %rdi
xorl    %eax, %eax
andq    $-8, %rdi
subq    %rdi, %rcx
addl    $1000, %ecx
shrl    $3, %ecx
rep stosq
ret


для кода:

class ex {
  public:
  ex() :a{0} {}
  char a[1000];
};

int main() {
    ex a;
}


читал, что  rep stosq делает примерно то же, что и цикл, заполняя что-то пока ecx
> 0, вот только не могу толком понять что. в подобных вопросах говорят, что вроде edi
заполняется значениями из eax... однако полного понимания  как достигается обнуление
массива в 1000 элементов нету... разъясните по инструкциям что там и зачем, сдвиг на
3 или вот это, например, зачем: 

movq    $0, (%rdi)
movq    $0, 992(%rdi)


обновление: в общем, предыдущий выхлоп был с -fno-inline, без него генерится вот это:

    subq    $1016, %rsp
    movl    $125, %ecx
    xorl    %eax, %eax
    movq    %rsp, %rdi
    rep stosq


вопрос тот же: что за треш с rep stosq?
    


Ответы

Ответ 1



Сдвиг на 3 нужен потому, что компилятор желает использовать инструкцию rep stosq, которая заполняет память не побайтно, а по 8-байтным словам. Поэтому количество итераций, которое надо сделать инструкции rep stosq равно размеру буфера, деленному на 8. Это и есть сдвиг вправо на 3. То есть при подходе "в лоб" надо просто сделать 125 итераций rep stosq. Однако для того, чтобы rep stosq работала эффективнее, необходимо, чтобы ее целевой адрес был выровнен на границу 8 байт. Ваш буфер a не гарантированно выровнен на границу 8 байт. Поэтому компилятор делает следующее: первое и последнее 8-байтное слово вашего буфера обнуляются индивидуально. Именно это делают инструкции movq $0, (%rdi) movq $0, 992(%rdi) А далее компилятор вычисляет выровненный на границу 8 байт адрес, с которого надо начать обнуление оставшейся "серединки" вашего буфера leaq 8(%rdi), %rdi andq $-8, %rdi а также вычисляет, сколько надо сделать итераций rep stosq, чтобы обнулить эту "серединку". Для вычисления количества итераций мы вычитаем из ecx (который в этот момент содержит точное начало буфера), значение выровненного начала subq %rdi, %rcx Если произошло фактическое выравнивание, то значение rcx станет отрицательным (более того, даже при выровненном буфере rdi здесь содержит адрес второго слова, т.е. значение rcx в любом случае будет отрицательным). Затем addl $1000, %ecx shrl $3, %ecx вычислит требуемое количество итераций, которое в общем случае получится меньше, чем 125 (т.е. 124).

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

Отправить комментарий