【问题标题】:Is $s necessary when implementing a function that swaps while looping through a loop in MIPS?实现在 MIPS 中循环时进行交换的函数时是否需要 $s?
【发布时间】:2021-11-29 01:01:46
【问题描述】:
void sort (int v[], int n)
{
    int i, j;
    for (i = 0;  i<n; i+=1){
        for(j=i-1; j>=0; && v[j]>v[j+1]; j-=1){
            swap(v,j);
            }
        }
    }
    
void swap(int v[], int k)
    {
        int temp;
        temp = v[k+1];
        v[k+1]= temp;
    }

下面是交换函数的MIPS代码

swap : sll $t1, $a0, 2   // reg $t = k*4 
       add $t1, $a0, $t1 // reg $t1 = v+(k*4)
                         // reg $t1 has the address of v[k]
       lw  $t0, 0($t1)   // reg $t0 (temp) =v[k]
       lw  $t2, 4($t1)   // reg $t2 = v[k+1]
                         // refers to next element of v
       sw  $t2, 0($t1)   // v[k] = reg $t2
       sw  $t0, 4($t1)   // v[k+1] = reg $t0 (temp)
     
       jr  $ra           // return to calling routine

我正在学习计算机体系结构。通过几个过程,我们了解到不应该改变的值是使用堆栈指针存储的,或者存储在$S中。

但是,在上面的代码中,sort函数的参数v[]($a0)和n($a1)使用堆栈指针存储在$s0$s1中,并且在运行之前最里面的循环,$s0 in $a0,数据显示为将$s1的值赋给$a1

但是,在上面的代码中,v[] 必须在 swap 函数内部保持更改后的状态,而j 也必须在循环时更改值,因此无需保存和放入之前的值。

我的解释正确吗?即在MIPS中实现上述代码时,使用栈指针不存储在$s中是没有问题的?

【问题讨论】:

    标签: mips cpu-registers preserve


    【解决方案1】:

    在 MIPS 中实现一个在循环中进行交换的函数时是否需要 $s?

    $s 寄存器从不需要。必要的是vnij 的变量值在对swap 的函数调用中仍然存在。有两种方法可以使值在函数调用中存活下来,一种是使用(堆栈)内存,另一种是使用$s 寄存器,因此,$s 寄存器完全不是必需的。 (两种形式都涉及堆栈内存(并且数量相同),但前者直接将堆栈内存用于变量值,而后者使用堆栈内存来保存 $s 寄存器的原始值)。

    但是,$s 寄存器更适用于具有多种用途的变量(通过运行时的指令执行动态测量,而不是在代码列表中看到的静态测量),并且此类变量也必须在函数调用中存活。 (当然,数组不能存在于$s 寄存器中,但是对数组的引用(指针变量)可以。)

    为什么这些变量必须在函数调用中存活下来?因为它们在调用之后都被消耗(使用,来源),尽管在函数调用之前已经定义(设置,目标)。无论函数调用是否发生,程序都希望其变量保持连续性。

    让我们指出高级语言和伪代码使用的逻辑变量与机器代码的物理存储之间的区别。

    逻辑变量具有名称、类型、作用域/生命周期(它们来来去去,有些在程序执行期间经常出现,作为局部变量和参数)。有无数种可能的名称——只需创建一个我们喜欢的新名称(也可以不命名,就像堆数据结构一样)。使用逻辑变量的程序具有连续性的期望——一旦设置,变量将保留其值,直到被程序更改(然后保留该新值)。

    机器的物理存储是永久的和全局的,只是比特组。 CPU 寄存器总是存在的,内存也是如此(让我们忽略虚拟化)。 CPU 看不到变量、它们的声明、它们的名称、它们的类型或它们的生命周期——CPU 看到的是物理存储。 (那么它是如何工作的呢?通过机器指令告诉 CPU 如何与物理存储交互;它会根据需要在每次运行机器代码指令时被告知。)

    将高级语言程序翻译成机器代码的部分工作是管理程序逻辑变量到硬件物理存储的映射。由于物理存储有限制,机器代码程序必须在其中工作以进行这些映射。

    有时必须将逻辑变量重新定位到替代物理存储(例如,从$a 寄存器到$s 寄存器或堆栈内存)。执行这种重定位通常需要机器代码指令(或序列),此外,编译器或汇编程序员必须意识到从变量到存储的映射在同一程序的不同点可能不同。

    (有时,逻辑变量的值实际上位于多个物理存储位置,例如在操作内存中的全局变量并将副本带入 CPU 寄存器进行处理时。数组元素也是如此。 )

    【讨论】:

    • 所有 4 个变量都需要跨越 swap 函数调用的连续性。如果i 的值因调用swap 而丢失,那么外循环将无法正常运行,对吗?因此,i 需要调用保留存储,可以是$s 寄存器或堆栈内存。如果i 仅映射到$t 寄存器,则该寄存器可能会被swap 清除,这对于i 变量和外部循环将是灾难性的。
    • 这些变量需要在函数调用中存活下来。这里的所有都是它的。他们应该使用支持这一点的物理存储。由于所有机器代码都可以读取和写入所有寄存器,因此我们需要对寄存器进行一些考虑。
    • 假装在更一般的情况下,我们不知道swap 是如何工作的:我们看不到它的机器代码(即使我们可以看到它可能会因作者的错误而改变在日常维护中进行修复或一些此类)。 swap 可以做什么(或有能力)?清除任何$t$a 寄存器?是的,调用约定允许这样做。
    • 好的,现在如果您确切知道swap 是如何实现的,您可以在sortswap 之间进行非标准和非冲突的寄存器用法,这没有错,但这意味着如果swap 的内部细节发生变化,我们将不得不重新审视sort 的内部寄存器使用情况
    • 因为这在大型程序中是一个重要的维护问题,所以我们提倡使用标准调用约定,该约定旨在最大限度地减少函数之间对函数签名的耦合要求。如果您使用标准调用约定,那么只有当您向swap 添加(或删除)参数(更改签名)时,才需要重新访问sort。构建系统旨在支持这一点,例如,通过检测具有前向函数声明的文件已更改。
    猜你喜欢
    • 1970-01-01
    • 2013-02-07
    • 1970-01-01
    • 2014-05-10
    • 2019-11-22
    • 2016-10-09
    • 2012-01-06
    • 2021-04-24
    • 2011-02-05
    相关资源
    最近更新 更多