Почему компилятор C# генерирует такой код IL для возврата значения

175
04 ноября 2018, 06:00

Ассоциация: stackoverflow.com/q/26323373/6677992.

Простая программа, метод принимающий 1 аргумент и тут же его отдающий (режим компилятора- без оптимизации).

void Main()
{
    var b = false;
    Method(b);
}
// Define other methods and classes here
public bool Method(bool a){
    return a;
}

Инструкции на языке IL:

IL_0000:  nop         
IL_0001:  ldc.i4.0    
IL_0002:  stloc.0     // b
IL_0003:  ldarg.0     
IL_0004:  ldloc.0     // b
IL_0005:  call        UserQuery.Method
IL_000A:  pop         
IL_000B:  ret         
Method:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  stloc.0     
IL_0003:  br.s        IL_0005
IL_0005:  ldloc.0     
IL_0006:  ret    

Вопрос: почему IL_0004 отсутствует? И вытекающий из этого вопрос — почему происходит перенаправление на IL_0005?

Answer 1

Перевод ответа @flindeberg

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

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

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

От себя хочу добавить- если включить режим оптимизации для компилятора, получаем следующие инструкции:

IL_0000:  ldc.i4.0    
IL_0001:  stloc.0     // b
IL_0002:  ldarg.0     
IL_0003:  ldloc.0     // b
IL_0004:  call        UserQuery.Method
IL_0009:  pop         
IL_000A:  ret         
Method:
IL_0000:  ldarg.1     
IL_0001:  ret  

Как мы видим, в данном случае в методе происходит просто загрузка аргумента в стек и возврат из метода. Как заметил @Hans Passant в этом же вопросе- это очень распространенный артефакт парсера рекурсивного спуска, который использует компилятор C#.

READ ALSO
StringFormat & число только с целой частью

StringFormat & число только с целой частью

Есть ли формат для чисел с целой частью, чтобы разбивал сам по тысячам и тд

136
Как обработать строку из реестра

Как обработать строку из реестра

Всех приветствую, вот небольшую задачку делаюДостаю из реестра данных о файлах запускающихся при старте системы

191
Передать данные в контроллер

Передать данные в контроллер

Имеется форма веб-приложения, хочу передать данные с нее в объект внутрь ajax, чтобы он в последствии передал их в контроллер, но только примерно...

194
Возврат некорректного значения

Возврат некорректного значения

Создал массив состояний заказа, создал enum-ы под состояния заказа, ввожу в консоль ID заказа возвращается его статусПри вводе в консоль ID заказа,...

154