Haskell 调用堆栈管理

Haskell 调用堆栈管理,haskell,compilation,callstack,Haskell,Compilation,Callstack,这并不是一个真正的哈斯克尔问题,但我想有很多人跟随哈斯克尔标签,他们会知道这个问题的答案 我目前正试图让我的大脑思考如何将函数的输入和输出、局部变量和临时值都保存在同一个堆栈上,但在退出时得到正确的堆栈布局 通常,堆栈提供推拉操作,仅修改最顶层的堆栈项。有时,还允许对堆栈下的项目进行随机读取访问。(这解释了如何将变量保存在那里并随机访问它们。)但通常更改堆栈大小的唯一方法是从堆栈中提取项。如果您想将项目保留在堆栈顶部并删除其下方的内容,那么这并没有多大好处 真正的编译器是如何做到这一点的?调用堆

这并不是一个真正的哈斯克尔问题,但我想有很多人跟随哈斯克尔标签,他们会知道这个问题的答案

我目前正试图让我的大脑思考如何将函数的输入和输出、局部变量和临时值都保存在同一个堆栈上,但在退出时得到正确的堆栈布局

通常,堆栈提供推拉操作,仅修改最顶层的堆栈项。有时,还允许对堆栈下的项目进行随机读取访问。(这解释了如何将变量保存在那里并随机访问它们。)但通常更改堆栈大小的唯一方法是从堆栈中提取项。如果您想将项目保留在堆栈顶部并删除其下方的内容,那么这并没有多大好处

真正的编译器是如何做到这一点的?

调用堆栈框架可以在参数所在的空间之前为返回值保留空间,然后是局部变量。这就是为什么知道类型有助于决定保留空间的大小。或者,在像Lisp这样的动态语言中,指向装箱值的指针会出现在那里。它只是特定函数调用协议的一部分

顺便说一句,带有闭包的语言中的运行时堆栈通常是一棵树(请参阅“Funarg问题”)。

调用堆栈框架可以在参数所在的空间之前为返回值保留空间,然后是局部变量。这就是为什么知道类型有助于决定保留空间的大小。或者,在像Lisp这样的动态语言中,指向装箱值的指针会出现在那里。它只是特定函数调用协议的一部分


顺便说一句,在带有闭包的语言中,运行时堆栈通常是一棵树(请参阅“Funarg问题”)。

我不能直接与Haskell交谈,但一般来说,应用程序二进制接口(或ABI)的工作是指定如何向函数传递数据和从函数传递数据(称为调用约定)。根据调用约定和数据大小,您提到的部分或全部内容可能存储在寄存器(甚至全局)中,而不是存储在堆栈中。此外,调用函数的返回地址和可能的某些处理器状态也将存储在堆栈上

就拿这个例子来说。堆栈布局:


在这种调用约定中,返回值通常存储在寄存器中,我不能直接与Haskell对话,但一般来说,应用程序二进制接口(或ABI)的工作是指定如何向函数传递数据(称为调用约定)。根据调用约定和数据大小,您提到的部分或全部内容可能存储在寄存器(甚至全局)中,而不是存储在堆栈中。此外,调用函数的返回地址和可能的某些处理器状态也将存储在堆栈上

就拿这个例子来说。堆栈布局:


在这种调用约定中,返回值通常存储在寄存器中

在什么情况下需要从调用堆栈中删除不在顶部的内容?@sepp2k通常将参数推到堆栈上,调用函数,当它返回时,结果是堆栈上唯一的内容。如果你有一堆参数,然后是一堆局部变量,最后你把结果建立在堆栈的最顶端,在返回之前,您需要以某种方式丢弃它下面的所有内容…在什么情况下需要从调用堆栈中删除不在顶部的内容?@sepp2k通常您将参数推到堆栈上,调用函数,当它返回时,结果是堆栈上唯一的内容。好吧,如果你有一堆参数,然后是一堆局部变量,最后你在堆栈的最顶端建立了结果,你需要在返回之前抛开它下面的所有东西…所以你在推其他东西之前为返回值保留空间,作为回报,除了结果,你什么都不做?@MathematicalArchid我想这会管用的,是的。堆栈上的放置顺序不必与实体启动的时间顺序相对应。因此,在推送其他所有内容之前,您为返回值保留空间,而在返回时,您只需删除结果以外的所有内容?@MathematicalArchid我认为这会起作用,是的。堆栈上的放置顺序不必与实体启动的时间顺序相对应。