让我们分解这个语句:
m |= constant;
这在一个称为赋值运算符的类别中,这些扩展如下:
m = m | constant
^ ^
| |
| +--- source/read
|
+-- target/written
因此,虽然常规赋值 (=) 仅写入,但这些赋值运算符在左侧读取和写入目标表达式。 (col++ 也是一个读-修改-写操作,可以认为是col += 1 和col = col + 1)。理解后,这些赋值运算符既方便又简洁,而且还有助于我们不必重复复杂的表达式,例如您示例中的数组索引。
| 运算符代表logical or。在 C 中,此运算符将对两个输入的每一位执行逻辑或运算以产生相同大小的输出,因此对于 4 字节大的 int,将逐个位置执行 32 个单独的“或”运算。
与常量“或”用于设置相应位 - 常量中有 1 时,该位的输出将为 1,常量中有 0 时,该位的输出将是另一个输入的值(在那个位位置)。
这就是说:取m的当前值,然后在常量中进行OR运算,并将该值写入m的新当前值。
MIPS 有一个or 指令和ori 指令。
如果变量m 在寄存器中,这可以在一条指令中完成,假设该常量适合 16 位立即数形式(否则,程序员,在某些情况下是汇编程序,可能会将其转换为分成两个或三个指令,一个或两个额外的以显示完整的常量值,然后是 MIPS 或指令)。 or 指令将为m 提供和定位寄存器。
但是,如果变量 m 在内存中,那么,因为 MIPS 是一个加载/存储机器,我们必须将 m 的副本加载到寄存器中,然后执行 or 操作(如如上所述),然后将其存储回内存中以更新它所在的变量。
数组——更具体地说是它们的所有元素——必须在内存中,所以它们需要这种完整的处理。你已经有一个计算来获取元素的地址。
大多数人会使用移位:sll $t3, $t2, 2 乘以 4,因为这在低端硬件上可能更便宜。
不要忘记在开始时初始化row=0;,而不是依赖模拟器来清除寄存器。
(别忘了增加col++。)
作为一种优化,grid 的la 可以移到循环之外和之前,因为它产生的值在循环执行期间不会改变。 (这称为loop invariant code motion。)如果您这样做,只要仍然需要它,就不要在代码的另一部分破坏$t0。
在汇编中还有其他可能的优化,例如在外部循环而不是内部循环中进行第一次乘法(row 乘以列大小),因为在内部循环中,row 变量不会变化,因此对于内部循环的每次执行来说,这是一个常量值。
这些代码运动优化中的每一个都需要使用额外的 CPU 寄存器(在某个循环期间),这也是 MIPS 和其他较新的处理器有这么多寄存器的原因之一。
(进一步优化也可以通过从数组索引更改为指针,然后从重新计算元素的字节地址,到将该地址作为在整个循环中有效的变量来维护。)
一般建议坚持使用$t 寄存器,其中有10 个对于您的代码来说已经足够了。 ($s 寄存器在使用时应该被保存和恢复,它们由进行函数调用的代码使用,所以如果你的循环体有一个函数调用作为它的一部分或作为赋值语句的一部分,我们会为row 和col 选择$s 寄存器,以及我们希望在寄存器中并在函数调用中存活的任何其他内容。)