Использование Br_S OpCode для указания следующей инструкции с использованием Reflection.Emit.Label - PullRequest
0 голосов
/ 20 июля 2011

Я экспериментирую с разбором IL для создания метода. Я получил код IL метода в строке [], где каждая строка является инструкцией IL. Я перебираю этот массив и добавляю коды операций с помощью ILGenerator:

        foreach (string ins in instructions) //string representations of IL          
        {
            string opCode = ins.Split(':').ElementAt(1);

            // other conditions omitted

            if (opCode.Contains("br.s"))
            {
                Label targetInstruction = ilGenerator.DefineLabel();

                ilGenerator.MarkLabel(targetInstruction);

                ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 
            }

Вот IL, который мне нужно воспроизвести:

Source IL:
IL_0000: nop
IL_0001: ldstr "Hello, World!"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret

И вот что я получаю в качестве вывода:

Target IL:
IL_0000: nop
IL_0001: ldstr "Hello, World!"
IL_0006: stloc.0
IL_0007: br.s IL_0007   // this is wrong -- needs to point to IL_0009
IL_0009: ldloc.0
IL_000a: ret

Как видите, вызов br.s указывает на себя, что, конечно, вызывает бесконечный цикл. Как я могу получить указание на следующую инструкцию, как в источнике? Это связано с использованием Reflection.Emit.Label, но я не уверен, как это работает.

РЕДАКТИРОВАТЬ Кстати, IL, указанный выше, предназначен для этого простого метода,

    public string HelloWorld()
    {
            return "Hello, World!";
    }

Ответы [ 3 ]

4 голосов
/ 20 июля 2011

Этот код:

ilGenerator.MarkLabel(targetInstruction);
ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 

четко говорит "пометить метку здесь", а затем вы добавляете инструкцию в точку, где вы пометили метку .

Если это не то, что вы хотите, почему вы это делаете?

MarkLabel помечает текущую позицию, что означает позицию выводимой вами инструкции next , в качестве цели метки.

В этом случае, чтобы получить «то, что вы хотите», просто переверните эти две строки, выведите инструкцию перехода перед маркировкой метки.

Я поместил «что вы хотите» вцитаты, так как я не понимаю смысл этой инструкции ветвления.Машина с радостью «перейдет» к следующей инструкции сама по себе, для этого не нужно добавлять инструкции «переходить к следующей инструкции».

4 голосов
/ 20 июля 2011

Вам необходимо совершить вызов ilGenerator.MarkLabel() непосредственно перед отправкой кода операции, который вы хотите перейти с на . Вы помещаете это перед ветвлением, что означает, что это ответвляется к себе, эффективно создавая бесконечный цикл. Но, как говорит Лассе, если вы будете правильно излучать IL, это будет бездействие.

Интересно, что весь метод может быть легко:

ldstr "Hello, World!"
ret

Какой бы компилятор ни исходил, исходный код должен иметь автора LARTed.

0 голосов
/ 21 апреля 2016

Вызов метода MarkLabel() на ILGenerator можно использовать для маркировки точки ветвления, а затем Emit(OpCodes.Br_S, [label]) для перехода к точке.

Я предполагаю, что любой API, который вы использовали для шпионажа инструкций IL дляметод Hello World был выполнен в режиме отладки, так как добавлены инструкции nop и branch, чтобы гарантировать, что отладчик охватывает каждый шаг.

В DynamicMethod нет необходимости подключать отладчик, и в зависимости отна платформе выполнение его с дополнительными инструкциями в режиме выпуска может привести к исключению InvalidProgramException.

Метод «Hello World» требует только 2 инструкции (и он довольно интуитивно понятен)

Ldstr "Hello, World!"
Ret
...