Проблемы с пониманием вывода сборки gcc - PullRequest
1 голос
/ 24 мая 2009

При написании кода на C я решил скомпилировать его в ассемблер и прочитать его - я просто время от времени делаю это - своего рода упражнение, чтобы заставить меня думать о том, что машина делает каждый раз Я пишу заявление на языке C.

В любом случае, я написал эти две строки на C

asm(";move old_string[i] to new_string[x]");
new_string[x] = old_string[i];
asm(";shift old_string[i+1] into new_string[x]");
new_string[x] |= old_string[i + 1] << 8;

(old_string - это массив char, а new_string - это массив unsigned short, поэтому, учитывая два символа, 42 и 43, это поместит 4342 в new_string[x])
Который дал следующий результат:

#move old_string[i] to new_string[x]

movl    -20(%ebp), %esi         #put address of first char of old_string in esi
movsbw  (%edi,%esi),%dx         #put first char into dx
movw    %dx, (%ecx,%ebx,2)      #put first char into new_string

#shift old_string[i+1] into new_string[x]

movsbl  1(%esi,%edi),%eax       #put old_string[i+1] into eax
sall    $8, %eax                #shift it left by 8 bits
orl     %edx, %eax              #or edx into it
movw    %ax, (%ecx,%ebx,2)      #?

(Я комментирую это сам, чтобы я мог следить за тем, что происходит). Я скомпилировал его с -O3, так что я также мог видеть, как компилятор оптимизирует определенные конструкции. В любом случае, я уверен, что это, вероятно, просто, но вот что я не понимаю:

первый раздел копирует char из old_string[i], а затем перемещает его (от dx) до (%ecx,%ebx). Затем следующий раздел копирует old_string[i+1], сдвигает его или упорядочивает, а затем помещает в то же место из ax. Он помещает два 16-битных значения в одно и то же место? Разве это не сработает?

Кроме того, он смещает old_string[i+1] к старшему мечу eax, затем или edx (new_string[x]) в него ... затем помещает ax в память! Не будет ли ax просто содержать то, что уже было в new_string[x]? таким образом, он сохраняет одну и ту же вещь в одном и том же месте в памяти дважды?

Есть что-то, что я пропускаю? Кроме того, я совершенно уверен, что остальная часть скомпилированной программы не имеет отношения к этому фрагменту ... Я прочитал до и после, чтобы найти, где хранится каждый массив и различные переменные, и каковы значения регистров было бы при достижении этого кода - я думаю, что это единственная часть сборки, которая имеет значение для этих строк C.

- о, оказывается, комментарии сборки GNU начинаются с #.

Ответы [ 3 ]

1 голос
/ 24 мая 2009

Хорошо, все было довольно просто. Я разобрался с ручкой и бумагой, записал каждый шаг, то, что он делал с каждым регистром, а затем записал содержимое каждого регистра, учитывая начальное начальное значение ...

Что меня поразило, так это то, что он использовал 32-битные и 16-битные регистры для 16- и 8-битных типов данных ... Вот что я думал, что происходило:

  • первое значение, введенное в память, скажем, 0001 (я думал 01).
  • второе значение (02), загруженное в 32-битный регистр (так было похоже, 00000002, я думал, 0002)
  • второе значение сдвинуто влево на 8 бит (00000200, я думал, 0200)
  • первое значение (0000001, я думал 0001) xor'd во второе значение (00000201, я думал 0201)
  • 16-битный регистр помещен в память (0201, я думал, просто снова 01).

Я не понял, почему он записал его в память дважды, или почему он использовал 32-битные регистры (ну, на самом деле, я предполагаю, что 32-битный процессор намного быстрее работает с 32-битными значениями, чем с 8- и 16-битными значениями, но это абсолютно необразованное предположение), поэтому я попытался переписать его:

movl -20(%ebp), %esi       #gets pointer to old_string
movsbw (%edi,%esi),%dx     #old_string[i] -> dx (0001)
movsbw 1(%edi,%esi),%ax    #old_string[i + 1] -> ax (0002)
salw $8, %ax               #shift ax left (0200)
orw %dx, %ax               #or dx into ax (0201)
movw %ax,(%ecx,%ebx,2)     #doesn't write to memory until end

Это работало точно так же.

Я не знаю, является ли это оптимизацией или нет (если не считать записи одной памяти, что, очевидно, так и есть), но если это так, я знаю, что это на самом деле не стоит и не принесло мне ничего. В любом случае, я понимаю, что сейчас делает этот код, спасибо за помощь всем.

0 голосов
/ 24 мая 2009

Кроме того, он сдвигает old_string [i + 1] к старшему мечу eax, затем ors edx (new_string [x]) в него ... затем кладет топор в память! не было бы топор просто содержит то, что уже было в new_string [x]? так что сохраняет то же самое вещь в одном и том же месте в памяти дважды?

Теперь вы понимаете, почему оптимизаторы - это хорошо. Этот вид избыточного кода довольно часто обнаруживается в неоптимизированном сгенерированном коде, потому что сгенерированный код более или менее поступает из шаблонов, которые «не знают», что произошло до или после.

0 голосов
/ 24 мая 2009

Я не уверен, что не понять, если я что-то упустил.

Первые 3 инструкции загружают байт из old_string в dx и сохраняют его в вашей new_string.

Следующие 3 инструкции используют то, что уже есть в dx, и комбинируют old_string [i + 1] с ним и сохраняют его как 16-битное значение (ax) для new_string.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...