#c# #bytecode #cil #opcode
#c# #байт-код #cil #код операции
Вопрос:
Учитывая следующие идентичные свойства инструкций ветвления (от Microsoft):
- blt: эффект идентичен выполнению инструкции clt, за которой следует переход brtrue к конкретной целевой инструкции.
- bgt: эффект идентичен выполнению инструкции cgt, за которой следует переход brtrue к конкретной целевой инструкции
- bge: эффект идентичен выполнению инструкции clt (clt.un для чисел с плавающей запятой), за которой следует переход brfalse к конкретной целевой инструкции.
- beq: эффект такой же, как при выполнении инструкции ceq, за которой следует переход brtrue к конкретной целевой инструкции.
Оказывается, что компилятор обычно оптимизирует поток управления, переводя логический оператор типа ‘<‘ в свою инструкцию ветвления дополнения IL (clt). Поэтому может быть так, что на разных компьютерах IL-код для сравнения может отличаться. Мой компилятор всегда будет генерировать оператор сравнения, однако на другом компьютере я увидел, что он скомпилировал тот же код в вариант ветвления.
Мне нужны конкретные примеры C #, генерирующие операторы всегда ветвления (blt / bgt / bge / beq) или всегда операторы сравнения (clt / cgt / clt / ceq), за которыми следует значение ветвления true. Мои тесты моего приложения должны иметь возможность утверждать этот IL-код.
Мои попытки:
- Добавить
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
- Операции If/while/for/do-while / switch, но все они приводят к операторам сравнения для меня.
- Поиграл со многими примерами, но безуспешно при генерации простого if, что привело к использованию операторов ветвления.
Usecase
Я использую модульные тесты, которые компилируют файл C # в сборку, затем эти тесты ищут определенный код операции (clt) и находят возможный обратный (cgt). Это требуется для тестирования инструмента тестирования мутаций на C #, который я написал. Этот инструмент мутации мутирует (инвертирует операторы) в коде dll на байтовом уровне. На уровне исходного кода C # мы используем ‘<‘, однако на уровне IL-байт-кода это может быть либо clt, либо blt.
Ресурсы
- Документация по коду операции Microsoft
- Список значений кода операции документация
- Используйте инструмент, подобный IL-spy / Dn-spy, для проверки инструкций, сгенерированных IL.
Комментарии:
1. Если вам нужны определенные последовательности IL, почему вы пытаетесь заставить компилятор C # генерировать их, а не генерировать их самостоятельно явно?
2. Не беспокойтесь об IL — беспокойтесь о ASM, который генерирует JIT
3. Я использую модульные тесты, которые компилируют файл C # в сборку, затем они ищут определенный код операции (clt) и находят возможный обратный (cgt). Это требуется для тестирования инструмента тестирования мутаций на C #, который я написал. Этот инструмент мутации изменяет dll-код на байтовом уровне.
4. Итак, вы проводите модульное тестирование инструмента, который работает на IL, и хотите убедить компилятор выводить определенные последовательности IL? Не делайте этого — это невероятно зависит от версии компилятора. Создайте последовательности IL, которые вы хотите протестировать самостоятельно
5. @canton7 Может быть, это то, что я могу сделать здесь. Я мог бы написать некоторую логику, которая гарантирует наличие правильного IL-кода. Так что, если у меня нет возможности определить выходной IL-код при генерации, я думаю, это было бы неплохо сделать.
Ответ №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);
}
}