tgoop.com/unsafecsharp/261
Create:
Last Update:
Last Update:
IL или как я разбираю джобы
Что такое IL? IL (или Intermediate Language) - это промежутный язык, который еще и не рантайм, но уже и не C#.
Его смысл в том, что это плюс-минус набор инструкций, по которым можно понять суть происходящего в методе.
Как оно выглядит?
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 C::a
IL_0007: ldc.i4.s 10
IL_0009: cgt
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brfalse.s IL_0018
IL_000f: nop
IL_0010: ldarg.0
IL_0011: call instance void C::A()
IL_0016: nop
IL_0017: nop
IL_0018: ldarg.0
IL_0019: call instance void C::A()
IL_001e: nop
IL_001f: ldarg.0
IL_0020: call instance void C::A()
IL_0025: nop
IL_0026: ret
Чем я пользуюсь? https://github.com/chromealex/ME.BECS/tree/main/Runwww.tgoop.com/Extensions/Mono.Reflection
Это не Mono.Cecil, но для моих задач подходит отлично.
Работа этой штуки сводится к тому, чтобы разобрать инструкции метода и сложить их в плюс-минус удобный список.
Давайте разберем задачу из прошлого поста:
Нам нужно посчитать количество вызовов метода.
var count = 0;
var targetMethod = typeof(Ent).GetMethod(nameof(Ent.New), ...);
var instructions = executeJobMethod.GetInstructions();
for (int i = 0; i < instructions.Count; ++i) {
var inst = instructions[i];
{
// тут можем обработать инструкцию
if (inst.Operand is System.Reflection.MethodInfo method && method == targetMethod) {
++count;
}
}
{
if (inst.Operand is System.Reflection.MethodInfo method) {
instructions.InsertRange(i + 1, method.GetInstructions());
}
}
}
Фактически в данный момент мы посчитали количество вызовов определенного метода в джобе рекурсивно заходя в каждый метод. У меня к этому коду еще добавлены различные аттрибуты, чтобы не заходить в ненужные методы и чтобы не попасть в рекурсию, но я уверен, что вы справитесь с этой задачей.
Как я говорил в прошлом посте, нам нужно считать не просто количество вызовов, но и посчитать количество вызовов в цикле (ну или определить, что они в принципе там есть).
Для этого нам нужно обратиться к
OpCodes
. Это по сути код операции у инструкции, которую мы сейчас обрабатываем.Кодов очень много, но нас интересуют лишь коды бранчей.
Бранч - это ветка. Бранчи могут быть созданы в различных кейсах: это может быть if, может быть цикл foreach, for, while и т.д.
Код вида
br*
будет приводить к переходу (типа goto) к конкретной инструкции.И тут начинается самое интересное. Любые циклы и условия - это одна и та же операция
br*
, но за одним исключением: вместе с этой операцией передается номер строки (например, IL_0007
), куда нужно осуществить переход.Так вот если адрес этого перехода меньше, чем адрес текущей инструкции - мы закончили итерацию цикла и возвращаемся назад.
А если инструкция
br*
ведет нас вперед, то это открытие условия или цикла.В коде выше можно увидеть эту инструкцию в виде
IL_000d: brfalse.s IL_0018
. То есть мы переходим в 0018, что находится ниже, значит это условие, а не цикл.Текущий код по поиску информации можно посмотреть здесь:
https://github.com/chromealex/ME.BECS/blob/a665a344e6957ff18945dd9862981d79910c0491/Editor/CodeGenerator/Generators/JobsEarlyInitCodeGenerator.cs#L437
#becs #il #jobs
BY Unity: Всё, что вы не знали о разработке

Share with your friend now:
tgoop.com/unsafecsharp/261