2

I'm just trying to get into IL because I'm working with code-injection. I'm required to analyze code and cover various cases.

Sadly it doesn't work to inject a method call at the end if the last instructions are inside an if-clause, because the call is being contained by the paranthesis then.

Now I've been analyzing if-code being translated into IL and I'm a bit confused by how this is done. Obviously the compiler reverses the if. Is this because of performance reasons? If so by how far does this improve performance?

See for yourself:

        string test;
        Random rnd = new Random();
        bool b = rnd.Next(0, 10) == 3;
        if (b)
        {
            // TRUE
            test = "True branch";
            // END TRUE
        }
        else
        {
            // FALSE
            test = "False branch";
            //END FALSE
        }

and this is the output:

    IL_0000: nop
    IL_0001: newobj instance void [mscorlib]System.Random::.ctor()
    IL_0006: stloc.1
    IL_0007: ldloc.1
    IL_0008: ldc.i4.0
    IL_0009: ldc.i4.s 10
    IL_000b: callvirt instance int32 [mscorlib]System.Random::Next(int32,  int32)
    IL_0010: ldc.i4.3
    IL_0011: ceq
    IL_0013: stloc.2
    IL_0014: ldloc.2
    IL_0015: ldc.i4.0
    IL_0016: ceq
    IL_0018: stloc.3
    IL_0019: ldloc.3
    IL_001a: brtrue.s IL_0026

    IL_001c: nop
    IL_001d: ldstr "True branch"
    IL_0022: stloc.0
    IL_0023: nop
    IL_0024: br.s IL_002e

    IL_0026: nop
    IL_0027: ldstr "False branch"
    IL_002c: stloc.0
    IL_002d: nop

    IL_002e: ret

As you can see, after the comparison of the Random result with the const 3 it does a comparision against 0 again and thus reverses the result which is equivalent to if (false).

What reason has this? Isn't it less performant since you need additional instructions? Does this happen always?

4

2 回答 2

1

您正在查看调试版本。将其更改为发布版本,它使用brfalse.s

IL_0000: newobj instance void [mscorlib]System.Random::.ctor()
IL_0005: stloc.1
IL_0006: ldloc.1
IL_0007: ldc.i4.0
IL_0008: ldc.i4.s 10
IL_000a: callvirt instance int32 [mscorlib]System.Random::Next(int32, int32)
IL_000f: ldc.i4.3
IL_0010: ceq
IL_0012: stloc.2
IL_0013: ldloc.2
IL_0014: brfalse.s IL_001e

IL_0016: ldstr "True branch"
IL_001b: stloc.0
IL_001c: br.s IL_0024

IL_001e: ldstr "False branch"
IL_0023: stloc.0

我添加了一个Console.WriteLine,否则test变量被删除。

IL_0024: ldloc.0
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: ret

所以Debug和release的区别是:

// Debug
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.3
IL_0019: ldloc.3
IL_001a: brtrue.s IL_0026

对比

// Release
IL_0014: brfalse.s IL_001e

因此,Debug 版本有四个附加指令,以及一个“反向”if。

首先我要说的是,C# 编译器试图让代码保持与编写时相同的顺序。所以首先是“真”分支,然后是“假”分支。

好的...我正在做一个hypotesis...

假设问题出在调试模式下……在调试模式下,代码必须很冗长……非常冗长。所以

if (b)

被翻译成

if (b == true)

可悲true的是“除了 0 之外的任何东西”,所以写起来更容易

if (!(b == false))

因为false是“0”。但这就是在调试模式下写的:-) 只有调试模式使用临时变量

作为

// bool temp = b == false;
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.3
IL_0019: ldloc.3

// if (temp) // go to else branch
IL_001a: brtrue.s IL_0026
于 2015-03-10T13:08:58.707 回答
0

编译器没有反转任何东西。请注意,if语句的两个分支在 IL 中的显示顺序与它们在源代码中的顺序相同。从两个字符串的顺序可以看出。

当布尔值为假时,使用的brtrue只是用于分支的自然 IL。测试布尔值是否为真意味着将其与 0 进行比较。值为 0 为假,其他任何值都被视为真。

因此,编译器发出 IL 以与 0 进行比较。如果该比较为真,即布尔值的序数值为 0,则布尔值为假。因此,如果等于 0,则分支,这就是您在此处所拥有的,这意味着如果布尔值为假,则分支。那是ceq反对 0 后跟brtrue.

也就是说,值得指出的是,在为调试性能而编译时,这不是问题。编译器希望编写代码以使调试器能够检查变量。如果您对性能感兴趣,则必须从发布版本中查看 IL。当你这样做时,你会看到完全不同的代码。

于 2015-03-10T13:14:39.093 回答