如何使用 CIL 操作码 bne.un vs beq,以及 bne.un.s vs beq.s,无符号​​与有符号相等

How to us CIL opcode bne.un vs beq, and bne.un.s vs beq.s, unsigned vs signed equality

我知道beqbne.un适用的场景更多(而且是相等对不等),但我想知道有没有区别。例如,假设我使用 char 并且有 x == y,我希望 beq.un,但它不存在,所以我得到 bne.un,但是当我使用 x != y,我得到 beq。无符号比较如何导致有符号比较指令?这有关系吗? (提示:可能不是,因为那是编译器所做的,但为什么会有所不同呢?)。

示例:

  .method public static char  test(char x) cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldc.i4.s   97
    IL_0002:  ldarg.0
    IL_0003:  beq.un.s   IL_0008

    IL_0005:  ldc.i4.s   65
    IL_0007:  ret

    IL_0008:  ldc.i4.s   66
    IL_000a:  ret
  }

对比:

  .method public static char  test(char x) cil managed
  {
    // Code size       11 (0xb)
    .maxstack  8
    IL_0000:  ldc.i4.s   97
    IL_0002:  ldarg.0
    IL_0003:  beq.s      IL_0008

    IL_0005:  ldc.i4.s   65
    IL_0007:  ret

    IL_0008:  ldc.i4.s   66
    IL_000a:  ret
  }

示例 C# 代码:

// this compiles to `bne.un.s`
public class C {
    public bool M(char x) {
        if(x == 'A')
            return true;
        return false;
    }
}

// this compiles to `beq.s`
public class C {
    public bool M(char x) {
        if(x != 'A')
            return true;
        return false;
    }
}

查看 sharplab 代码:

我很惊讶 == 被翻译成(看似更正确的)bne.un.s!= 被翻译成更一般的 beq.s 不使用任何无符号指令。

编译后的 JITted 机器代码没有显示这会导致不同的汇编(这很好):

    L0000: movzx eax, dx
    L0003: cmp eax, 0x41
    L0006: jne short L000e   ; will be je for `!=`
    L0008: mov eax, 1
    L000d: ret
    L000e: xor eax, eax
    L0010: ret

但情况总是如此吗?只要结果是正确的,关于这些操作码的文档并不表明使用任何一种操作码都是错误的。

背景:我在 this PR for F# on the not function 工作时遇到了这种特质,并且很好奇。

“无符号”位是奇数。 beq 的文档没有提到 signed/unsigned 值或浮点数,但 bne.un 的文档明确表示它适用于无符号值和浮点数。但是,编译器对有符号值和无符号值都使用 bne.un

无论如何,docs for beq 会说:

The effect is the same as performing a ceq instruction followed by a brtrue branch to the specific target instruction

docs for bne.un 说:

The effect is identical to performing a ceq instruction followed by a brfalse branch to the specific target instruction

所以我认为可以安全地假设它们彼此完全相反,并且可以在任何可以使用 ceq 的地方使用。

ceq 的文档提到了浮点数,但没有提到有符号/无符号整数。


我怀疑 beq.un 之所以如此命名,是因为如果给定的值相等,或者是无序浮点数 ("branch if equal 或 unordered floats)。有可能是一个过于热心的文档编写者将“un”误解为“无符号”(就像其他人一样) bxx.un 条指令),并传播开来。

符号在比较 int 时很重要(即询问一个是否大于或小于另一个),但不用于测试它们是否相等:如果两个操作数具有相同的符号,您只需要检查它们的位是否模式是相同的。这就是为什么你有例如bleble.un,但没有 beq.un