如何使用 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
我知道beq
比bne.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 时很重要(即询问一个是否大于或小于另一个),但不用于测试它们是否相等:如果两个操作数具有相同的符号,您只需要检查它们的位是否模式是相同的。这就是为什么你有例如ble
和 ble.un
,但没有 beq.un
。
我知道beq
比bne.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 abrtrue
branch to the specific target instruction
而 docs for bne.un
说:
The effect is identical to performing a
ceq
instruction followed by abrfalse
branch to the specific target instruction
所以我认为可以安全地假设它们彼此完全相反,并且可以在任何可以使用 ceq
的地方使用。
ceq
的文档提到了浮点数,但没有提到有符号/无符号整数。
我怀疑 beq.un
之所以如此命名,是因为如果给定的值相等,或者是无序浮点数 ("branch if equal 或 unordered floats)。有可能是一个过于热心的文档编写者将“un”误解为“无符号”(就像其他人一样) bxx.un
条指令),并传播开来。
符号在比较 int 时很重要(即询问一个是否大于或小于另一个),但不用于测试它们是否相等:如果两个操作数具有相同的符号,您只需要检查它们的位是否模式是相同的。这就是为什么你有例如ble
和 ble.un
,但没有 beq.un
。