那些熟悉x86汇编编程的人非常习惯于典型的函数序言/结语:
push ebp ; Save old frame pointer.
mov ebp, esp ; Point frame pointer to top-of-stack.
sub esp, [size of local variables]
...
mov esp, ebp ; Restore frame pointer and remove stack space for locals.
pop ebp
ret
同样的代码序列也可以用ENTER
和LEAVE
指令来实现:
enter [size of local variables], 0
...
leave
ret
ENTER
指令的第二个操作数是 * 嵌套级别 *,它允许从被调用的函数访问多个父帧。
这在C中没有使用,因为没有嵌套函数;局部变量的作用域仅限于声明它们的函数。这种构造并不存在(尽管有时我希望它存在):
void func_a(void)
{
int a1 = 7;
void func_b(void)
{
printf("a1 = %d\n", a1); /* a1 inherited from func_a() */
}
func_b();
}
然而,Python * 确实 * 有这样的嵌套函数:
def func_a():
a1 = 7
def func_b():
print 'a1 = %d' % a1 # a1 inherited from func_a()
func_b()
当然,Python代码不会直接转换成x86机器码,因此无法(不太可能?)利用这条指令。
是否有编译到x86并提供嵌套函数的语言?是否有编译器会发出第二个操作数为非零的ENTER
指令?
英特尔在该嵌套级操作数上投入了非零的时间/金钱,基本上我只是好奇是否有人使用它:-)
参考文献:
7条答案
按热度按时间ohtdti5x1#
enter
在实践中被避免使用,因为它的性能非常差--请参阅"enter" vs "push ebp; mov ebp, esp; sub esp, imm" and "leave" vs "mov esp, ebp; pop ebp"中的答案。有一堆x86指令已经过时,但出于向后兼容的原因仍然被支持--enter
就是其中之一。(leave
是可以的,编译器很乐意发出它。在Python中实现完全通用的嵌套函数实际上是一个比简单地选择几个帧管理指令更有趣的问题--搜索“闭包转换”和“向上/向下funarg问题”,你会发现许多有趣的讨论。
请注意,x86最初是作为Pascal机器设计的,这就是为什么有指令支持嵌套函数(
enter
,leave
)、pascal
调用约定(被调用方从堆栈中弹出已知数量的参数)(ret K
)、边界检查(bound
)等等。zphenhs42#
正如Iwillnotexist Idonotexist所指出的,GCC * 确实 * 支持C中的嵌套函数,使用的语法与我上面所示的完全相同。
但是,它没有使用
ENTER
指令。相反,嵌套函数中使用的变量在局部变量区被分组在一起,指向该组的指针被传递给嵌套函数。有趣的是,这个“指向父变量的指针”是通过一个非标准机制传递的:在x64上,它在r10
中传递,而在x86(cdecl)上,它在ecx
中传递,这是为C++中的this
指针保留的(它不支持嵌套函数)。针对64位进行编译时生成以下代码(代码段):
objdump --no-show-raw-insn -d -Mintel
的输出这相当于更详细的内容,如下所示:
vql8enpb3#
我们的PARLANSE编译器(用于SMP x86上的细粒度并行程序)具有词法作用域。
PARLANSE试图生成很多很多小的并行计算粒度,然后将它们多路复用到线程上(每个CPU一个线程)。我们不想为每一个颗粒付出“大堆栈”的代价,因为我们有很多颗粒,我们也不想限制任何东西可以递归的深度。由于并行分叉,堆栈实际上是一个仙人掌堆栈。
每个过程在进入时都会构建一个词法显示,以便能够访问周围的词法作用域。我们考虑过使用ENTER指令,但出于两个原因决定不使用它:
因此,编译器会准确地计算出函数需要访问的词法作用域,并在函数prolog中生成MOV指令,复制父函数实际需要的显示部分,这通常是1或2对移动。
因此,与使用ENTER相比,我们在性能方面获得了两次优势。
IMHO,ENTER现在是那些遗留的CISC指令之一,这在它被定义的时候似乎是一个好主意,但是被RISC指令序列超越了,甚至Intel x86也优化了。
ma8fv8wu4#
我在使用Simics虚拟平台的Linux引导上做了一些指令计数统计,发现从来没有使用过ENTER。然而,在混合中有相当多的LEAVE指令。CALL和LEAVE之间几乎有1-1的相关性。这似乎证实了ENTER只是慢且昂贵,而LEAVE相当方便的想法。这是在2.6系列内核上测量的。
在4.4系列和3.14系列内核上进行的相同实验显示,LEAVE或ENTER的使用都为零。据推测,用于编译这些内核的较新gcc的gcc代码生成已经停止发出LEAVE(或者机器选项设置不同)。
cygmwpex5#
IMP 77(由爱丁堡大学开发)允许嵌套例程/函数。英特尔版本的编译器有时使用带有非零级别值的ENTER指令。
h43kikqp6#
显示IMP源代码和生成的机器代码的相应英特尔指令!主例程/程序%开始0000 C8 00 00 01 ENTER 0000,1
v7pvogib7#