Условные операторы в CIL — как получить следующее свободное место для хранения?

Я конвертирую код в IL (CIL/MSIL и т.д.) из С#. Я застрял на условных выражениях, потому что хочу иметь возможность сохранять данные в следующем доступном свободном местоположении. Пример:

var x = 0;
if(x > 20)
 x = 1;

Console.WriteLine(x);

Если я преобразую это в то, что я считаю правильным IL, я получаю:

IL_0001:  ldc.i4.0    
IL_0002:  stloc.0     
IL_0003:  ldloc.0     
IL_0004:  ldc.i4.s    14 
IL_0006:  cgt         
IL_0008:  ldc.i4.0    
IL_0009:  ceq         
IL_000B:  stloc.1     
IL_000C:  ldloc.1     
IL_000D:  brtrue.s    IL_0011
IL_000F:  ldc.i4.1    
IL_0010:  stloc.0     
IL_0011:  ldloc.0     
IL_0012:  call        System.Console.WriteLine

Я считаю, что это правильный IL, но мой пример очень статичен. Если вы видите в коде IL, он сохраняет результат ceq в loc.1

И, таким образом, моя проблема - компилятор увидел, что loc.0 уже занят (переменная «x»), и использовал следующее свободное место, которое было 1. Моя цель - сделать это динамически, где данный метод может иметь переменную N перед условным.

Итак, наконец, вот мой вопрос: как мне из C# создать код операции, чтобы сказать «stloc.nextAvailable» и его эквивалентную нагрузку?


person OnResolve    schedule 19.06.2012    source источник
comment
Зачем вообще хранить его, если значение используется только для загрузки в следующей инструкции?   -  person harold    schedule 20.06.2012
comment
Почему вы все равно сравниваете bool с нулем?   -  person harold    schedule 20.06.2012
comment
@harold Инструкция ceq IL извлекает два верхних значения в оценочном стеке, сравнивает их, а затем помещает 1, если они равны, или 0, если они не равны.   -  person dlev    schedule 20.06.2012
comment
@dlev да. Но почему? Один из его операндов является результатом другого сравнения — его можно использовать напрямую.   -  person harold    schedule 20.06.2012
comment
Вы должны считать сами. Не большая проблема с ILGenerator.DeclareLocal()   -  person Hans Passant    schedule 20.06.2012


Ответы (2)


Мне кажется, что вы смотрите на IL, сгенерированный при компиляции для режима отладки, stloc.1 и ldloc.1 ссылаются на локальный объект, который не существует в вашем коде, но может быть создан, чтобы дать симпатичную небольшую всплывающую подсказку при наведении курсора на более символ во время отладки.

Я бы ожидал, что IL, сгенерированный режимом выпуска, будет выглядеть примерно так (если бы он не оптимизировал все это до Console.WriteLine(0); заранее):

//load the 4-byte integer 0 on to the stack
IL_0001:    ldc.i4.0
//set the value of local 0 (x) to the previous value on the stack (0)
IL_0002:    stloc.0
//load up x for the comparison
IL_0003:    ldloc.0
//load the 4-byte integer 0x14 (20) on to the stack
IL_0004:    ldc.i4.s    14
//check to see whether two elements back on the stack is greater than one element back on the stack (x > 20), push 1 one back on the stack in the case the first is greater, 0 otherwise
IL_0005:    cgt
//if 0x14 (20) was the greater (or equal) value, jump over the body of the if block
IL_0006:    brfalse.s    IL_0009
//load the 4-byte integer 1 (1) on to the stack
IL_0007:    ldc.i4.1
//set the value of local 0 (x) to the previous value on the stack (1)
IL_0008:    stloc.0
//load the value of local 0 (x) on to the stack
IL_0009:    ldloc.0
//call Console.WriteLine
IL_000A:    call    System.Console.WriteLine
person mlorbetske    schedule 20.06.2012
comment
Вы также можете объединить cgt и ветку - person harold; 20.06.2012
comment
После перекомпиляции в режиме выпуска проблема исчезла. Мне никогда не приходило в голову, что виноваты символы отладки! Благодарность - person OnResolve; 20.06.2012

stloc и ldloc всегда работают только с постоянным индексом. Однако это неплохо - это означает, что если вы выпустили другой код, у вас уже есть следующий доступный индекс, или если вы каким-то образом получите код где-то еще, все, что вам нужно сделать, это взять максимум по всем локальным индексам и добавить один ( или прочитать количество локалов из шапки метода, если он у вас есть).

Или вы можете просто пропустить локальный и использовать результат cgt напрямую (с brfalse) или использовать инструкцию ble. Тогда эта проблема даже не появляется на первом месте.

person harold    schedule 20.06.2012