2013-01-07 8 views
12

Dodając int32 do 64 bitów native int, czy CLR logowania rozszerzyć lub zerowej przedłużenie 32-bitową liczbę całkowitą? A co najważniejsze: na podstawie jakich informacji dokonuje tego wyboru?Wynik dodania int32 do 64-bitowego natywnego int?


Piszę kompilatora .NET i przeczytałem specyfikacji ECMA throughly, ale nie mógł znaleźć odpowiedź.

CLI obsługuje tylko podzbiór tych typów w jego operacji na wartościach zapisanych na stosie ocena: int32, int64 i native int.
- ECMA 335 sekcja I 12.1: Obsługiwane typy danych

Ponieważ wartości na stosie oceny nie mają informacji na temat ich signedness, instrukcje dla którego uzyskiwany znak argumentów liczą mieć dwa warianty: jeden dla podpisany i jeden dla liczb całkowitych bez znaku. W add, sub i mul instrukcje (te, które nie sprawdzają przepełnienia) nie muszą się martwić o uzyskiwany znak argumentów tak długo jak operandy są tej samej wielkości, a zatem mają tylko jeden wariant. Jednak argumenty nie zawsze są tej samej wielkości ...

ECMA 335 sekcja III 1.5: Argument typu tabela stwierdza, że ​​int32 i native int można dodać, odjąć, mnoży i dzieli. Rezultatem jest ponownie native int. W systemie 64-bitowym numer native int ma szerokość 64 bitów.

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 

Jaki byłby rezultat? Należy zauważyć, że zgodnie ze specyfikacją, środowisko wykonawcze nie musi śledzić dokładnych typów lub uzyskiwany znak wartości na stosie: wie tylko int32, int64 i native int (i kilka innych, które nie są istotne tutaj).


Mogę sobie wyobrazić, że IntPtr i UIntPtr arytmetyka, ponieważ jest wewnętrznie reprezentowane rodzimych wskazówki, również korzysta z tego rodzaju dodatek. Jednak ILSpy pokazuje, że dodanie IntPtr oraz Int32 w C# wywołuje przeciążony operator + na klasie IntPtr, który akceptuje tylko podpisane Int32 argument.

Wykonanie tego bezpośrednio w CIL (przy użyciu instrukcji add) wskazuje również, że liczba całkowita jest interpretowana jako podpisana. Powinien również zostać zaimplementowany w Mono, ale nie mogłem znaleźć żadnych odniesień do poparcia moich odkryć.

+0

Znak promowanej wartości nie zostanie przechwycony. Zobacz te informacje o możliwych rozwiązaniach (dotyczy to komputerów Mac, ale nie powinno to mieć znaczenia w tym przypadku): https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/64bitPorting/MakingCode64-BitClean/MakingCode64- BitClean.html – K3N

Odpowiedz

5

Podpisanie nie ma znaczenia przy dodawaniu dwóch wartości tego samego bitu. Na przykład dodanie 32-bitowego -10 (0xfffffff6) do 32-bitowego 10 (0x0000000a) spowoduje prawidłowe wygenerowanie 0. Z tego powodu istnieje tylko jedna instrukcja add w CIL (Common Instruction Language).

Jednak przy dodawaniu dwóch wartości różniących się bitem, liczy się podpis . Na przykład dodanie 32-bitowego -10 do 64-bitowego 10 może skutkować 4294967296 (0x100000000) po zakończeniu unsigned, a 0 po podpisaniu.

CIL add instrukcja pozwala na dodanie natywnego całkowitą i 32-bitową liczbę całkowitą. Natywna liczba całkowita może być 64-bitowa (w systemie 64-bitowym). Testowanie ujawnia, że ​​add traktuje 32-bitową liczbę całkowitą jako liczbę całkowitą ze znakiem i znak - rozszerza ją.This is not always correct i może być uznane za a bug. Microsoft obecnie nie zamierza go naprawić.

Ponieważ kontrola przepełnienia zależy od tego, czy argumenty są traktowane jako unsigned lub podpisane, są dwa warianty add.ovf: add.ovf (znakiem) i add.ovf.un (znaku). Jednak warianty te również poprawnie wypisują-przedłużenie -rozszerzenia mniejszego operandu podczas dodawania 32-bitowej liczby całkowitej do natywnej liczby całkowitej.

Dodanie naturalnej liczby całkowitej i niepodpisanej 32-bitowej liczby całkowitej może dać różne wyniki w zależności od ustawienia sprawdzania przepełnienia C#. Najwyraźniej fakt, że nie mogłem tego zrozumieć, jest wynikiem błędu lub niedopatrzenia w projekcie języka CIL.

2

Jesteś na niezbadanym terytorium, nie znam żadnego języka .NET, który faktycznie na to pozwala. Ich kontroler składni odrzuca dowolny kod, który próbuje to zrobić. Nawet dodanie dwóch natywnych znaków jest odrzucane. Ostatecznie do jittera należy generowanie kodu maszynowego. Jeśli chcesz wiedzieć, co się dzieje, po prostu eksperymentuj. Pamiętaj, aby przetestować przynajmniej drgawki x86 i x64.

Biorąc pod uwagę wątpliwą semantykę i bardzo realną możliwość, że przyszła zmiana jittera może złamać twoje założenia, zdecydowanie zaleciłbym, abyś także odrzucił to w swoim własnym języku.To po prostu nie jest zbyt użyteczne i proste obejście, które rzuca na (długie), a wynik z powrotem do (IntPtr) ma dobrze zdefiniowaną semantykę. Co samo w sobie jest sposobem na uzyskanie przewidywalnego zachowania we własnym generatorze kodu.

+0

Dodałem trochę informacji do wpisu: C# używa przeciążonego operatora dodawania przy dodawaniu 'IntPtr' do' Int32'. W języku CIL instrukcja 'add' wydaje się interpretować' Int32' jako podpisaną. Jednak nie wiem dlaczego. – Virtlink

Powiązane problemy