-1

我正在尝试将此 C 代码转换为 MIPS 程序集,但我不确定它是否正确。有人能帮我吗?请

问题:假设 a、b、i 和 j 的值分别在寄存器 $s0、$s1、$t0 和 $t1 中。此外,假设寄存器 $s2 保存数组 D 的基地址

C代码:

for(i=0; i<a; i++)
   for(j=0; j<b; j++)
       D[4*j] = i + j;

我在 MIPS ASSEMBLY 上的尝试

     add $t0, $t0, $zero   # i = 0
     add $t1, $t1, $zero   # j = 0
L1 : slt $t2, $t0, $s0     # i<a 
     beq $t2, $zero, EXIT  # if $t2 == 0, Exit
     add $t1, $zero, $zero # j=0
     addi $t0, $t0, 1      # i ++
L2 : slt $t3, $t1, $s1     # j<b 
     beq $t3, $zero, L1,   # if $t3 == 0, goto L1
     add $t4, $t0, $t1     # $t4 = i+j
     muli $t5, $t1, 4      # $t5 = $t1 * 4 
     sll $t5, $t5, 2       # $t5 << 2
     add $t5, $t5, $s2     # D + $t5
     sw $t4, $t5($s2)      # store word $t4 in addr $t5(D)
     addi $t0, $t1, 1      # j ++
     j L2                  # goto L2
EXIT :
4

2 回答 2

0

add $t0, $t0, $zero # i = 0 不,它保持$t0不变,保留它以前做过的任何垃圾。也许您打算使用addi $t0, $zero, 0?

此外,MIPS 没有 2 寄存器寻址模式(用于整数加载/存储),只有16-bit-constant ($reg). $t5($s2)是不合法的。您需要单独的addu指令,或者更好的是指针增量。

(您应该使用指针数学addu来代替add;如果地址计算从地址空间的低半部分跨越到高半部分,这不是错误。)


在 C 中,另一个线程在您编写对象时读取它是未定义的行为,因此我们可以优化外部循环的实际循环。 除非类型D_Atomic int *Dor volatile int *D,但问题中没有指定。

无论外部循环计数器如何,内部循环每次都写入相同的元素,因此我们可以优化外部循环,只进行最后的外部迭代,使用i = a-1. 除非a <= 0,那么我们必须跳过外部循环体,即什么也不做。

将除最后一个商店之外的所有位置优化到每个位置称为“死店消除”。早期外循环迭代中的存储是“死的”,因为它们被覆盖而没有读取它们的值。


您通常希望将循环条件放在循环的底部,因此循环分支就是一个bne $t0, $t1, top_of_loop例子。(MIPS 具有bne本机硬件指令;blt除非第二个寄存器是 ,否则它只是伪指令$zero。)所以我们想要优化j<b到,j!=b因为我们知道我们在向上计数。

在循环之前放置一个条件分支以检查它是否可能需要运行零次。例如blez $s0, after_loop跳过内部循环体 if b <= 0

asm 中的惯用for(i=0 ; i<a ; i++)循环在 C 中看起来像这样(或对此的一些变体)。

 if(a<=0) goto end_of_loop;
 int i=0;
 do{ ... }while(++i != a);

或者如果i没有在循环内使用,那么i=ado{}while(--i)。(即添加-1和使用bnez)。尽管 MIPS 可以像在 上一样有效地进行分支i!=a,但i!=0与大多数具有 FLAGS 寄存器的架构不同,其中倒计时可以保存比较指令。


D[4*j]意味着我们在一个字数组中跨过 16 个字节。分别使用乘以 4 和移位 2 是疯狂的多余。只需将指针保存在单独的寄存器中,每次迭代将其递增 16,就像 C 编译器一样。


我们不知道 的类型D或任何其他变量。如果它们中的任何一个是窄无符号整数,我们可能需要实现 8 位或 16 位截断/换行。

但是您的实现假设它们都是intor unsigned,所以让我们这样做。

我假设一个没有分支延迟槽的 MIPS,就像 MARS 默认模拟的那样。

i+ja-1从设置最终值的最后一个外循环迭代开始(j=0) 。它运行到j=b-1,所以最大值是a-1 + b-1

在编写任何 asm之前,将问题简化为我们需要存储的值以及需要存储它们的位置,这意味着我们编写的 asm 更简单,更容易调试。

您可以通过在 C 源代码中进行这些转换并使用 C 中的单元测试进行检查来检查大多数转换的有效性。

# int a: $s0
# int b: $s1
# int *D: $s2

# Pointer to D[4*j] : $t0
# int  i+j          : $t1
# int  a-1 + b      : $t2 loop bound

     blez   $s0,  EXIT                        # if(a<=0) goto EXIT
     blez   $s1,  EXIT                        # if(b<=0) goto EXIT
  # now we know both a and b loops run at least once so there's work to do

     addiu  $t1, $s0, -1      # tmp = a-1        // addu because the C source doesn't do this operation, so we must not fault on signed overflow here.  Although that's impossible because we already excluded negatives
     addu   $t2, $t1, $s1     # tmp_end = a-1 + b  // one past the max we store
     add    $t0, $s2, $zero   # p = D              // to avoid destroying the D pointer?  Otherwise increment it.

inner:                           # do {
     sw     $t1, ($t0)                 # tmp = i+j
     addiu  $t1, $t1, 1                # tmp++;
     addiu  $t0, $t0, 16               # 4*sizeof(*D)   # could go in the branch-delay slot
     bne    $t1, $t2, inner      # }while(tmp != tmp_end)
EXIT:

我们本可以在存储之前先完成增量,然后使用a-2anda+b-2作为 and 的初始化tmptmp_end。在一些真正的流水线/超标量 MIPS CPU 上,最好避免将增量放在bne读取它之前。(将指针增量移动到分支延迟槽后)。当然,您实际上会展开以节省工作,例如使用sw $t1, 16($t0)and 32($t0)/ 48($t0)

再次在具有分支延迟的真正 MIPS 上,您将移动一些 init$t0..2以填充早期输出blez指令中的分支延迟槽,因为它们不能相邻。

如您所见,您的版本至少可以说过于复杂。问题中没有说我们必须将每个 C 表达式分别音译为 asm,而 C 的全部意义在于允许这样优化的“as-if”规则。

于 2019-07-05T07:57:19.713 回答
-1

这个类似的 C 代码编译并转换为 MIPS:

#include <stdio.h>

main()
{
int a,b,i,j=5;
int D[50];
for(i=0; i<a; i++)
  for(j=0; j<b; j++)
    D[4*j] = i + j;
}

结果:

.file   1 "Ccode.c"

 # -G value = 8, Cpu = 3000, ISA = 1
 # GNU C version cygnus-2.7.2-970404 (mips-mips-ecoff) compiled by GNU C version cygnus-2.7.2-970404.
 # options passed:  -msoft-float
 # options enabled:  -fpeephole -ffunction-cse -fkeep-static-consts
 # -fpcc-struct-return -fcommon -fverbose-asm -fgnu-linker -msoft-float
 # -meb -mcpu=3000

gcc2_compiled.:
__gnu_compiled_c:
    .text
    .align  2
    .globl  main
    .ent    main
main:
    .frame  $fp,240,$31     # vars= 216, regs= 2/0, args= 16, extra= 0
    .mask   0xc0000000,-4
    .fmask  0x00000000,0
    subu    $sp,$sp,240
    sw  $31,236($sp)
    sw  $fp,232($sp)
    move    $fp,$sp
    jal __main
    li  $2,5            # 0x00000005
    sw  $2,28($fp)
    sw  $0,24($fp)
$L2:
    lw  $2,24($fp)
    lw  $3,16($fp)
    slt $2,$2,$3
    bne $2,$0,$L5
    j   $L3
$L5:
    .set    noreorder
    nop
    .set    reorder
    sw  $0,28($fp)
$L6:
    lw  $2,28($fp)
    lw  $3,20($fp)
    slt $2,$2,$3
    bne $2,$0,$L9
    j   $L4
$L9:
    lw  $2,28($fp)
    move    $3,$2
    sll $2,$3,4
    addu    $4,$fp,16
    addu    $3,$2,$4
    addu    $2,$3,16
    lw  $3,24($fp)
    lw  $4,28($fp)
    addu    $3,$3,$4
    sw  $3,0($2)
$L8:
    lw  $2,28($fp)
    addu    $3,$2,1
    sw  $3,28($fp)
    j   $L6
$L7:
$L4:
    lw  $2,24($fp)
    addu    $3,$2,1
    sw  $3,24($fp)
    j   $L2
$L3:
$L1:
    move    $sp,$fp         # sp not trusted here
    lw  $31,236($sp)
    lw  $fp,232($sp)
    addu    $sp,$sp,240
    j   $31
    .end    main
于 2013-12-16T07:25:21.257 回答