2016-04-24 40 views
5

我目前正在学习汇编和C编程语言,我有几个关于它的问题。组装与C代码比较

C代码

int arith(int x, int y, int z) { 
    int t1 = x + y; 
    int t2 = z*48; 
    int t3 = t1 & 0xFFFF; 
    int t4 = t2 * t3; 
    return t4; 
} 

汇编代码

movl 16(%ebp),%eax   z 
leal (%eax,%eax,2), %eax z*3 
sall $4,%eax    t2 = z*48 
movl 12(%ebp),%edx   y 
addl 8(%ebp),%edx   t1 = x+y 
andl $65535,%edx   t3 = t1&0xFFFF 
imull %edx,%eax    Return t4 = t2*t3 

而不是使用莱亚尔,然后由4移动由48乘Z,可我只是用imull $ 48,%EAX的?

此外,这是多次使用%edx寄存器。这是否意味着t1被覆盖?换句话说,如果我愿意,我还能在t4之前检索t1吗?

+1

是,否(变量't1'被优化掉),并且编号。对于最后一个问题'x + y'是计算出来的,但是从未保存过。 _EDX_在'addl 8(%ebp),%edx'后面具有值'x + y',但是指令'andl $ 65535,%edx'破坏了它。如果您在'addl 8(%ebp),%edx'之后将_EDX_移动到像_ECX_这样的寄存器,那么您仍然可以访问计算的x + y部分。 –

+0

很酷。 C代码是否真的会在幕后翻译,以至于它不会将每个变量存储到它自己的寄存器中? – Dylan

+2

如果不需要它,则不需要。这是优化编译器的功能。 – usr2564301

回答

2

尝试将程序集与您的代码逐行匹配可能不是解决此问题的最佳方法。编译器进行了几次优化,以使程序尽可能高效地运行,这就是为什么您可能会注意到代码之间存在一些不一致的原因。

为了回答你的第一个问题,从技术上讲,这可以起作用,但编译器再一次做了几次优化。所以虽然使用imul看起来更直观,但编译器确定leal和sall更有效率。 编辑:我只想指出,在可能的情况下,移位操作符几乎总是用来代替imul。对于CPU而言,位移要便宜得多,因为它实际上只是移位位值,而不是尝试执行一些可能需要更多CPU时间的数学运算。

现在关于“覆盖”t1。程序集没有任何有关程序变量的信息 - 它只知道它需要对某些值执行一些操作。虽然程序集有可能使用4个不同的寄存器来存储t1-4,但编译器确定这是不必要的,并且对于所有的值只需要2个寄存器。如果你仔细想想,这应该是有道理的。你的功能可以简化为几行代码。很明显,这不是一个好主意,因为这会让人无法阅读,但是汇编不一定被设计为“可读”。如果您在返回t4之前回到程序并执行了其他一些t1操作,则可能会注意到您的程序集与以前不同,并且可能正在使用另一个寄存器,具体取决于值的使用方式。

如果您真的想在汇编中使用您的程序的准系统版本,请使用-Og标志编译来关闭编译器优化。它可能仍然不完全符合您的代码,但它可能会使您更容易理解正在发生的事情。

+1

谢谢。你所说的一切都对我有意义。这很酷,它如何在这样的幕后进行优化。 – Dylan

+1

在这个[Agner Fog文档](http://www.agner。组织/优化/ instruction_tables.pdf)。根据LEAL的架构,甚至可能无法到达ALU。在一些x86架构上,它是作为AGU的一部分完成的。 –