如何将MIPS翻译成C以及如何减少MIPS指令?

发布于 2025-01-15 20:17:13 字数 275 浏览 1 评论 0原文

假设f、g、h、i分别存储在$s0~$s4中,数组A和B的基地址在$S6和$S7中。

sll  $t0, $s0, 2
add $t0, $s6, $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0)
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)

我对MIPS不熟悉,所以我想知道如何将MIPS翻译成C以及如何最小化这些MIPS指令?

Supposing that f, g, h, i are stored in $s0~$s4 respectively and the base addresses of arrays A and B are in $S6 and $S7.

sll  $t0, $s0, 2
add $t0, $s6, $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0)
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)

I'm not familiar with MIPS so I Wonder how to translate MIPS into C and how to minimize these MIPS instructions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

-小熊_ 2025-01-22 20:17:13

如何将 MIPS 翻译成 C

您认识这些模式,这里用于数组索引/数组元素访问。

在字节可寻址机器(所有现代硬件)上,4 字节整数在内存中占用 4 个字节,并且每个字节都有一个唯一的内存地址。由于硬件的工作方式,我们只使用这 4 个地址之一来引用整个 4 字节整数,即我们使用 4 个地址中的最低地址。给定一个地址(最低地址),硬件可以从内存中加载 4 字节整数。

由于内存中每个 4 字节整数占用 4 个地址,因此在 4 字节整数数组中,第一个元素的内存地址和第二个元素的内存地址相距 4 个地址,即使是连续的索引位置(即它们是仅相隔 1 个索引位置)。

索引 4 字节整数数组的公式是将索引转换为字节偏移量,然后将字节偏移量添加到数组的基地址。第一部分:将索引转换为字节偏移量,有时称为“缩放”。缩放概念上是通过乘法完成的,因此在 A[i] 中,i 需要按 A 的数组元素的大小进行缩放。  如果是 4 字节整数,则意味着将索引缩放(乘以)4。一种快速的方法是移动 2 位位置,这与乘以 4 具有相同的效果。C

语言在进行数组引用时自动缩放,而汇编语言则需要显式缩放。 C 可以做到这一点,因为它知道数组的类型,而汇编语言则不知道。

在 C 中,我们可以执行类似 A[i] 的表达式。 C 语言允许我们将其分解为 *(A+i),它将指针算术加法 A+i 与该和的解引用分开,解引用为一元间接运算符,*。如前所述,C 会自动缩放,因此 A+i 相当于 A+i*4,其中我们可以用移位代替乘法:A+(我<<2)

接下来,我们需要知道取消引用是用于读取还是用于写入。当访问 A[i] 的值时,我们将在赋值运算符的“右侧”看到它,如 ... = A[i]. 当 A[i] 访问更新/存储值时,我们将在赋值运算符的左侧看到它,如 A[i] = ...

因此,在 C 中执行 A[i] 读取(右侧)的顺序在汇编中如下:

sll $temp1, $i, 2
addu $temp2, $A, $temp1
lw $temp3, 0($temp1)

其中 $tempN 是选择用于保存中间值的某个寄存器(通常是指定的临时寄存器) . 由于完成任何操作都需要多条指令,因此指令序列与保存中间状态的寄存器互连。而且,在汇编中我们命名寄存器,而不是变量,所以在我上面的 $i$A 中应该是代表这些变量的寄存器名称,而不是直接使用的变量名称。

写入/存储数组访问的模式类似,但以 sw 指令结尾,以将某些值存储到索引位置的内存中。

这些指令序列通过使用这些寄存器互连,并且这些序列可以被其他指令打断或散布——那么我们必须遵循上面的模式,注意互连它们的寄存器用法而不是特定的序列。

在您的示例代码中:

sll  $t0, $s0, 2     # sourcing an index in $s0, scaling it into temp $t0
add $t0, $s6, $t0    # adding a base array in $s6, putting back into $t0
sll $tl, $sl, 2      
add $tl, $s7, $tl
lw $s0, 0($t0)       # accessing the value of $s6[$s0*4], aka A[f]
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)

我们可以看到对 $s0 中的索引和 $s6 中的数组进行读取访问的模式,我们被告知,这些映射到fA,因此这三个指令包含 A[f],用于从索引 A 处读取值代码>f。

其余的类似即可。您的工作是使用这些知识来查找上述序列中的其他数组索引模式。了解如何使用数组索引操作的结果,您将获得完整的 C 代码。


请注意,当指针算术应使用 adduaddiu 时,您收到的示例错误地使用了 addaddi - 我们不希望对指针算术进行有符号整数溢出检查,因为指针是无符号的。

其中一条 add 指令不适用于指针算术,但如果打算在 C 中复制,则可能仍应使用 addu,因为 C 语言没有内置运算符可捕获溢出。

how to translate MIPS into C

You recognize the patterns, here for array indexing / array element access.

On a byte addressable machine (all modern hardware), a 4-byte integer occupies 4 bytes in memory, and each of those bytes has a unique memory address.  Because of the way the hardware works, we only use one of those 4 addresses to refer to the whole 4-byte integer, namely we use the lowest address among the 4.  The hardware can load a 4-byte integer from memory given that one address (the lowest).

Since each 4-byte integer in memory occupies 4 addresses, in an array of 4-byte integers, the memory address of the first element and the memory address of the second element are 4 addresses apart even though are sequential index positions (i.e. they are only 1 index position apart).

The formula for indexing a 4-byte integer array, then is to convert the index into a byte offset, then add the byte offset to the base address of the array.  The first part of that: converting an index to a byte offset, is sometimes referred to as "scaling".  Scaling is conceptually done by multiplication, so in A[i], i needs to be scaled by the size of the array elements of A.  If 4-byte integers that means scaling (multiplying) the index by 4.  A quick way of doing that is shifting by 2 bit positions, which has the same effect as multiplying by 4.

The C language automatically scales when doing array references, whereas assembly language requires explicit scaling.  C can do this because it knows the type of the array, whereas assembly language does not.

In C we can do expressions like A[i].  The C language allows us to break that down somewhat into *(A+i), which separates the pointer arithmetic addition A+i from the dereferencing of that sum, dereferencing with the unary indirection operator, *.  As previously mentioned, C automatically scales, so A+i becomes the equivalent of A+i*4, in which we can substitute shifting for multiplication: A+(i<<2).

Next, we need to know if the dereference is for read or for write.  When A[i] is accessed for its value, we will see it on what we call the "right hand side" of an assignment operator, as in ... = A[i].  When A[i] is access to update/store a value, we will see it on what we call the left hand side of an assignment operator, as in A[i] = ....

So, the sequence for doing A[i] for read (right hand side) in C is the following in assembly:

sll $temp1, $i, 2
addu $temp2, $A, $temp1
lw $temp3, 0($temp1)

Where $tempN is some register (usually a designated temporary) chosen to hold an intermediate value.  Since multiple instructions are needed to accomplish anything, sequences of instructions are interconnected with registers that hold the intermediate states.  And also, in assembly we name registers, not variables, so in my above $i and $A should be a registers names representing those variables rather than variable names directly used.

The pattern for write/store array access is similar but ends with a sw instruction instead, to store some value into memory at the index position.

These instruction sequence are interconnected by the use of these registers, and the sequences can be interrupted or interspersed with other instructions — what we have to follow then is the above pattern by paying attention to to the register usages that interconnect them rather than the specific sequences.

In your sample code:

sll  $t0, $s0, 2     # sourcing an index in $s0, scaling it into temp $t0
add $t0, $s6, $t0    # adding a base array in $s6, putting back into $t0
sll $tl, $sl, 2      
add $tl, $s7, $tl
lw $s0, 0($t0)       # accessing the value of $s6[$s0*4], aka A[f]
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)

We can see the pattern for a read access to an index in $s0, and an array in $s6, these, we are told, map to f and A, so those three instructions comprise A[f] to read a value from A at index f.

The rest are done similarly.  Your job is to use this knowledge to find the other array indexing patterns in the above sequence.  Find out how the results of the array indexing operations are used and you'll have the complete C code.


NOTE that the sample you've been given incorrectly uses add and addi when pointer arithmetic should use addu and addiu — we don't want signed integer overflow checking on pointer arithmetic, as pointers are unsigned.

One of the add instructions is not for pointer arithmetic, but should probably still have used addu if this is intended to be replicated in C, because the C language does not have a built in operator to trap on overflow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文