Chciałbym zadać kilka pytań na temat języka asemblera. Rozumiem, że jest bardzo zbliżony do języka maszynowego, dzięki czemu jest szybszy i bardziej wydajny.
Skoro mamy różne architektury komputerów, czy to oznacza, że muszę pisać inny kod w asemblerze dla różnych architektur? Jeśli tak, to dlaczego nie jest Zgromadzenie, napisz raz - biegnij wszędzie, językiem? Czy nie byłoby łatwiej po prostu uczynić go uniwersalnym, tak aby napisać go tylko raz i można go uruchomić na praktycznie dowolnej maszynie o różnych konfiguracjach? (Myślę, że byłoby to niemożliwe, ale chciałbym uzyskać konkretne, szczegółowe odpowiedzi)
Niektórzy ludzie mogą powiedzieć, że C jest językiem, którego szukam. Nie korzystałem wcześniej z C, ale myślę, że wciąż jest to język wysokiego poziomu, choć prawdopodobnie na przykład szybszy niż Java. Mogę się tutaj mylić.
Odpowiedzi:
Język asemblera to sposób pisania instrukcji dla zestawu instrukcji komputera , w sposób nieco bardziej zrozumiały dla programistów.
Różne architektury mają różne zestawy instrukcji: zestaw dozwolonych instrukcji jest różny dla każdej architektury. Dlatego nie możesz mieć nadziei, że będziesz mieć program asemblujący po uruchomieniu w dowolnym miejscu. Na przykład zestaw instrukcji obsługiwanych przez procesory x86 wygląda zupełnie inaczej niż zestaw instrukcji obsługiwanych przez procesory ARM. Jeśli napisałeś program asemblera dla procesora x86, miałby on wiele instrukcji, które nie są obsługiwane przez procesor ARM i odwrotnie.
Podstawowym powodem używania języka asemblerowego jest to, że pozwala on na bardzo niski poziom kontroli nad twoim programem i pozwala korzystać ze wszystkich instrukcji procesora: dostosowując program do korzystania z funkcji, które są unikalne dla konkretnego procesora będzie działać, czasem możesz przyspieszyć program. Filozofia „pisz raz i wszędzie” jest zasadniczo sprzeczna z tym.
źródło
DEFINICJA języka asemblera polega na tym, że jest to język, który można tłumaczyć bezpośrednio na kod maszynowy. Każdy kod operacji w języku asemblera przekłada się na dokładnie jedną operację na komputerze docelowym. (Cóż, jest to trochę bardziej skomplikowane: niektóre asemblery automatycznie określają „tryb adresowania” na podstawie argumentów kodu operacyjnego. Ale nadal zasada jest taka, że jedna linia zestawu tłumaczy się na jedną instrukcję języka maszynowego.)
Bez wątpienia można wymyślić język, który wyglądałby jak język asemblera, ale zostałby przetłumaczony na różne kody maszynowe na różnych komputerach. Ale z definicji nie byłby to język asemblera. Byłby to język wyższego poziomu, który przypomina język asemblera.
Twoje pytanie przypomina trochę: „Czy można stworzyć łódź, która nie unosi się na wodzie lub nie ma innego sposobu na podróżowanie po wodzie, ale ma koła i silnik i może podróżować na lądzie?” Odpowiedź byłaby taka, że z definicji taki pojazd nie byłby łodzią. Brzmi bardziej jak samochód.
źródło
Nie ma koncepcyjne (Przypuszczam, żaden komputer nauki ) powód przeciwko posiadające jedną asemblera dla wszystkich komputerów na świecie. W rzeczywistości ułatwiłoby to wiele rzeczy. Jeśli chodzi o teorię, w każdym razie są one takie same, aż do jakiejś funky bijection.
W praktyce jednak istnieją różne układy scalone do różnych celów, z różnymi operacjami i zasadami projektowania (np. RISC vs. CISC), które służą różnym celom, a zestawy instrukcji, które je obsługują, a także języki asemblera są różne. Ostatecznie odpowiedź jest taka sama, jak w przypadku pytania, dlaczego istnieje tak wiele różnych języków programowania : różne cele, różne decyzje projektowe.
To powiedziawszy, możesz oczywiście wprowadzić poziomy abstrakcji, aby dostać się do jakiegoś wspólnego interfejsu. na przykład x86 zostało wyeliminowane na poziomie chipa od dłuższego czasu; jest mały sprzęt, który tłumaczy instrukcje x86 na wszystko, z czym procesor naprawdę współpracuje. Języki takie jak C byłyby kolejnym krokiem od sprzętu (nawet jeśli prawdopodobnie niewielkim), aż do języków takich jak Haskell, Java lub Ruby. Tak, kompilator jest jednym z głównych osiągnięć informatyki, ponieważ umożliwia rozdzielenie problemów w ten sposób.
źródło
Wspominasz wyrażenie „pisz raz, biegnij gdziekolwiek”, nie zauważając jego znaczenia. To jest slogan marketing dla Sun Microsystems , który komercyjnie wynalazł pojęcie „maszynie wirtualnej” i „bytecodes” dla Javy, choć być może pomysł mógł powstać w środowisku akademickim 1 st. Pomysł został później skopiowany przez Microsoft dla .Net po tym, jak Sun pozwał ich za naruszenie licencji Java. Bytecodes Java są implementacją idei asemblera między maszynami lub języka maszynowego. Są one używane w kilku innych językach niż Java i teoretycznie mogą być użyte do kompilacji dowolnego języka. Po wielu latach bardzo zaawansowanej optymalizacji, Java jest bliska wydajności skompilowanym językom, co pokazuje, że cel w postaci wysokowydajnej, niezależnej od platformy technologii maszyn wirtualnych jest ogólnie możliwy do osiągnięcia.
Kolejny nowy pomysł na wczesnych etapach / w obiegu związany z Twoimi wymaganiami nazywa się projektem ponownego obliczenia i jest przeznaczony do badań naukowych, chociaż można go wykorzystać do innych celów. Chodzi o to, aby eksperymenty obliczeniowe były powtarzalne za pomocą technologii maszyn wirtualnych. Jest to głównie idea symulacji różnych architektur maszyn na dowolnym sprzęcie.
źródło
Powody wysokiego poziomu
Kiedy się nad tym zastanowić, mikroprocesor robi niesamowitą rzecz: pozwala zabrać maszynę (taką jak pralka lub winda) i zastąpić cały kawałek niestandardowych mechanizmów lub obwodów tanim, masowo produkowanym krzemem żeton. Zaoszczędzisz dużo pieniędzy na częściach i dużo czasu na projekt.
Ale poczekaj, standardowy układ zastępujący niezliczone niestandardowe projekty? Nie może być jednego idealnego mikroprocesora, który byłby idealny dla każdego zastosowania. Niektóre aplikacje muszą minimalizować zużycie energii, ale nie muszą być szybkie; inne muszą być szybkie, ale nie muszą być łatwe do zaprogramowania, inne muszą być tanie, itp.
Mamy więc wiele różnych „smaków” mikroprocesora, z których każdy ma swoje mocne i słabe strony. Pożądane jest, aby wszyscy korzystali z kompatybilnego zestawu instrukcji, ponieważ umożliwia to ponowne użycie kodu i ułatwia znalezienie osób o odpowiednich umiejętnościach. Jednak zestaw instrukcji nie wpływa na koszty, złożoność, szybkość, łatwość użycia i fizyczne ograniczenia procesora, a więc mamy kompromis: istnieje kilka „mainstreamowe” zestawy instrukcji (i wiele z nich moll), a w każdym zestawie instrukcji znajduje się wiele procesorów o różnych właściwościach.
Aha, a wraz ze zmianą technologii zmieniają się wszystkie kompromisy, więc ewoluują zestawy instrukcji, pojawiają się nowe, a stare umierają. Nawet gdyby istniał dziś „najlepszy” zestaw instrukcji, może nie być za 20 lat.
Szczegóły sprzętu
Prawdopodobnie największą decyzją projektową w zestawie instrukcji jest rozmiar słowa , tzn. Jak duża liczba procesor może „naturalnie” manipulować. 8-bitowe procesory obsługują liczby od 0 do 255, podczas gdy 32-bitowe procesory obsługują liczby od 0 do 4 294 967 295. Kod zaprojektowany dla jednego musi być całkowicie przemyślany dla innego.
Nie chodzi tylko o tłumaczenie instrukcji z jednego zestawu instrukcji na inny. W innym zestawie instrukcji może być preferowane zupełnie inne podejście. Na przykład na 8-bitowym procesorze tablica odnośników może być idealna, podczas gdy na 32-bitowym procesorze lepiej byłoby wykonać operację arytmetyczną w tym samym celu.
Istnieją inne główne różnice między zestawami instrukcji. Większość instrukcji dzieli się na cztery kategorie:
Procesory różnią się rodzajem obliczeń, które mogą wykonywać, a także sposobem podejścia do przepływu kontroli, transferu danych i konfiguracji procesora.
Na przykład niektóre procesory AVR nie mogą się zwielokrotniać ani dzielić; podczas gdy wszystkie procesory x86 mogą. Jak można sobie wyobrazić, wyeliminowanie obwodów wymaganych do zadań takich jak mnożenie i dzielenie może uczynić procesor prostszym i tańszym; operacje te mogą być nadal wykonywane przy użyciu procedur oprogramowania, jeśli są potrzebne.
x86 pozwala instrukcjom arytmetycznym ładować operandy z pamięci i / lub zapisywać wyniki w pamięci; ARM jest architekturą przechowującą ładunki, dlatego ma tylko kilka dedykowanych instrukcji dostępu do pamięci. Tymczasem x86 ma dedykowane instrukcje rozgałęzienia warunkowego, podczas gdy ARM pozwala na warunkowe wykonanie praktycznie wszystkich instrukcji. Ponadto ARM pozwala na przeprowadzanie przesunięć bitów w ramach większości instrukcji arytmetycznych. Różnice te prowadzą do różnych charakterystyk wydajności, różnic w projekcie wewnętrznym i koszcie chipów oraz różnic w technikach programowania na poziomie języka asemblera.
Wniosek
Powodem niemożności posiadania uniwersalnego języka asemblera jest to, że aby poprawnie przekonwertować kod asemblera z jednego zestawu instrukcji na inny, należy od nowa zaprojektować kod - czego nie mogą jeszcze zrobić komputery.
źródło
Dodając do cudownej odpowiedzi DW: jeśli chciałbyś mieć jednego asemblera, musiałby on utrzymywać wszystkie architektury, idealny między nimi tłumacz i w pełni zrozumieć, co robisz.
Niektóre wysoce zoptymalizowane kody na jedną architekturę musiałyby zostać dezoptymalizowane, zrozumiane na bardziej abstrakcyjnym poziomie i zoptymalizowane pod kątem innej.
Ale gdyby to było możliwe, mielibyśmy doskonały kompilator C, a pisanie w czystym asemblerze nie byłoby w ogóle korzystne.
Głównym punktem używania asemblera jest wydajność, której nie można wycisnąć z najnowszych kompilatorów.
Napisanie takiego programu byłoby jeszcze trudniejsze niż istniejące kompilatory, a utrzymanie wszystkich tworzonych architektur jeszcze bardziej utrudniłoby.
A dla programu „tylko jeden” oznaczałoby to również pełną zgodność wsteczną.
źródło
Microsoft wynalazł MSIL jako pośredni język asemblera. Programy będą kompilowane z C # lub VB.Net do MSIL. W czasie wykonywania plik MSIL został skompilowany do kodu maszynowego komputera, na którym był uruchomiony przy użyciu kompilatora JIT . Plik zawierający MSIL był plikiem .EXE z kilkoma instrukcjami na początku w X86, aby uruchomić program. W procesorze ARM należy wpisać słowo mono przed nazwą programu, aby go uruchomić.
źródło
Jak wspomniano, LLVM jest jak dotąd najbliższą rzeczą. Dużą barierą dla naprawdę uniwersalnego języka będą fundamentalne różnice związane z domniemanymi kompromisami: współbieżność, wykorzystanie pamięci, przepustowość, opóźnienie i zużycie energii. Jeśli piszesz wyraźnie w stylu SIMD, możesz używać zbyt dużej ilości pamięci. Jeśli piszesz jawnie w stylu SISD, uzyskasz suboptymalną równoległość. Jeśli zoptymalizujesz przepustowość, zranisz opóźnienie. Jeśli zmaksymalizujesz przepustowość jednowątkową (tj. Prędkość zegara), zepsujesz żywotność baterii.
Przynajmniej kod musiałby być opatrzony adnotacjami z kompromisami. Najważniejsze może być to, że język ma dobre właściwości algebraiczne / typu, które dają kompilatorowi dużo miejsca na optymalizację i wykrywanie logicznej niespójności.
Następnie pojawia się kwestia nieokreślonego zachowania. Duża część języków C i asemblera pochodzi z nieokreślonego zachowania. Jeśli przyznajesz się do nieokreślonego zachowania, które faktycznie się dzieje, ostatecznie traktujesz je jako przypadki szczególne (np. Włamania do architektury i kontekstu).
źródło
Być może to, czego szukasz, to notacja Universal Turning Machine, w której wszyscy zgadzają się co do symboli poleceń. ( https://en.wikipedia.org/wiki/Universal_Turing_machine )
„Asembler”, który tłumaczy język dopuszczalny pod kątem Turning na kod maszynowy konkretnego dostawcy i może być budowany dla dowolnej z tych rzeczy, które nazywamy komputerami.
W sztuce programowania komputerowego znajduje się przykład tego, jak może to wyglądać.
Zastanówmy się jednak nad pytaniem „dlaczego nie jest to dostępny na rynku uniwersalny język, którego można używać na wszystkich komputerach”. Sugeruję, że najbardziej dominującymi wpływami są (1) wygoda, nie wszystkie języki asemblera są najbardziej wygodne w użyciu; (2) ekonomia, zapewnienie, niekompatybilność maszyn różnych marek i sprzedawców to strategia biznesowa, a także wynik ograniczonych zasobów (czasu / pieniędzy) na projektowanie maszyn.
źródło
założenie: kompilacja i optymalizacja języka wysokiego poziomu L1 do języka niższego poziomu L0 jest łatwiejsza niż kompilacja i optymalizacja języka wysokiego poziomu L2 (wyższego niż L1) do L0; łatwiejsze w tym sensie, że podobno można wygenerować bardziej zoptymalizowany kod podczas kompilacji L1 do L0 niż L2 do L0.
Myślę, że założenie jest prawdopodobnie poprawne, dlatego prawdopodobnie większość kompilatorów używa niskiego poziomu języka pośredniego (IR / LLVM).
jeśli jest to prawdą, użyj dowolnego L0 języka niskiego poziomu i napisz kompilatory, aby przetłumaczyć L0 na inne języki niskiego poziomu. Na przykład użyj zestawu instrukcji MIPS i skompiluj go do x86, arm, power, ...
-Taoufik
źródło