W poprzednim poście opisałem jak działa stos w IL. Wiemy, że jest on kluczowy dla wszelkich operacji. Jedną z ważniejszych instrukcji, wprowadzonych w tamtym poście jest ldc.i4. Służy ona do załadowania liczby 4 bajtowej na stos.
Oprócz niej, istnieje wiele innych instrukcji, które pełnią analogiczną rolę. Rozważmy następujący kod c#:
float floatNumber = 533.3f; double doubleNumber = 5454.14; string text = "text"; int[] array = new int[] {21, 3}; Console.WriteLine(floatNumber); Console.WriteLine(doubleNumber); Console.WriteLine(text);
Float zostanie załadowany za pomocą:
ldc.r4 533.3
Instrukcja analogiczna do wspomnianej ldc.i4. Cyfra „4” oznacza liczbę bajtów, a przedrostek „r” lub „i” kolejno real (zmiennoprzecinkowa) i integer (całkowita).
Dla double będzie to ldc.r8, ponieważ jest to liczba 8-bajtowa. String z kolei ma osobną instrukcję ldstr:
ldstr "text"
To jeszcze nie wszystko. W IL istnieje wiele skrótów. Oczywiście programistów c# zwykle takie szczegóły nie interesują. Ułatwia to jednak czytanie kodu IL, kiedy pracujemy nad jakąś optymalizacją albo gdy po prostu aplikacja nie działa poprawnie.
W celu zaprezentowania skrótów do powyższych instrukcji spójrzmy na:
int minusOne = -1; int plusOne = 1; bool falseVaue = false; bool trueValue = true; object objectNull = null;
Powyższe wartości maja specjalne instrukcje. Zamiast np. pisać ldc.i4 -1 mamy ldc.i4.m1. Podsumowując, powyższy kod wygeneruje kolejno:
• ldc.i4.m1 – załadowanie -1
• ldc.i4.1 – załadowanie +1
• ldc.i4.0 – załadowanie false (Boolean)
• ldc.i4.1 – załadowanie true (Boolean)
• ldnull – załadowanie null
W dokumentacji znajdziemy wiele innych skrótów, ale są one analogiczne do powyższych i dość oczywiste.
Myślę, że przy okazji stosu, warto również wspomnień o:
• pop – instrukcja zdejmuje element ze stosu
• dup – instrukcja duplikujecie element znajdujący się na górze stosu.
Funkcja pop jest dość oczywista i po prostu usunie wartość ze stosu. Najprostszy kod, obrazujący pop to:
int a = 5; a.ToString(); int b = 5;
Wywołanie ToString spowoduje umieszczenie wyniku na stosie. Nie jest on nigdzie używany, zatem należy go usunąć potem (przed int b=5) ze stosu:
ldc.i4.5 stloc.0 ldloca.s a call instance string [mscorlib]System.Int32::ToString() pop
Instrukcja dup z kolei ma na celu optymalizację. Bardzo często musimy mieć duplikat wartości na stosie. Na przykład, jeśli chcemy tą samą liczbę umieścić w zmiennej lokalnej i wykonać jakaś operacje na niej. Instrukcja stloc (umieszczenie wartości w zmiennej lokalnej) zdejmuje również wartość ze stosu, a wszelkie operacje operują wyłącznie na stosie.
W poprzednim wpisie pokazałem również instrukcje add. Oczywiście istnieje szereg innych operacji tzn.:
- 1. neg – negacja (!)
- 2. sub – odejmowanie
- 3. mul – mnożenie
- 4. div – dzielenie
- 5. rem – reszta z dzielenia
- 6. shl – przesuniecie o jeden bit w lewo
- 7. shr- analogicznie w prawo
- 8. operatory bitowe: and, or, xor, not
Jeśli mamy dwie wartości (a,b) na stosie, wykonanie odpowiedniej instrukcji umieści cyfrę 1 albo 0, w zależności od operatora i relacji argumentów. Dostępne instrukcje to: clt, cle, cgt, cge, ceq, cne.