2011-07-20 2 views
0

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

 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, но я не уверен, как это работает.

EDIT Кстати IL видели выше, для этого простого метода,

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

Там должно быть что-то я не вижу здесь ... Почему вы хотите, чтобы перейти к следующей инструкции? Разве это не просто более дорогостоящий нет-op? Кроме того, почему вы отмечаете инструкцию ветки как цель метки, если вы не хотите, чтобы она была целью? –

+0

Переместите вызов MarkLabel() после вызова Emit(). Или просто опустить ветвь полностью, она ничего не делает. –

+0

@Sean Я думаю, вы скоро поймете, что находитесь у вас над головой. Если вы планируете реализовать полный ИЛ-ассемблер, вам нужно будет сделать намного лучше, чем «string.Contains» для инструкций филиала. Вам в основном нужно создать ярлык и * найти * нужное место для него. Не все ветви «для следующей строки». –

ответ

4

Этот код:

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

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

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

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

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

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

4

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

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

ldstr "Hello, World!" 
ret 

Независимо компилятор излучаемый исходный код должен иметь своего автора LARTed.

+0

Я вижу. Я не понимаю, что такое no-op? Почему IL всегда содержит эту строку для простого метода HelloWorld()? Похоже, он должен просто перейти от IL_0006 к IL_0009. –

+0

No-op = нет операции. Это немного кода, который ничего не добьется. Почему ИЛ содержит локальную или ветвь вообще вне меня; не требуются. – cdhowie

+0

«nop» или no-op означает «нет операции», это в основном инструкция-манекен, инструкция наполнителя, которая ничего не делает. Машина просто пропустит его. Обычно он добавляется из-за выравнивания, некоторые оптимизации могут переупорядочить некоторый код и сделать «дыры» или аналогичные. –

0

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

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

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

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

Ldstr "Hello, World!" 
Ret