为何表达式`10 + 32`不使用栈而`52 + -10`却使用?
为什么
52 + -10编译时会用栈存储中间值,而10 + 32直接用寄存器? 核心原因是表达式结构差异和入门编译器的代码生成策略,并非后者“必须”用栈,只是简易实现的选择:
表达式结构的本质区别
10 + 32是两个直接的立即数做二元加法,语法树是简单的(+, 10, 32);而52 + -10的语法树是(+, 52, (-, 10))——这里的-10是一元负号操作的结果,属于一个独立的子表达式,不是原生的立即数(虽然值是固定的,但编译器的语法分析阶段会把它当成需要计算的节点)。入门编译器的代码生成逻辑
很多教材里的简易编译器不会做复杂的常量折叠优化,而是严格按照表达式树的层级生成代码:- 对于
10 + 32,编译器可以直接把其中一个立即数加载到寄存器,再加上另一个立即数,比如AT&T汇编:
甚至可以直接计算出结果42,生成movl $32, %eax addl $10, %eaxmovl $42, %eax(如果做了常量折叠)。 - 对于
52 + -10,编译器会先处理子表达式-10:把10加载到寄存器,取负,然后如果此时没有空闲寄存器(入门实现可能只固定使用1-2个寄存器),就会把这个中间值tmp_0 = -10存到栈上,再加载52到寄存器,从栈中取出tmp_0完成加法,生成类似这样的代码:movl $10, %eax negl %eax ; 得到-10 pushq %rax ; 存入栈作为tmp_0 movl $52, %eax popq %rdx addl %edx, %eax
- 对于
这不是必须的——优化后的编译器会怎么做?
成熟的编译器(比如GCC、Clang)会做常量折叠,把-10直接识别为立即数$-10,此时52 + -10会生成和10+32一样的寄存器代码:movl $52, %eax addl $-10, %eax所以用栈存储中间值只是入门编译器的简化实现,并非x86-64架构的限制。
内容的提问来源于stack exchange,提问作者chez93
相关产品推荐
相关产品推荐

