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

277
06 апреля 2017, 20:49

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

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?

Answer 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).

READ ALSO
Как конвертировать usigned __int64 * в std::vector<bool>

Как конвертировать usigned __int64 * в std::vector<bool>

Есть массив unsigned __int64В него необходимо дописать 2 бита

269
Определить закрыта ли камера или нет (C++)

Определить закрыта ли камера или нет (C++)

Рабочее приложение сотрудника (клиент) транслирует веб-камеруНужно определять не заклеена ли камера и не заблокирована ли она какой-то программой

226
How to connect Excel to a VS C ++ CLI project [требует правки]

How to connect Excel to a VS C ++ CLI project [требует правки]

How to connect Excel to a VS C ++ CLI projectNamely, how to interact with data cells in Excel, how to move from cell to cell, and give please a book on this topic (preferably in Russian)

183