MIPS 中的递归函数如何工作?

2024-01-05

我是 MIPS 的新手(因为我开始为大学学习 MIPS 汇编),并且在理解 MIPS 中的递归函数如何工作方面遇到了问题。

例如,我有这个程序(用 C 语言)可以用 MIPS 编写:

int fact (int n)
{
   if (n < 1) return 0;
   else return n * fact(n - 1);
}

有人可以帮助我,用这个或另一个递归函数的例子,并向我解释它是如何工作的吗?


我想分享的第一件事是将其转换为 MIPS 的复杂性来自于单纯的函数调用的存在,而不是因为涉及递归 - 即fact是递归的,恕我直言,这是一个转移注意力的事情。为此,我将说明一个非递归函数,它具有您所说的递归函数的所有复杂性:

int fact (int n)
{
   if (n < 1) return 0;
   else return n * other(n - 1);    // I've changed the call to "fact" to function "other"
}

我的修改不再是递归的!然而,此版本的 MIPS 代码看起来与您的 MIPS 代码相同。fact(当然,例外的是jal fact这改变了jal other)。这是为了说明翻译 this 的复杂性是由于函数内的调用造成的,与调用者无关。 (虽然 YMMV 具有优化技术。)


要理解函数调用,您需要了解:

  • 程序计数器:程序如何与程序计数器交互,特别是在函数调用的上下文中。
  • 参数传递
  • 一般而言,寄存器约定

在 C 语言中,我们有显式参数。当然,这些显式参数也出现在汇编/机器语言中,但也有一些在机器代码中传递的参数在 C 代码中不可见。这些示例包括返回地址值和堆栈指针。


这里需要的是对函数的分析(与递归无关):

参数n将在$a0在函数入口处。的价值n函数调用后需要(到other),因为在该函数调用返回右侧操作数之前我们无法进行乘法运算*.

所以,n(左手操作数*) 必须在函数调用后继续存在other,并在$a0它不会——因为我们自己的代码将重新调整用途$a0为了打电话other(n-1), as n-1必须进入$a0为了那个原因。

另外,(在 C 中,隐式)参数$ra保存返回给调用者所需的返回地址值。致电给other同样,将重新调整用途$ra寄存器,清除其先前的值。

因此,这个函数(你的或我的)需要两个值才能在其体内的函数调用中幸存下来(例如调用other).

解决方案很简单:我们需要的值(存在于寄存器中,这些值被我们正在做的事情或被调用者可能做的事情重新利用或擦除)需要移动或复制到其他地方:在函数调用中将幸存的地方。

内存可以用于此目的,并且我们可以使用堆栈获取一些内存来实现这些目的。

基于此,我们需要创建一个堆栈帧,在调用后为我们需要的两件事提供空间(否则会被清除)other。入口$ra必须保存(并稍后重新加载)以便我们使用它返回;另外,最初的n需要保存值,以便我们可以将其用于乘法。 (堆栈帧通常在函数序言中创建,并在函数尾声中删除。)


正如机器代码(甚至一般编程)中经常出现的情况一样,还有其他处理事物的方法,尽管要点是相同的。 (这是一件好事,优化编译器通常会根据特定情况寻求最佳方法。)


递归的存在或不存在不会改变我们将其翻译成汇编/机器语言所需的基本分析。递归极大地增加了堆栈溢出的可能性,但不会改变此分析。


Addendum

需要明确的是,递归要求使用可动态扩展的调用堆栈 - 尽管所有现代计算机系统都提供这样的调用堆栈,因此在当今的系统上很容易忘记或掩盖这一要求。

对于没有递归的程序,调用堆栈不是必需的 - 局部变量可以分配给函数私有全局变量(包括返回地址),这是在某些较旧的系统上完成的,例如 PDP-8,它不提供特定的对调用堆栈的硬件支持。


使用堆栈内存传递参数和/或寄存器较差的系统可能不需要本答案中描述的分析,因为变量已经存储在嵌套函数调用后仍然存在的内存中。

正是现代寄存器丰富的机器上的寄存器分区产生了上述分析的要求。这些寄存器丰富的机器(大部分)在 CPU 寄存器中传递参数和返回值,这很高效,但有时需要进行复制,因为寄存器从一个函数重新用于另一个函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MIPS 中的递归函数如何工作? 的相关文章

  • Python,将迭代函数变成递归函数

    我创建了一个输出 4 3 2 1 0 1 2 3 4 的迭代函数 def bounce2 n s n for i in range n print n n n 1 if n lt 0 for i in range s 1 print n n
  • 具有默认值的参数不在 PsBoundParameters 中?

    通用代码 考虑这段代码 PS gt function Test param p default value PsBoundParameters PS gt Test some value Key Value p some value PS
  • 未对齐的内存访问

    我正在开发不支持未对齐内存访问的嵌入式设备 对于视频解码器 我必须处理 8x8 像素块中的像素 每个像素一个字节 该设备具有一些 SIMD 处理功能 使我能够并行处理 4 个字节 问题是 8x8 像素块不能保证从对齐的地址开始 并且函数需要
  • MySQL 在 select 语句中使用循环生成列

    在 MySQL 中 我有一个函数 它接受一个数字参数 并根据该数字从另一个表中吐出结果的子集 目前的实现如下 SELECT id date function do stuff with value 1 as t1 function do s
  • 通过另一个函数访问一个函数的返回值

    总的来说 我对编程还很陌生 刚刚开始真正接触Python 我正在做一个猜数字项目 import random def main main function print Welcome to the number guesser game r
  • 计算PE文件中入口点的文件偏移量

    In http en redinskala com finding the ep http en redinskala com finding the ep 有关于如何查找 exe 文件中入口点的文件偏移量的信息 在这里我可以读到 EP 文
  • “致命错误:已达到最大函数嵌套级别‘100’,正在中止!”的解决方案在 PHP 中

    我创建了一个函数 可以查找 html 文件中的所有 URL 并对链接到发现的 URL 的每个 html 内容重复相同的过程 该函数是递归的 可以无限地进行下去 但是 我通过设置一个全局变量来限制递归 这会导致递归在 100 次递归后停止 但
  • 对多维数组的键进行递归排序

    我很难尝试对多维数组的键进行递归排序 我尝试过usort 但没有成功 样本数据 first level gt dir 3 gt subdir 1 gt file 2 mp4 gt object name gt file 2 mp4 file
  • swift 中的延迟函数[重复]

    这个问题在这里已经有答案了 我没有可供采样的代码或任何东西 因为我不知道该怎么做 但是有人可以告诉我如何使用 swift 将函数延迟一定的时间吗 您可以使用 GCD 在示例中延迟 10 秒 Swift 2 let triggerTime I
  • 递归指数法

    public static int exponent int baseNum int temp baseNum baseNum return temp exponent baseNum 现在 如果我调试它 上面的方法会 n n 变成无穷大
  • 返回中断处理程序后程序计数器去了哪里?

    您好 我想知道当程序从中断服务程序返回时程序计数器去哪里 我知道当中断事件发生时PC被压入堆栈 但是下一个或同一个 刚刚执行的一个 被压入堆栈的地址是什么 当我们有 first instruction interrupt event her
  • 为什么两个函数有相同的地址?

    考虑这个函数模板 template
  • 修改字符数组,修改部分向后显示

    我刚刚开始学习汇编 我正在尝试修改字符数组 这是我的汇编代码 data data byte Five 0 code Asm proc lea rax data mov dword ptr rax Four ret Asm endp end
  • 缓存寻址:索引长度、块偏移、字节偏移和标记?

    假设我知道以下值 W Word length 32 bits S Cache size in words B Block size in words M Main memory size in words 如何计算需要多少位 Index B
  • 成员函数的Decltype

    class A int f int x int j return 2 decltype f p 给我错误 error decltype cannot resolve address of overloaded function 我不明白为什
  • 32 位 x86 汇编中堆栈对齐的职责

    我试图清楚地了解谁 调用者或被调用者 负责堆栈对齐 64 位汇编的情况相当清楚 它是由caller 请参阅系统 V AMD64 ABI 第 3 2 2 节栈帧 输入参数区域的末尾应按 16 对齐 32 如果 m256 在堆栈 字节边界上传递
  • 使用 32 位块对 1024 位 2 的补码进行有符号乘法

    因此 我对 1024 位数字有以下结构定义 我想在此处使用 2 的补码表示 并且我使用的是 32 位系统 typedef struct int1024 int32 t num 32 should I use uint32 t int1024
  • 如何在Python中求和

    我想知道如何在 python 中表示总和而不需要像这样的循环here http docs scipy org doc scipy reference tutorial optimize html 我们有 def rosen x The Ro
  • 我怎么知道PowerShell函数参数被省略了

    考虑这样的函数 function Test foo bar 我们可以称之为 Test foo null Test 我如何知道何时省略了 foo 以及何时为 null 如果您使用的是 Powershell V2 或更高版本 则可以使用 PSB
  • ESP 和 EBP 寄存器是什么?

    我发现ESP寄存器是当前堆栈指针 EBP是当前堆栈帧的基指针 但是 我不理解这些定义 我刚刚开始学习如何在汇编程序中编码 What I understand is that ESP points towards the stack itse

随机推荐

  • Ruby 中的枚举器作为无限生成器

    我正在阅读一份资源 解释如何将枚举器用作生成器 例如 triangular numbers Enumerator new do yielder number 0 count 1 loop do number count count 1 yi
  • Android Things:连接到 Raspberry Pi 3

    完全新手 我有一个树莓派并已将安卓事物磁盘映像并启动它 但我无法从Windows10台电脑运行安卓工作室 adb exe通过以太网还是USB 这Pi屏幕上有一个绿色和灰色的 androidthings 标志 但上面写着 未连接 如果我连接
  • 将布局从 networkx 传输到 cytoscape

    我想使用 networkx 生成图形的布局 是否可以将此布局转移到细胞景观 http www cytoscape org 并把它画在那里 我试图简单地写一个图表 import networkx as nx G nx Graph G add
  • 设置 WSO2 EMM

    我正在尝试设置 WSO2 EMM V2 0 1 我能够在我的实时服务器上进行设置并遵循此处提供的所有说明WSO2 入门 https docs wso2 com display EMM201 Getting Started直到我到达配置And
  • PHP读取txt文件并检查用户名密码是否存在

    我有一堆密码和用户名存储在一个 txt 文件中 我需要检查是否匹配 我知道这不是一个安全的方法 但它是为了学习的目的 此代码将用户名和密码写入 txt 文件 if isset POST register user POST username
  • 碲与硒:比较

    我使用硒有一段时间并且做得很好 我想尝试一下碲 搜索并发现只有很少的问题 我想了解以下内容 使用碲的主要优点是什么 Selenium Groovy 有什么不同 Tellurium 是 Selenium 的 DSL 领域特定语言 它是为了让
  • 如何将 javascript 设置样式属性返回到 CSS 默认值

    我正在尝试弄清楚如何在使用 javascript 更改样式属性后恢复为样式表中的值 包括单位 在下面的示例中 我希望输出为100px CSS 中的值 而不是10px as getComputedStyle gives 我还将虚拟 div 保
  • Linux 上的 Emacs/xterm 颜色烦恼

    我在本地 Linux 机器和远程集群的登录节点上的控制台窗口中使用 emacs 我经常使用 emacs 并且在 emacs 文件中将前景色设置为白色 如下所示 set foreground color white set backgroun
  • AppCompatDelegate 无法实例化自定义视图充气器 android.support.v7.app.AppCompatViewInflater

    当获取发布 apk 时 我在应用程序的所有活动中收到此错误日志 04 03 17 10 54 105 26527 26527 I AppCompatDelegate Failed to instantiate custom view inf
  • 不超过两个重复字母/数字的正则表达式

    我需要处理 XSL 文件中不超过两个相同字母 数字的正则表达式 no space 不支持特殊字符 支持 a z A Z 0 9 需要 a z 之一 需要 0 9 之一 不超过 2 个相同的字母 数字 即BBB将失败 BB被接受 到目前为止我
  • 是否可以在 WordPress 中测试空术语或类别?

    我有一个项目 要求我列出每个自定义帖子类型的可用术语 并通过 css javascript 直观地指示哪些术语 类别为空 有没有办法返回术语 类别列表并说向空的类别添加一个类 感谢您提供的所有帮助 就在这里 首先 您使用以下方式获取条款获取
  • 如何为 springdoc swagger-ui HTML 页面配置自定义 URL?

    将 springdoc openapi ui 依赖项添加到我的 Spring 项目 不是 Spring Boot 后 将生成 OpenAPI V3 文档 并且可以使用默认的 swagger ui 页面查看 localhost 8080 sw
  • 从 std::async 返回的 std::future 在超出范围时挂起

    我正在使用以下组合std async and std future from C 11 我用来对我在代码中执行的某个活动强制执行 time outmight我尝试连接到服务器时需要一些时间 代码如下 include
  • 当我在 PHP 中使用 session_regenerate_id(true) 时,session_destroy 会带来什么附加价值?

    我一直在阅读手册和网络上的各个页面 包括这里的很多问题 然而 我仍然无法理解这个概念session destroy 在 PHP 中与其他取消设置会话数据的方法结合使用 考虑这个网站从不注册会话变量之外的 SESSION超全局数组 sessi
  • 自动从 XML 模式创建 GUI

    我必须编写一个桌面应用程序来编辑 XML 文件中存储的数据 该格式由 XML 架构文件 xsd 定义 格式相当复杂 有没有可以自动生成基本GUI的工具 目前尚未决定使用哪种语言 我有使用 wxWidgets 的 Python 和 C 以及使
  • 为什么“npm run dev”不能在新的“npx create-next-app”上工作?

    我刚刚创建了一个新的 Next 应用程序npx create next app 看起来已经成功运行了 npx create next app 8 46 31 npx installed 1 in 8 826s What is your pr
  • 使用 LINQ 逐字读取文本文件

    我正在学习 LINQ 并且想使用 LINQ 逐字阅读文本文件 比如说电子书 这就是为什么我可以想出 static void Main string content File ReadAllLines text txt var query f
  • 处理 ASP.NET MVC 中的路由错误

    我知道如何设置自己的路由 但是如何处理路由表漏洞中的路由呢 我的意思是 我猜默认 controller action id 路线可能是一个通用的包罗万象的东西 但我不确定这是否是正确的方法 我喜欢让我的用户知道他们请求的数据 页面 不存在
  • 使用 JSONPath 过滤 JSON 文档中的属性

    我有一个任意定义的 JSON 文档 并且我希望能够应用JSONPath https goessner net articles JsonPath 类似于属性白名单过滤器的表达式 所有选定的节点和他们的祖先回到根节点保留 所有其他节点都被删除
  • MIPS 中的递归函数如何工作?

    我是 MIPS 的新手 因为我开始为大学学习 MIPS 汇编 并且在理解 MIPS 中的递归函数如何工作方面遇到了问题 例如 我有这个程序 用 C 语言 可以用 MIPS 编写 int fact int n if n lt 1 return