如何在汇编语言(MIPS)中小于或等于?

时间:2014-03-29 20:59:19

标签: assembly mips cpu-registers

我面前有一个C代码,我必须翻译成MIPS汇编语言。

我不是在寻找直接的答案,但我希望有人纠正我对这个问题的思考方式。

我面前的C代码是:

x = ((++z)<=y);

我认为x,y和z分别存储在寄存器 $ 6,$ 7,$ 8

问题是我无法使用运算符直接比较小于或等于。我只能使用以下比较操作数:bne,beq,ori,slt。

我解决问题的方式是这样的:

      addi   $8,$8,1     #this will increment z by 1 to have ++z
      slt    $1,$8,$7    #compares ++z to y if ++z is < than y, it will store 1 in $1
      beq    $8,$7,Label #compares if $8 = $7, if so the code jumps to Label
Label addi   $t0,$0,1    #if ++z = y, stores 1 in $t0
      ori    $6,$t0,$1   #Or's the t0 and t1 and accordingly stores 0 or 1 in x

这是解决这个问题的正确方法吗?

2 个答案:

答案 0 :(得分:0)

正如迈克尔所指出的那样,你应该在下面标记一行:

         addi   $8,$8,1
         slt    $1,$8,$7
         beq    $8,$7,Label
         addi   $t0,$0,1
Label    ori    $6,$t0,$1

有趣的是,您使用slt但不使用seq,为什么不呢?你可能会这样做:

         addi   $8,$8,1
         slt    $1,$8,$7
         seq    $t0,$8,$7
         ori    $6,$t0,$1

通过避免分支,程序可以稍快一些(假设案例多次发生。)

现在,如果我没记错的话,sltseq将生成0或255.如果您正在编写C / C ++兼容语句,那么您需要使用0或1。一种方法是在ori

之后添加一个班次,类似于此
         srl    $6,$6,7

答案 1 :(得分:0)

像往常一样,询问编译器(例如 on the Godbolt compiler explorer)。另见How to remove "noise" from GCC/clang assembly output?


<= 单独(没有增量) 最好用 反向 slt 实现(做 b<aa<=b) 相反,然后 xori 翻转结果。

int booleanize_le(int y, int z){
    return (z<=y);
}
# GCC5.4 -O3 -fno-delayed-branch
booleanize_le(int, int):
        slt     $2,$4,$5        # slt retval, y, z
        xori    $2,$2,0x1       # !retval, i.e. flip 0 <-> 1 since it was already boolean
        j       $31                       # return
  # (omitting the NOP in the branch-delay slot)

有趣的事实:用于 RISC-V 的 clang 做同样的事情,slt/xori,因为 RISC-V 就像 MIPS 一样,只提供 slt 用于比较到布尔值。 (因为您可以将任何关系(如 ==、<=、> 或其他任何关系)布尔化为最多 2 条指令的整数寄存器。)


但是在您想要递增并且使用 addi 的情况下,很明显 z 必须是有符号整数(否则您的代码会在从 0x7fffffffU 递增到 0x80000000U 时出错;使用 addiu如果你想要定义明确的环绕)。

未定义行为的签名溢出的 C 规则基本上匹配 MIPS addi 的使用,这意味着编译器还将假定 ++z 不包装,并进行优化我们想要。他们和我们可以只使用原始的 z 值。 (z+1)<=yz<y 相同,如果 z+1 不/不能换行。

int booleanize_inc_le_signed(int y, int z){
    return ((++z)<=y);
}
booleanize_inc_le_signed(int, int):
        slt     $2,$5,$4             # z<y  before incrementing
        j       $31
    # increment optimized away in this function, but you'd do it with
    addiu   $5, $5, 1

如果 z 未签名,则无法进行优化,编译器只会递增,然后使用 2 指令 <= slt/xori 序列的未签名版本:

int booleanize_inc_le_unsigned(unsigned int y, unsigned int z){
    return ((++z)<=y);
}
booleanize_inc_le_unsigned(unsigned int, unsigned int):
        addiu   $5,$5,1                # z++
        sltu    $2,$4,$5               # y<z (unsigned)
        xori    $2,$2,0x1              # return !(y<z)
        j       $31

其他关系

完全相等,a == b

    xor     $2,$4,$5          # find bit-differences
    sltu    $2,$2,1           # (a^b) < 1U

不等于,a != b

    xor     $2,$4,$5
    sltu    $2,$0,$2          # 0U < (a^b)

一个非零整数:a!=0,即!!a

    sltu    $2,$0,$4          # 0U < a

显然可以派生任何其他人,或者只是询问编译器(使用 Godbolt 链接)。