Подтвердить что ты не робот

Результат добавления int32 в 64-битный собственный int?

При добавлении int32 в 64-разрядный native int, является ли знак CLR расширением или нулевым расширением 32-разрядного целого? И самое главное: на основе какой информации он делает этот выбор?


Я пишу компилятор .NET и прочитал спецификацию ECMA, но не смог найти ответ.

CLI поддерживает только подмножество этих типов в своих операциях над значениями, хранящимися в его стеке оценки: int32, int64 и native int.
- ECMA 335, Раздел я 12.1: Поддерживаемые типы данных

Так как значения в стеке оценки не имеют информации об их подписи, инструкции, для которых важна подпись операндов, имеют два варианта: один для подписанных и один для целых чисел без знака. Команды add, sub и mul (те, которые не проверяют переполнение), не должны заботиться о подписности операндов, если операнды имеют одинаковый размер и, следовательно, имеют только единый вариант. Однако операнды не всегда одинакового размера...

ECMA 335, раздел III 1.5: таблица типов операндов утверждает, что int32 и native int можно добавлять, вычитать, умножать и разделены. В результате снова будет native int. В 64-битной системе a native int имеет ширину 64 бит.

ldc.i4.0            // Load int32 0
conv.i              // Convert to (64-bit) native int
ldc.i4.m1           // Load int32 -1
add                 // Add native int 0 and int32 0xFFFFFFFF together

Итак, каков будет результат здесь? Обратите внимание, что в соответствии со спецификацией время выполнения не требует отслеживания точных типов или подписи значений в стеке: он знает только int32, int64 и native int (и некоторые другие, которые не актуальны здесь).


Я бы предположил, что арифметика IntPtr и UIntPtr, так как она внутренне представлена ​​как native ints, также будет использовать этот вид добавления. Однако ILSpy показывает, что добавление IntPtr и int32 в С# вызывает перегруженный + оператор класса IntPtr, который принимает только подписанный аргумент int32.

Выполнение непосредственно в CIL (с помощью команды add) также указывает, что целое число интерпретируется как подписанное. Он также должен был быть реализован в Mono, но я не мог найти ссылок на мои результаты.

4b9b3361

Ответ 1

Подпись не имеет значения при добавлении двух значений одного и того же битрейта. Например, добавление 32-бит -10 (0xfffffff6) в 32-разрядный 10 (0x0000000a) будет корректно давать 0. Из-за этого в CIL (Common Language Language) есть только одна инструкция add.

Однако при добавлении двух значений разных битов, значение подписи имеет значение. Например, добавление 32-разрядных от -10 до 64-разрядных 10 может привести к 4294967296 (0x100000000), когда будет выполнено без знака, и 0 при подписании.

Инструкция CIL add позволяет добавлять собственное целое число и 32-разрядное целое число. Нативное целое число может быть 64-битным (в 64-битной системе). Тестирование показывает, что add рассматривает 32-битное целое число как целое число со знаком и расширяет его. Это не всегда правильно и может считаться ошибка. В настоящее время Microsoft не собирается его исправлять.

Поскольку проверка переполнения зависит от того, рассматриваются ли операнды как неподписанные или подписанные, существуют два варианта add.ovf: add.ovf (подпись) и add.ovf.un (без знака). Тем не менее, эти варианты также правильно подписывают-удлиняют нуль-расширяют меньший операнд при добавлении 32-разрядного целого к наследуемому целому числу.

Таким образом, добавление собственного целого числа и 32-битного целого числа без знака может давать разные результаты в зависимости от параметра проверки переполнения С#. По-видимому, тот факт, что я не мог понять это, является результатом ошибки или контроля над языком языка CIL.

Ответ 2

Вы находитесь на неизведанной территории здесь, я не знаю ни одного языка .NET, который на самом деле позволяет это. Их синтаксическая проверка отклоняет любой код, который пытается это сделать. Отказано даже добавление двух собственных int. В конечном счете это зависит от дрожания, чтобы сгенерировать для него машинный код. Если вы хотите знать, что происходит, тогда просто экспериментируйте. Обязательно проверьте по крайней мере хладнокровие x86 и x64.

Учитывая семантику iffy и реальную возможность того, что изменение будущего дрожания может нарушить ваши предположения, я настоятельно рекомендую вам также отказаться от этого на своем родном языке. Это просто не очень полезно и простое обходное решение, которое отбрасывает (long), а результат обратно (IntPtr) имеет хорошо определенную семантику. Что само по себе является способом получить предсказуемое поведение в вашем собственном генераторе кода.