Страница 1 из 1

Различие стека в Delphi and FPC

СообщениеДобавлено: 21.02.2011 17:28:52
Maxizar
Прочитал книгу «Использование ассемблера в Дельфи» автор: Гуйдо Гайбелс.
Но не совсем разобрался с мелочами, а именно со стэком. Если быть точным в различии стэков между Delphi and Free Pascal.
Везде пишут, что обращаться нужно по имения переменной а не через указания адреса типа [EBP-4], да это верно, но когда разбираешь пример по полочкам нужно проделать хотя бы раз именно так через [EBP-4] и хорошо представить себе как выглядит стэк.
Вот что я тестил:
Код: Выделить всё
function Test3Param2(Value1,Value2,Value3:Integer):Integer; pascal;
asm
  mov EAX, [EBP+8]
end;


Тестируем:
Код: Выделить всё
procedure TForm1.Button1Click(Sender: TObject);
begin
   Caption:=IntToStr(Test3Param2(1,2,3));
end;

Должны вернуть цифру 3. потому что стэк выглядит вот так:
Код: Выделить всё
Value1
Value2
Value3
Return Address //Адрес возврата
Previous EBP // Предыдущее значение EBP


В данном случае EBP=ESP и указывают они на Previous EBP, т.е если [EBP+8] заменить на [ESP+8], ничего не изменится. Для Delphi стэк выглядит именно так, в Free pascal. Нужно писать [ESP+12] чтобы вернуть 3, т.е в стэк записали что то еще, и он стал выглядеть так:

Код: Выделить всё
Value1
Value2
Value3
Return Address //Адрес возврата
Previous EBP // Предыдущее значение EBP (EBP указывает на этот адрес)
Что то еще    //STP указывают сюда..

Я правильно понял, но что там лежит? Почему такие различия?

Если переписать процедуру так, чтобы добавилась локальная переменная:
Код: Выделить всё
function Test3Param2(Value1,Value2,Value3:Integer):Integer; pascal;
var M:Integer;
asm
   mov M, $FF // M:=255;
   mov EAX, [ESP]
end;


То стэк будет выглядеть так:
Код: Выделить всё
Value1
Value2
Value3
Return Address //Адрес возврата
Previous EBP // Предыдущее значение EBP (EBP указывает на этот адрес)
M                  // ESP будет указывать на ячейку памяти в которой записана переменная M


Если провести тест, то вернем 255 и в Delphi и в FP.
Если хотим вернуть число 3 (Value3) то в Delphi число 3 лежит по адресу [ESP+12], в FP по адресу [ESP+16], если работать через EBP. То для Delphi и FP, 3 лежит по адресу [EBP+8]. Вывод, в FP опять что то поместили между EBP и верхушкой стэка (в данном случаем переменной М).

Объясните если не трудно. Просто очень хочется разобраться, но так чтобы было понимание и в Delphi и в Free Pascal.

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 18:22:48
pda
В Delphi "Stack frames" включено в проекте?
И что там в ассемблерном коде функции получается? Есть ли в начале инструкции?
Код: Выделить всё
push EBP
mov EBP, ESP

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 18:31:22
Sergei I. Gorelkin
Может сохранять регистры, может выравнивать стек. Самый простой способ - откомпилировать в ассемблерный текст (с ключами -Amasm -al -s) и посмотреть, что происходит.

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 19:49:14
Maxizar
pda писал(а):В Delphi "Stack frames" включено в проекте?

Вот что дает делфи 2009 с включенным Stack frames
Изображение

Вот что дает делфи 2009 с выключенным Stack frames:
Изображение
Разницы нету.

Но насколько я понял, что Delphi, то работает так, как и написано, а вот FPC нет... Вопрос как раз в подводных ракушках FPC.

Вот что дал вывод при помощи (-Amasm -al -s)
Оптимизация уровень 1:
Код: Выделить всё
; [53] end;
      push   ebp
      mov   ebp,esp
      sub   esp,8
; Var Value1 located at ebp+16
; Var Value2 located at ebp+12
; Var Value3 located at ebp+8
; Var $result located at ebp-4
; Var M located at ebp-8
@@l6:
; [50] mov M, $FF
      mov   dword ptr [ebp-8],255
@@l7:
; [51] mov EAX, [ESP+16]
      mov   eax,dword ptr [esp+16]
@@l8:
      leave
      ret   12
@@t6:
_CODE      ENDS

_CODE      SEGMENT   PARA PUBLIC USE32 'CODE'
   ALIGN 16
   PUBLIC   UNIT1_TFORM1_$__BUTTON1CLICK$TOBJECT
UNIT1_TFORM1_$__BUTTON1CLICK$TOBJECT:
; Temps allocated between ebp-56 and ebp-12
@@f3:
@@l9:
; [58] begin


Оптимизация уровень 2: На выходе вообще нет строк 44-53

Оптимизация уровень 3:

Код: Выделить всё
; [53] end;
      push   ebp
      mov   ebp,esp
      sub   esp,8
; Var Value1 located at ebp+16
; Var Value2 located at ebp+12
; Var Value3 located at ebp+8
; Var $result located at ebp-4
; Var M located at ebp-8
@@l6:
; [50] mov M, $FF
      mov   dword ptr [ebp-8],255
@@l7:
; [51] mov EAX, [ESP+16]
      mov   eax,dword ptr [esp+16]
@@l8:
      leave
      ret   12
@@t6:
_CODE      ENDS

_CODE      SEGMENT   PARA PUBLIC USE32 'CODE'
   ALIGN 16
   PUBLIC   UNIT1_TFORM1_$__BUTTON1CLICK$TOBJECT
UNIT1_TFORM1_$__BUTTON1CLICK$TOBJECT:
; Temps allocated between ebp-52 and ebp+0
@@f3:
@@l9:
; [58] begin


Собственно мой код по строкам:
Код: Выделить всё
44 -function Test3Param2(Value1,Value2,Value3:Integer):Integer;pascal; assembler;
var M:Integer;
//begin
{$ASMMODE intel}
   asm
   //push EBX
   mov M, $FF
   mov EAX, [ESP+16]
//   pop EBX
53 - end;

Ну собственно, то о чем я и говорил, между ESP и EBP помещено что то еще, зачем?, что? кто?...
Т.е у меня на Delphi стэк выглядит так:
Код: Выделить всё
Value1
Value2
Value3
Return Address //Адрес возврата
Previous EBP // Предыдущее значение EBP (EBP указывает на этот адрес)
M                  // ESP будет указывать на ячейку памяти в которой записана переменная M


Т.е у меня на FPC стэк выглядит так:
Код: Выделить всё
Value1
Value2
Value3
Return Address //Адрес возврата
Previous EBP // Предыдущее значение EBP (EBP указывает на этот адрес)
что то еще
M                  // ESP будет указывать на ячейку памяти в которой записана переменная M

Если внимательно прочитать вывод асма видим такую строчку:
Код: Выделить всё
; Var $result located at ebp-4

Т.е между переменной M(на которую указывает ESP) и EBP засунули $result, Это так и должно быть? если да то почему в Delphi этого нету? или я опять все напутал....

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 20:11:14
Odyssey
По поводу Delphi ничего не могу сказать, но по поводу передачи параметров в документации FPC я находил вот это. Вообще в Programmer's Guide более-менее расписаны особенности генерации кода.

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 20:29:24
Sergei I. Gorelkin
Если до адреса возврата содержимое регламентировано (должны быть параметры в определенной последовательности и формате, согласно типу вызова функции), то после адреса возврата там может быть вообще все что угодно. В данном случае, видимо, FPC забывает убрать ячейку, зарезервированную под результат.

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 21.02.2011 21:28:53
Maxizar
Хм... Спасибо за Ваши ответы.

Odyssey писал(а):в документации FPC я находил вот это. Вообще в Programmer's Guide более-менее расписаны особенности генерации кода.

Вот именно что раздел 6.7, дал инфу которую может понять тот, кто уже разбирал стэк по полочкам. Им что было лень привести пару примеров функций, и таблицу стэка, да я понимаю, скажут используй именя переменных. Да, но ведь нужно сначало понять, суть вещей, и потом с пониманием дела работать. Может кто то тыркнет, тех кто пишет документацию, если есть такие люди.... ведь одна из причин популярности языка - это доступность документации.

Даже если рассуждать так: если мы возвращаем результат через стэк, и результат можно получить обратившись по аддресу (см пост выше)
Код: Выделить всё
Var $result located at ebp-4

То получается обсурд, зачем лишнее значение в стэке, если результат вернули через EAX пример:
Код: Выделить всё
function Test3Param2(Value1,Value2,Value3:Integer):Integer;pascal; assembler;
var M:Integer;
{$ASMMODE intel}
   asm

   mov M, $FF
   mov EAX, [ESP+16] //Сохранили в EAX значение переменной Value3
   mov [EBP-4], EAX   //Записали в результ (на который находится ссылка в стэке см документацию и код который дал (с ключами -Amasm -al -s) )
   mov EAX, $F         //Записали в EAX число 15

   end; 

Процедура возврощает 15, т.е как и при соглашении Register, тогда зачем
Код: Выделить всё
Var $result located at ebp-4

Действительно FPC, что то забыл и это БАГ.
Хотя может кто то и знает ответ, но молчит бандит такой.. :evil:

Добавлено спустя 15 часов 24 минуты 7 секунд:
Сегодня, я проверил это дело еще раз, теперь смотрел честно в Delphi на стэк. В лазарусе смотрел на вывод асма как советовал Сергей Горелкин.
Вывод:
Стэк в делфи, выглядит так как и написанов во всех книжках, а именно для процедуры:
Код: Выделить всё
function Test3Param2(Value1,Value2,Value3:Integer):Integer; pascal;
var M:Integer;
   asm
   mov M, $FF
   mov EAX, [ESP+12]
   end;

Стэк такой:
Код: Выделить всё
Value1
Value2
Value3
Return Address // Адрес возврата
Previous EBP   // Предыдущее значение EBP (EBP указывает на этот адрес)
M                      // Переменная М, сюда указывает ESP


Для Lazarus, стэк выглядит так:
Код: Выделить всё
Value1
Value2
Value3
Return Address // Адрес возврата
Previous EBP   // Предыдущее значение EBP (EBP указывает на этот адрес)
$result               //Но судя по тестам, изменение этого значение ни к чему не приводт
M                      // Переменная М, сюда указывает ESP


Вследствие чего, получаем разные смещения для M относительно EBP
Delphi
Код: Выделить всё
M это [EBP-4]

Lazarus
Код: Выделить всё
M это [EBP-8]


И разные смещения относительно ESP скажем для Value3:
Delphi
Код: Выделить всё
Value3 это [ESP+12]

Lazarus
Код: Выделить всё
Value3 это [ESP-16]


Если изменения $result, ни к чему не приводят, т.е функция возвращает результат через регистр EAX. То это либо вообще не result (тот что в стэке) или это лишнее значение…
Вопрос:
Что это? Если это не результат функции.
Если же это Результат, то функция все равно вернет через EAX и это просто лишнее время, которое потрачено на размещение (получается что мусора).
Так это БАГ?, или что-то от нас (меня) скрыли?.

Re: Различие стэка в Delphi and FPC

СообщениеДобавлено: 22.02.2011 17:04:16
Kvasshtain
Я конечно в этом деле полный дилетант. Хоте немного и игрался с ASM-ом, но только по DOS. Но я считаю, что это банальный ЖУЧОК. Или точнее всего не жучок, а его кривое исправление :shock: . Т.е. они (разработчики) скорее всего чо то там сначала напутали, а потом исправили, но немножко криво. А может это какая то внутренняя особенность Лазарыча (ну типа что то вроде какая-то там дополнительная инфа по выполнению процедуры/функции в стек заносится, а чо за инфа и как она им используется, история типа умалчивает, или ищем не там где надо :| ). Вот мое ИМХО :roll: .