【问题标题】:Trouble understanding gcc's assembly output无法理解 gcc 的汇编输出
【发布时间】:2009-05-24 02:00:35
【问题描述】:

在编写一些 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_stringchar 的数组,new_stringunsigned 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 编译它,所以我也可以看到编译器如何优化某些结构。无论如何,我确信这可能很简单,但这是我不明白的:

第一部分从old_string[i] 中复制一个char,然后将它(从dx)复制到(%ecx,%ebx)。然后下一部分,复制old_string[i+1],移动它,ors 它,然后把它从ax 放到同一个地方。它将两个 16 位值放在同一个地方?这样不行吗?

此外,它会将old_string[i+1] 移动到eax 的高位双字,然后将edx (new_string[x]) 移入其中...然后将ax 放入内存! ax 不会只包含 new_string[x] 中已有的内容吗?所以它会将同样的东西保存到内存中的同一个地方两次?

我有什么遗漏吗?另外,我相当确定编译程序的其余部分与这个 sn-p 无关......我已经阅读了前后,以找到每个数组和不同变量的存储位置,以及寄存器的内容' 值将在到达该代码时出现——我认为这是对这些 C 行重要的程序集的唯一部分。

-- 哦,原来 GNU 汇编 cmets 是以 # 开头的。

【问题讨论】:

  • 我很确定你的意思是 old_string 是一个 char 数组,而不是一个指向 char 的指针数组。
  • 你在问为什么 -O3 没有优化更多的代码吗?
  • 不,我不明白它在做什么,但我现在明白了
  • 顺便说一句,你正在做这个练习真是太好了。
  • 非常感谢,我真的很想成为一名软件开发人员——但我想成为那种真正知道发生了什么的人,而不是对哪些代码满足哪些要求有模糊的了解。不懂这玩意就觉得自己在撒谎,还自称是“程序员”

标签: c gcc assembly


【解决方案1】:

好的,毕竟这很简单。 我用笔和纸算出来,写下每一步,它对每个寄存器做了什么,然后写下每个寄存器的内容,给定一个初始起始值......

让我感动的是它使用 32 位和 16 位寄存器来存储 16 位和 8 位数据类型... 这就是我认为正在发生的事情:

  • 将第一个值放入内存中,例如 0001(我想的是 01)。
  • 第二个值 (02) 加载到 32 位寄存器中(就像是,00000002,我在想,0002)
  • 第二个值左移 8 位(00000200,我在想,0200)
  • 第一个值(0000001,我认为是 0001)异或到第二个值(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

这完全一样。

我不知道这是否是一种优化(除了写出一个内存,这显然是),但如果是的话,我知道这并不值得,也没有给我任何好处。无论如何,我现在知道这段代码在做什么,谢谢大家的帮助。

【讨论】:

  • 查看 bdonian 对 Charlie Martin 的回答的评论——它会写入内存两次,以防 new_string[i] 碰巧引用与 old_string[i+1] 相同的内存位置(所谓的 别名)。您可以通过正确使用 C99 关键字“restrict”来消除冗余存储。
【解决方案2】:

除非我遗漏了什么,否则我不确定什么是不理解的。

前 3 条指令将 old_string 中的一个字节加载到 dx 中,并将其存储到你的 new_string。

接下来的 3 条指令利用 dx 中已有的内容并将 old_string[i+1] 与其结合,并将其作为 16 位值 (ax) 存储到 new_string。

【讨论】:

  • 两个部分都将一个 16 位值移动到内存中的同一位置: movw %dx, (%ecx,%ebx,2) 然后 movw %ax, (%ecx,%ebx,2) 两者都不ecx 或 ebx 在这些指令之间变化。
  • 是的,因为您的 C 代码两次存储到 new_string[x] - 我希望内存位置不会改变 :)
  • 我误解了正在移动的数据,因为使用 32 位和 16 位寄存器来存储 16 位和 8 位值。我错误地认为它将 ax 中相同的 8 位值存储到内存中相同的 8 位位置两次。这是数据大小的错误。
【解决方案3】:

此外,它会将 old_string[i+1] 移动到 eax 的高位 dword,然后 ors edx (new_string[x]) 放入其中...然后将 ax 放入内存中!不会 ax 只包含 new_string[x] 中已有的内容?所以它保存相同 两次内存中的同一个地方?

现在您明白为什么优化器是一件好事了。这种冗余代码经常出现在未优化的生成代码中,因为生成的代码或多或少来自不“知道”之前或之后发生了什么的模板。

【讨论】:

  • 这是使用 -O3 标志编译的。此外,冗余并不是我感到困惑的原因,而是它应该将 16 位值 01 移动到 32 位值,然后将 16 位值 02 移位和 or-ing 到相同的 32 位值,创建0201 在 32 位值中......但看起来它只是将 01 放入同一个位置两次,将 xx01 作为 32 位值。
  • 由于是 char* 和 short*,所以处理的是 8 位和 16 位的值,而不是 16 位和 32 位的值。
  • 但是 eax 是 32 位的,而 ax 是 16 位的,对吧?该代码使用 32 位和 16 位寄存器,而不是使用 ax 来处理 short 和 al 来处理 char。
  • 其实之所以做这种冗余存储是因为根据C的别名规则,两个数组可能会重叠(char*别名所有类型),所以编译器一定要保存new_string[i] 在取消引用 old_string[i+1] 之前。这可以通过适当地应用限制来避免。
  • 啊——虽然我正在编译 C89——但假设我使用 C99——其中一个数组是函数参数。这有什么改变吗?我切换到 C99 进行尝试,并在 new_string 上设置了限制(old_string 是此函数的参数)。它仍然进行了两次内存写入,当我尝试在 old_string 上加上“restrict”时,它说这是一个无效的使用。
猜你喜欢
  • 2011-08-25
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
相关资源
最近更新 更多