Как .NET VM проверяет целочисленные переполнения? - PullRequest
1 голос
/ 12 января 2010

Как мы все знаем и любим .NET выдает исключение IntegerOverflow каждый раз, когда целое число переполняется. Я думаю, что это очень хорошая вещь.

Но мне интересно, как они делают это быстро. x86 не перехватывает целочисленные переполнения, и я был бы удивлен, если бы другие архитектуры позволяли это делать. Лучшее решение, которое я нашел для x86, - это помещать инструкцию «INTO» после каждой отдельной арифметической операции. Но я предполагаю, что это приведет к заметному замедлению.

Они могут выполнить некоторую статическую проверку в компиляторе, чтобы избежать этого в случаях, когда можно доказать, что операция не может быть переполнена. Но как быть с критичными по производительности внутренними циклами, когда компилятор не может определить результат операций?

Я попытался посмотреть на источник Mono, но не смог найти место, где они делают эти проверки.

Так кто-нибудь знает, что они на самом деле делают? Я действительно хотел бы знать.

Примечание: есть ли способ увидеть код x86, который излучает .NET JITC?

Ответы [ 2 ]

3 голосов
/ 12 января 2010

Запустите отладку, щелкните правой кнопкой мыши по источнику, перейдите к разборке. Вы бы увидели что-то вроде этого:

      int ix = int.MaxValue;
0000003a  mov         dword ptr [ebp-40h],7FFFFFFFh 
      int jx = 1;
00000041  mov         dword ptr [ebp-44h],1 
      Console.WriteLine(ix + jx);
00000048  mov         ecx,dword ptr [ebp-40h] 
0000004b  add         ecx,dword ptr [ebp-44h] 
0000004e  jno         00000055                 <--- overflow test
00000050  call        6D7ABAD2                 <--- kaboom
00000055  call        6CFE2F40 

Другими словами: JIT-компилятор генерирует явный код для проверки на переполнение. По умолчанию это отключено.

2 голосов
/ 12 января 2010

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

Это всего лишь конкретный пример общей проблемы арифметического переполнения , и реализация JIT x86, несомненно, вставит проверку соответствующих флагов после любой такой операции с инструкцией для выдачи исключения, если флаги установлены.

инструкции (используя в качестве примера дополнение):

  • добавление для добавления двух чисел (с переносом вокруг переполнения)
  • add.ovf для добавления двух чисел и переполнения со знаком trap
  • add.ovf.un , чтобы добавить два числа и перехватить переполнение без знака

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

Если вы хотите увидеть испущенный код JIT, просто отладьте соответствующий код в смешанном режиме и посмотрите на разборку, стек, регистры, как в обычной программе. Или же посмотрите и посмотрите на изображения ngen (это сложно, так как формат подвержен изменениям).

Обратите внимание, что при выполнении этого через VS вы можете запустить программу как обычно (в режиме Release), а затем подключить отладчик, поскольку результат JIT отличается в зависимости от того, подключен ли отладчик и помечена ли сборка. как запрещающие оптимизации (по умолчанию для отладочных сборок)

...