Почему в этом случае используется код операции "br.s" IL?

В образовательных целях я изучаю немного ИЛ (главным образом потому, что мне было любопытно, что происходит с "%" под капотом (что оказывается в бремене) и начинало отвлекаться...).

Я написал метод, просто вернув true, чтобы немного сломать вещи и задавался вопросом о коде операции "br.s":

.method public hidebysig static bool  ReturnTrue() cil managed
{
  // Code size       7 (0x7)
  .maxstack  1
  .locals init ([0] bool CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0005
  IL_0005:  ldloc.0
  IL_0006:  ret
} // End of method Primes::ReturnTrue

После того, как ldc.i4.1 нажимает 1 на стек, а stloc.0 помещает это в 0-й локальный, br.s в основном (насколько мне известно) выполняет 'goto' до ldloc.0 в строке IL_0005.

Почему это? Почему нет линии IL_0004, поэтому это можно опустить?

Ответы

Ответ 1

Эта ветка предназначена для целей отладки, возвращаемое значение было рассчитано и сохранено, и теперь отладчик может быть вызван. То же самое с NOP в записи метода.

Что касается IL_0004, как указано в @hvd, br.s имеет адрес и не помещается в "одну строку", один байт здесь (я не знаю, насколько вы знакомы с адресацией, но один обычно является одним байтом, то есть 8-битным, а также адресом или смещением, обычно 8-, 16- или 32-разрядным. В этом случае мы имеем 8-битный код операции с 8-битным смещением. В Википедии есть хорошая статья о кодах CIL-OP).

Кроме того, скажем, ваш метод имеет несколько возвратов и через, например, if -branches, все они переходят в конец, IL_0005 в вашем случае, поэтому требуется только одна точка останова при возврате функции.

Ответ 2

Это очень распространенный артефакт рекурсивный спуск парсер, как тот, который использует компилятор С#. Чтобы избавиться от этих ветвей, требуется оптимизатор глазок.

Вероятно, когда компилятор сам оптимизировал тривиальную операцию, результат которой можно определить во время компиляции. Компилятор С# не имеет оптимизатора подглядывания, потому что это не обязательно, дрожание заботится об устранении этих ненужных ветвей. Включение оптимизатора в дрожание - это, как правило, выигрышная стратегия, каждый из которых использует компилятор языка. Сохраняет компиляторы очень простыми и значительными расходами на запись и поддержание оптимизатора кода в одном (или нескольких) местах.

То, что не работает оптимизатор дрожания, весь ваш метод исчезнет во время выполнения. С большой вероятностью, что любой код, который вызывает этот метод, будет существенно оптимизирован, так как ваше возвращаемое значение метода известно во время компиляции. Увидеть этот вид MSIL - это в противном случае сильный намек на то, что ваш код может быть легко упрощен или имеет ошибку:)

Ответ 3

Это не произойдет на Visual Studio 2013, похоже, что dev наконец-то исправил его. Это будет выглядеть на VS2013.

.method public hidebysig static bool  ReturnTrue() cil managed
{
  .maxstack  1
  ldc.i4.1
  ret
} // End of method Primes::ReturnTrue