简体   繁体   English

为什么这个添加的byte *和uint无法进入更高的dword?

[英]Why does this addition of byte* and uint fail to carry into the higher dword?

Now filed on Microsoft Connect ; 现在提交Microsoft Connect ; please upvote if you feel it needs fixing. 如果您觉得需要修理请立即投票。 I've also simplified the test case a lot: 我也简化了测试用例:

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

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

Looking at the IL, as far as I can tell, the C# compiler did everything right, and the bug lies in the JITter. 看看IL,就我所知,C#编译器做的一切都正确,错误在于JITter。


Original question : What is going on here? 原始问题 :这里发生了什么?

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);

This test requires a 64-bit OS targeting the x64 platform. 此测试需要针对x64平台的64位操作系统。

Output: 输出:

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

So is this a bug or did I mess something up? 那么这是一个错误还是我搞砸了什么?

(Answer under construction) (正在建设中的答案)

I checked the emitted x64 asm and these are my observations: 我检查了发出的x64 asm,这些是我的观察结果:

Base pointer: 基指针:

data:
00000000024539E0

Pointer with correct carry: 带正确携带的指针:

data + (uint)0xFFFF0000:
00000001024439E0

Disassembly of the instructions: 拆卸说明:

    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 

Pointer with incorrect carry: 携带不正确的指针:

data + offset:
00000000024439E0

Disassembly of the instructions: 拆卸说明:

    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 

The instruction (1) converts an unsigned int32 into a signed long with sign extension (bug or feature?). 指令(1)将unsigned int32转换为带符号扩展(bug或feature?)的带符号long。 Therefore rcx contains 0xFFFFFFFFFFFF0000 , while it should contain 0x00000000FFFF0000 for the addition to work properly. 因此rcx包含0xFFFFFFFFFFFF0000 ,而它应该包含0x00000000FFFF0000以使加法正常工作。

And according to 64 bit arithmetic: 并根据64位算术:

0xFFFFFFFFFFFF0000 +
0x00000000024539E0 =
0x00000000024439E0

The add overflows indeed. 添加溢出确实。

I don't know if this is a bug or intended behavior, I'm going to check SSCLI before trying to give any conclusion. 我不知道这是一个错误或预期的行为,我会在尝试给出任何结论之前检查SSCLI。 EDIT: See Ben Voigt's answer. 编辑:见Ben Voigt的回答。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM