如何防止编译器生成比较而不是分支指令?

How to prevent the compiler from generating comparison instead of branching instructions?

鉴于分支指令的以下相同属性(来自 microsoft):

  1. blt:效果 与执行 clt 指令后跟 brtrue 相同分支到特定的目标指令。
  2. bgt:效果与执行 cgt 指令后跟 brtrue 相同分支到特定目标指令
  3. bge:效果 与执行 clt 指令相同(clt.un 用于浮点数)后跟指向特定目标指令的 brfalse 分支。
  4. beq:效果与执行 ceq 指令后跟到特定目标指令的 brtrue 分支相同。

事实证明,编译器通常会通过将诸如“<”之类的布尔运算符转换为其 IL 补码分支指令 (clt) 来优化控制流。因此,在不同的计算机上,用于比较的 IL 代码可能会有所不同。我的编译器总是会生成比较运算符,但是在另一台 PC 上我看到它将相同的代码编译成分支变体。

我需要特定的 C# 示例生成始终分支运算符 (blt/bgt/bge/beq) 或始终生成比较运算符 (clt/cgt/clt/ceq),后跟分支 true。我的应用程序测试需要能够断言此 IL 代码。

我的尝试:

用例

我使用将 C# 文件编译成程序集的单元测试,然后这些测试搜索某个操作码 (clt) 并找到可能的逆操作码 (cgt)。这是测试我编写的 C#-mutation 测试工具所必需的。此变异工具在字节级的 dll 代码上变异(反转运算符)。在 C# 源代码级别,我们使用“<”,但在 IL 字节码级别,它可以是 clt 或 blt。

资源

我编写了一个生成器,它能够确保方法定义包含分支变体而不是比较变体。它通过为分支变体建立一个完整的方法体来做到这一点。此生成器仅适用于文档中所示的方法。我正在使用这种方法进行测试。

    /// <summary>
    ///     Generator for a conditional branch statement.
    ///     This generator will only work for the following method structure:
    ///     * Returns true if condition is met
    ///     * Returns false if condition is not met
    ///     * Two int parameters.
    ///     * One if check with the condition.
    ///     ```
    ///     public bool ConditionCheck(int lhs, int rhs)
    ///     {
    ///         if (lhs (ANY CONDITION) rhs)
    ///         {
    ///             return true;
    ///         }
    ///         return false;
    ///     }
    ///     ```
    ///     In some cases the compiler can generate 'comparison' like clt/cgt instead of the branching variants blt/bgt.
    ///     This generator is able to override a method body were an comparison is occurring with the branching variant.
    /// </summary>
    internal class ConditionalBranchGenerator
    {
        private readonly List<Instruction> _instructions;

        /// <summary>
        ///     Generates a new instance by passing in the branching operator that will be used in the generation process.
        ///     Accepts 'only' a branching operators like bgt/blt/beq etc..
        /// </summary>
        /// <param name="comparison"></param>
        public ConditionalBranchGenerator(OpCode comparison)
        {
            var ia = Instruction.Create(OpCodes.Ldloc_1);
            var ib = Instruction.Create(OpCodes.Ret);
            var i7 = Instruction.Create(OpCodes.Ldc_I4_1);

            // load the two parameter booleans
            var i1 = Instruction.Create(OpCodes.Ldarg_1);
            var i2 = Instruction.Create(OpCodes.Ldarg_2);

            // if comparison true branch to i7.
            var i3 = Instruction.Create(comparison, i7);

            // if comparison false load '0' (false) and branch to return. 
            var i4 = Instruction.Create(OpCodes.Ldc_I4_0);
            var i5 = Instruction.Create(OpCodes.Stloc_1);
            var i6 = Instruction.Create(OpCodes.Br_S, ia);

            var i8 = Instruction.Create(OpCodes.Stloc_1);
            var i9 = Instruction.Create(OpCodes.Br_S, ia);

            i1.Next = i2;
            i2.Next = i3;
            i3.Next = i4;
            i4.Next = i5;
            i5.Next = i6;
            i6.Next = i7;
            i7.Next = i8;
            i8.Next = i9;
            i9.Next = ia;
            ia.Next = ib;

            i2.Previous = i1;
            i3.Previous = i2;
            i4.Previous = i3;
            i5.Previous = i4;
            i6.Previous = i5;
            i7.Previous = i6;
            i8.Previous = i7;
            i9.Previous = i8;
            ia.Previous = i9;
            ib.Previous = ia;

            i1.Offset = 0;
            i2.Offset = 1;
            i3.Offset = 2;
            i4.Offset = 3;
            i5.Offset = 4;
            i6.Offset = 5;
            i7.Offset = 6;
            i8.Offset = 7;
            i9.Offset = 8;
            ia.Offset = 9;
            ib.Offset = 10;

            _instructions = new List<Instruction>
            {
                i1, i2, i3, i4, i5, i6, i7, i8, i9, ia, ib
            };
        }

        /// <summary>
        ///     Replace a method body were a comparison is occurring with the branching variant.
        ///     Note that the method definition must ad-hear to strict rules noted in the class-level comments.
        /// </summary>
        /// <param name="method"></param>
        public void ReplaceMethodInstructions(MethodDefinition method)
        {
            method.Body.Instructions.Clear();

            foreach (var instruction in _instructions)
                method.Body.Instructions.Add(instruction);
        }
    }