Попытка понять сложное выравнивание стека gcc наверху основного, которое копирует обратный адрес

Путем я делаю это:

  • Всегда помещает условия объединения в ON пункт, если Вы делаете INNER JOIN. Так, не добавляйте никого, КУДА условия к НА пункте, поместите их в WHERE пункт.

  • , Если Вы делаете LEFT JOIN, добавьте любого ГДЕ условия к ON пункт для таблицы в право сторона соединения. Это - необходимость, потому что добавление оператора Where, который ссылается на правую сторону соединения, преобразует соединение во ВНУТРЕННЕЕ ОБЪЕДИНЕНИЕ.

    исключение - при поиске записей, которые не находятся в конкретной таблице. Вы добавили бы ссылку на уникальный идентификатор (который никогда не является ПУСТЫМ) в ПРАВИЛЬНОЙ Объединяющей таблице к оператору Where этот путь: WHERE t2.idfield IS NULL. Так, единственное время, необходимо сослаться на таблицу на правой стороне соединения, должно найти те записи, которые не находятся в таблице.

17
задан Peter Cordes 1 June 2019 в 07:32
поделиться

3 ответа

I've had a go at it:

;# As you have already noticed, the compiler wants to align the stack
;# pointer on a 16 byte boundary before it pushes anything. That's
;# because certain instructions' memory access needs to be aligned
;# that way.
;# So in order to first save the original offset of esp (+4), it
;# executes the first instruction:
lea    ecx,[esp+0x4]

;# Now alignment can happen. Without the previous insn the next one
;# would have made the original esp unrecoverable:
and    esp,0xfffffff0

;# Next it pushes the return addresss and creates a stack frame. I
;# assume it now wants to make the stack look like a normal
;# subroutine call:
push   DWORD PTR [ecx-0x4]
push   ebp
mov    ebp,esp

;# Remember that ecx is still the only value that can restore the
;# original esp. Since ecx may be garbled by any subroutine calls,
;# it has to save it somewhere:
push   ecx
27
ответ дан 30 November 2019 в 11:37
поделиться
lea    ecx,[esp+0x4] ; I assume this is for getting the adress of the first argument of     the main...why ?
and    esp,0xfffffff0 ; ??? is the compiler trying to align the stack pointer on 16 bytes ???
push   DWORD PTR [ecx-0x4] ; I understand the assembler is pushing the return adress....why ?
push   ebp                
mov    ebp,esp
push   ecx  ;why is ecx pushed too ??

Даже если каждая инструкция работала идеально без потери скорости, несмотря на произвольное выравнивание операндов, выравнивание все равно повысит производительность. Представьте себе цикл, ссылающийся на 16-байтовую величину, которая просто перекрывает две строки кэша. Теперь, чтобы загрузить этот маленький wchar в кеш, нужно исключить две целые строки кеша, а что, если они вам понадобятся в том же цикле? Кэш настолько быстрее, чем оперативная память, что производительность кеша всегда критична.

Кроме того, обычно существует потеря скорости для смещения неверно выровненных операндов в регистры. Учитывая, что стек повторно выравнивается, мы, естественно, должны сохранить старое выравнивание, чтобы просмотреть кадры стека для параметров и возврата.

ecx - это временный регистр, поэтому его необходимо сохранить. Кроме того, в зависимости от уровня оптимизации, некоторые операции по связыванию кадров, которые не кажутся строго необходимыми для запуска программы, могут быть важны для создания готовой к трассировке цепочки кадров.

4
ответ дан 30 November 2019 в 11:37
поделиться

This is done to keep the stack aligned to a 16-byte boundary. Some instructions require certain data types to be aligned on as much as a 16-byte boundary. In order to meet this requirement, GCC makes sure that the stack is initially 16-byte aligned, and allocates stack space in multiples of 16 bytes. This can be controlled using the option -mpreferred-stack-boundary=num. If you use -mpreferred-stack-boundary=2 (for a 22=4-byte alignment), this alignment code will not be generated because the stack is always at least 4-byte aligned. However you could then have trouble if your program uses any data types that require stronger alignment.

According to the gcc manual:

On Pentium and PentiumPro, double and long double values should be aligned to an 8 byte boundary (see -malign-double) or suffer significant run time performance penalties. On Pentium III, the Streaming SIMD Extension (SSE) data type __m128 may not work properly if it is not 16 byte aligned.

To ensure proper alignment of this values on the stack, the stack boundary must be as aligned as that required by any value stored on the stack. Further, every function must be generated such that it keeps the stack aligned. Thus calling a function compiled with a higher preferred stack boundary from a function compiled with a lower preferred stack boundary will most likely misalign the stack. It is recommended that libraries that use callbacks always use the default setting.

This extra alignment does consume extra stack space, and generally increases code size. Code that is sensitive to stack space usage, such as embedded systems and operating system kernels, may want to reduce the preferred alignment to -mpreferred-stack-boundary=2.

The lea loads the original stack pointer (from before the call to main) into ecx, since the stack pointer is about to modified. This is used for two purposes:

  1. to access the arguments to the main function, since they are relative to the original stack pointer
  2. to restore the stack pointer to its original value when returning from main
7
ответ дан 30 November 2019 в 11:37
поделиться
Другие вопросы по тегам:

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