Почему это добавление байта * и uint не может перенести в высшее меч? - PullRequest
9 голосов
/ 04 января 2012

Сейчас подано в Microsoft Connect ; Пожалуйста, подтвердите, если вы чувствуете, что это нужно исправить. Я также значительно упростил тестовый пример:

byte* data = (byte*) 0x76543210;
uint offset = 0x80000000;
byte* wrong = data + offset;
byte* correct = data + (uint) 0x80000000;

// "wrong" is now 0xFFFFFFFFF6543210 (!)
// "correct" is 0xF6543210

Глядя на IL, насколько я могу судить, компилятор C # все сделал правильно, и ошибка заключается в JITter.


Оригинальный вопрос : Что здесь происходит?

byte* data = (byte*)Marshal.AllocHGlobal(0x100);

uint uioffset = 0xFFFF0000;
byte* uiptr1 = data + uioffset;
byte* uiptr2 = data + (uint)0xFFFF0000;

ulong uloffset = 0xFFFF0000;
byte* ulptr1 = data + uloffset;
byte* ulptr2 = data + (ulong)0xFFFF0000;

Action<string, ulong> dumpValue =
    (name, value) => Console.WriteLine("{0,8}: {1:x16}", name, value);

dumpValue("data",     (ulong)data);
dumpValue("uiptr1",   (ulong)uiptr1);
dumpValue("uiptr2",   (ulong)uiptr2);
dumpValue("ulptr1",   (ulong)ulptr1);
dumpValue("ulptr2",   (ulong)ulptr2);

Для этого теста требуется 64-разрядная ОС для платформы x64.

Выход:

  data: 000000001c00a720    (original pointer)
uiptr1: 000000001bffa720    (pointer with a failed carry into the higher dword)
uiptr2: 000000011bffa720    (pointer with a correct carry into the higher dword)
ulptr1: 000000011bffa720    (pointer with a correct carry into the higher dword)
ulptr2: 000000011bffa720    (pointer with a correct carry into the higher dword)
               ^
               look here

Так это ошибка или я что-то напутал?

Ответы [ 2 ]

4 голосов
/ 04 января 2012
3 голосов
/ 04 января 2012

(Ответ в процессе разработки)

Я проверил испущенный x64 asm, и это мои наблюдения:

Базовый указатель:

data:
00000000024539E0

Указатель с правильным переносом:

data + (uint)0xFFFF0000:
00000001024439E0

Разборка инструкций:

    byte* ptr2 = data + ((uint)0xFFFF0000); // redundant cast to be extra sure
00000084  mov         ecx,0FFFF0000h 
00000089  mov         rax,qword ptr [rsp+20h] 
0000008e  add         rax,rcx 
00000091  mov         qword ptr [rsp+38h],rax 

Указатель с неправильным переносом:

data + offset:
00000000024439E0

Разборка инструкций:

    uint offset = 0xFFFF0000;
0000006a  mov         dword ptr [rsp+28h],0FFFF0000h 
    byte* ptr1 = data + offset;
00000072  movsxd      rcx,dword ptr [rsp+28h] ; (1)
00000077  mov         rax,qword ptr [rsp+20h] 
0000007c  add         rax,rcx 
0000007f  mov         qword ptr [rsp+30h],rax 

Инструкция (1) преобразует беззнаковый int32 в длинное со знаком с расширением знака (ошибка или функция?).Поэтому rcx содержит 0xFFFFFFFFFFFF0000, в то время как оно должно содержать 0x00000000FFFF0000 для правильной работы сложения.

И согласно 64-битной арифметике:

0xFFFFFFFFFFFF0000 +
0x00000000024539E0 =
0x00000000024439E0

Действительно, переполнение add.

Я не знаю, является ли это ошибкой или предполагаемым поведением, я собираюсь проверить SSCLI, прежде чем пытаться дать какое-либо заключение.РЕДАКТИРОВАТЬ: см. Ответ Бена Фойгта.

...