【问题标题】:How to prevent the compiler from generating comparison instead of branching instructions?如何防止编译器生成比较而不是分支指令?
【发布时间】:2020-12-01 10:45:16
【问题描述】:

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

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

事实证明,编译器通常会通过将诸如“

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

我的尝试:

  • 添加[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
  • If/while/for/do-while/switch 操作,但对我来说都是比较运算符。
  • 尝试了多种示例,但未能成功生成一个简单的 if,导致使用了分支运算符。

用例

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

资源

【问题讨论】:

  • 如果您需要特定的 IL 序列,为什么要强制 C# 编译器生成它们而不是自己显式生成它们?
  • 不用担心 IL -- 不用担心 JIT 生成的 ASM
  • 我使用单元测试将 C# 文件编译成程序集,然后他们搜索某个操作码 (clt) 并找到可能的逆操作码 (cgt)。这是测试我编写的 C#-mutation 测试工具所必需的。这个变异工具会在字节级别上对 dll 代码进行变异。
  • 因此,您正在对适用于 IL 的工具进行单元测试,并且您想说服编译器输出特定的 IL 序列?不要那样做——这非常依赖于编译器版本。发出你想自己测试的 IL 序列
  • @canton7 也许这就是我可以在这里做的。我可以编写一些逻辑来确保存在正确的 IL 代码。因此,如果我无法在生成时确定输出 IL 代码,我认为这将是一件好事。

标签: c# bytecode cil opcode


【解决方案1】:

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

    /// <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);
        }
    }

【讨论】:

    猜你喜欢
    • 2020-04-15
    • 1970-01-01
    • 2013-05-22
    • 2022-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多