2012-10-14 15 views
7

Załóżmy, że piszesz kompilator Java (lub podzestaw Java) i chcesz wygenerować kod bajtowy dla jednoargumentowego wyrażenia, !E. Sprawdziłeś typ, wiesz, że wiesz E ma typ boolean, to znaczy popchnie 1 lub 0 na stos operandów.Generowanie kodu bajtowego JVM dla jednoargumentowego nie wyrażenia

Jednym ze sposobów, aby to zrobić jest coś jak (w składni Jasmin):

E 
ifeq truelabel 
iconst_0 
goto stoplabel 
truelabel: 
iconst_1 
stoplabel: 

czyli jeśli istnieje 0 Na naciśnięciem stosu 1, inny Push 0. Innym sposobem, aby to zrobić, korzystając z fakt, że boolean jest tylko int o wartości 1 lub 0, to znaczy !E = (E + 1) % 2 i generować

E 
iconst_1 
iadd 
iconst_2 
irem 

Czy jest zaletą przy użyciu jednego nad drugim? Lub coś zupełnie innego?

Odpowiedz

4

Nie będę liczyć na następującą definicję, aby zachować wartość true na poziomie bajtodu.

true == 1 

Na poziomie binarnym (i jego niemal niezależny od języka), logiczną jest zazwyczaj definiowane jako

false == 0 
true != 0 

kompilator javac najwyraźniej wynika również tę definicję (wszystkie kontrole w javac kodu bajtowego Widziałem tylko zawsze sprawdza ZERO, nigdy nie przeciwko JEDNO).

I ma sens użycie tej definicji dla wartości boolowskiej zamiast traktowania tylko 1 jako prawdziwej, C także definiuje ją w ten sposób (prawda jest po prostu! = 0, nie tylko 1), a w kodzie zespołu ta konwencja jest również powszechnie używana. Tak więc java również używa tej definicji umożliwia przeniesienie/przekazanie wartości boolowskich java do innego kodu bez żadnych specjalnych konwersji.

Podejrzewam, że pierwszy przykładowy kod (z ifeq) jest jedynym sposobem prawidłowego wdrożenia operatora-operatora dla wartości logicznych. Metoda^1 (xor z 1) nie powiedzie się, jeśli wartość boolowska nie jest reprezentowana bezpośrednio jako 0/1. Każda inna wartość int powodowałaby nieprawidłowe działanie wyrażenia.

+0

Specyfikacja JVM mówi: "Wirtualna maszyna Java koduje składniki tablicy logicznej, używając 1 do reprezentowania wartości true, a 0 do reprezentowania wartości false.Jeśli wartości logiczne języka programowania Java są mapowane przez kompilatory na wartości typu maszyny wirtualnej Java int, kompilatory muszą używać to samo kodowanie. " http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.3.4 –

+0

@isbadawi To opisuje, w jaki sposób JVM koduje * tablice * booleans i jak kompilatory Java muszą koduj booleany jako ints * kiedy i jeśli to robią w ogóle. * Nie jest to bezpośrednio związane z pytaniem. – EJP

+0

@EJP Wydaje mi się to trochę niejednoznaczne. Pierwsze zdanie dotyczy tablic logicznych, ale drugie dotyczy wartości logicznych. Prawdopodobnie nie mam racji. –

5

Kiedyś próbowałem napisać dekompilator Java, więc kiedyś wiedziałem, jaki kod wygenerował javac. Jak pamiętam, javac 1.0.x używał !E = E ? false : true, podczas gdy javac 1.1 używał !E = E^1 (bitowego XOR).

+0

Nie myślałem o "^ 1"; to zdecydowanie lepsze niż drugie. Czy znasz uzasadnienie przejścia z poprzedniego na drugie, lub dlaczego "^ 1" nie było używane w pierwszej kolejności? –

+0

Właściwie to właśnie próbowałem z 'javac 1.6.0_26' i wygenerował on pierwszy (z wyjątkiem' ifne' zamiast 'ifeq'), więc domyślam się, że się przełączają. Sądzę, że wciąż zastanawiam się nad zaletami jednego z drugim. –

+1

To tylko dzikie przypuszczenie, ale być może przenieśli tego typu mikro-optymalizacje do JIT, który prawdopodobnie jest tam, gdzie ich miejsce. – Neil

0

Słyszałem, że operacje modułu mogą być bardzo powolne. Nie mam źródła, ale ma to sens, biorąc pod uwagę, o ile prostsze jest dodawanie niż dzielenie. Z drugiej strony może się zdarzyć, że problem z przeskakiwaniem licznika programu będzie zbyt duży, w takim przypadku podejście if/else nie działałoby zbyt dobrze.

Powiedziawszy to, podejrzewam, że Neil E^1 jest najszybszy, ale to tylko przeczucie. Wszystko, co musisz zrobić, to przekazać numer przez jeden obwód logiczny, i gotowe! Tylko jedna operacja, a nie garstka.

+0

^1 jest zdecydowanie * dużo * mniej intensywne obliczeniowo niż div/rem, a gałęzie warunkowe są również nieco kosztowne (ponieważ mogą powodować kary za niedostrzeganie gałęzi przy większości aktualnych architektur procesorów). Największe pytanie brzmi, czy jego słuszność zakładająca, że ​​int służący do wprowadzenia wartości boolowskiej jest ściśle ograniczona do wartości 0/1. – Durandal

+0

Wierzę w Javę, to jest. W każdym razie możesz po prostu zrobić '(E & 1)^1', jeśli nie wiesz. –

+0

(& 1)^1 konwertuje 0x2 na 0x1 (na przykład). true ==! true nie jest tym, czego się spodziewa. Nie znam żadnego prostego wyrażenia, aby uzyskać logiczne OR wszystkich 32 bitów int, aby zwinąć w pojedynczy bit. Albo booleans są * ściśle * ograniczeni, albo^- metoda nie może być użyta. – Durandal