Почему в этом случае используется код операции "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