Στην προηγούμενη ανάρτησή μου, The MRISC32 – Μια διανυσματική σχεδίαση πρώτης CPU, πέρασα από μερικές από τις αρχές του MRISC32 ISA. Σε αυτήν την ανάρτηση θα ήθελα να εστιάσω σε βαθμωτές λειτουργίες και να παρουσιάσω μερικά από τα χαρακτηριστικά που ξεχωρίζουν το ISA από άλλα.
Πρόσφατα έκανα κάποια πρόοδο με ένα GCC back end για MRISC32και αυτό διευκόλυνε τη σύγκριση του κώδικα MRISC32 με άλλες αρχιτεκτονικές, όπως x86, ARM και RISC-V, και ορισμένες αποφάσεις σχεδίασης ISA έχουν πραγματικά αποδειχθεί επιτυχείς.
Σημείωση: Στα παρακάτω παραδείγματα, το GCC -O2 χρησιμοποιήθηκε για τη δημιουργία του κώδικα. Κάθε φορά που παρουσιάζεται ο κώδικας μηχανής, τα ακατέργαστα byte που αντιπροσωπεύουν τα περιεχόμενα της μνήμης δίνονται σε εξάγωνο στα αριστερά και η αντίστοιχη σύνταξη συναρμολόγησης δίνεται στα δεξιά.
Επεξεργασία: Ορισμένα τμήματα του ISA έχουν αλλάξει από τότε που γράφτηκε αυτό το άρθρο (π.χ. ονόματα μητρώου και οδηγίες LDI). Δείτε τα πιο πρόσφατα Εγχειρίδιο MRISC32 Instruction Set.
Φόρτωση άμεσων τιμών
Το MRISC32 ISA έχει τρεις οδηγίες που είναι αφιερωμένες στη φόρτωση άμεσων τιμών σε καταχωρητές: ldi (φόρτωση αμέσως), ldhi (φόρτωση ψηλά άμεσα) και ldhio (φόρτωση ψηλά αμέσως με χαμηλά). Κάθε εντολή φορτώνει μια άμεση τιμή 21-bit σε έναν καταχωρητή, όπως φαίνεται παρακάτω:

Μαζί οι τρεις οδηγίες επιτρέπουν τη φόρτωση μιας μεγάλης ποικιλίας άμεσων τιμών, π.χ. ακέραιοι στην περιοχή -1048576 έως 1048575, και μάσκες bit όπως 0x7ffffffff και 0x00ff0000.
Μια από τις πιο ενδιαφέρουσες πτυχές εμφανίζεται όταν χρησιμοποιείτε τις παραλλαγές „υψηλού“ για τη φόρτωση άμεσων τιμών κινητής υποδιαστολής: δεδομένου ότι και οι δύο τιμές ακέραιου και κινητής υποδιαστολής αποθηκεύονται στους ίδιους καταχωρητές στο MRISC32, οι ίδιες οδηγίες μπορούν να χρησιμοποιηθούν για τη φόρτωση ακεραίων και άμεσες τιμές κινητής υποδιαστολής.
Φορτώνοντας τα υψηλά 21 bit μιας τιμής κινητής υποδιαστολής IEEE 754 32 bit, έχετε αρκετές πληροφορίες για να περιγράψετε: το bit πρόσημου, τον εκθέτη των 8 bit και ένα mantissa 12 bit (13 ενεργά bit σημαντικότητας και). Αυτό σημαίνει ότι με μία μόνο εντολή είναι δυνατό να φορτωθεί ένα ευρύ φάσμα τιμών κινητής υποδιαστολής, συμπεριλαμβανομένων όλων των ακεραίων στο εύρος -8191,0 έως +8191,0. Στην πραγματικότητα, εφόσον ένας αριθμός μπορεί να γραφτεί ως (+/-)m * 2^nπου m ∈ [4096, 8191] και n ∈ [-138, 115]μπορεί να φορτωθεί χρησιμοποιώντας ένα μόνο ldhi εντολή.
Ως παράδειγμα, πάρτε τον ακόλουθο κώδικα που υπολογίζει 511,9375 * x:

Ανάλυση
Μεταξύ των αρχιτεκτονικών που χρησιμοποιούνται, μόνο το MRISC32 φορτώνει την τιμή κινητής υποδιαστολής (511,9375 ή 0x43fff800 σε δεκαεξαδική σημείωση) χρησιμοποιώντας μια εντολή load-immediate. Όλες οι άλλες αρχιτεκτονικές θα εκδώσουν μια εντολή φόρτωσης δεδομένων (δηλαδή θα φορτώσουν την τιμή κινητής υποδιαστολής μέσω της κρυφής μνήμης δεδομένων).
Αν και δεν γνωρίζω τις λεπτομέρειες υλοποίησης της σύγχρονης CPU:s x86-64, υποπτεύομαι ότι εσωτερικά mulss Η εντολή θα χωριστεί σε μια εντολή φόρτωσης δεδομένων ακολουθούμενη από την πραγματική εντολή πολλαπλασιασμού (καθιστώντας την πολύ παρόμοια με το ARMv7 σε αυτήν την περίπτωση).
Αξίζει επίσης να σημειωθεί ότι το MRISC32 έχει τον πιο συμπαγή κώδικα (8 bytes), ενώ το RISC-V καταναλώνει διπλάσια μνήμη (16 bytes) για να κάνει την ίδια λειτουργία. Για τη φόρτωση μιας πλήρους τιμής κινητής υποδιαστολής 32 bit, η έκδοση MRISC32 θα απαιτούσε μία ακόμη εντολή (στο Ή στα χαμηλότερα 11 bit της τιμής).
Γιατί είναι σημαντικό?
Η τοποθέτηση της άμεσης τιμής απευθείας στη ροή εντολών, αντί για τη φόρτωσή της χρησιμοποιώντας μια εντολή φόρτωσης δεδομένων, έχει πολλά πλεονεκτήματα:
- Ο τελεστής εντολής είναι έτοιμος μόλις αποκωδικοποιηθεί η εντολή, αντί να χρειάζεται να περιμένει το αποτέλεσμα μιας προηγούμενης εντολής φόρτωσης μνήμης.
- Η κρυφή μνήμη δεδομένων δεν μολύνεται με τελεστές εντολών.
- Αποφεύγετε πιθανή απώλεια της προσωρινής μνήμης δεδομένων.
- Ο κώδικας τείνει να είναι πιο πυκνός (αντί να κωδικοποιήσετε μια αναφορά μνήμης + την τιμή δεδομένων, πρέπει μόνο να κωδικοποιήσετε την τιμή δεδομένων).
Οι οδηγίες ελάχ./μέγ
Σε κάθε ISA που έχω χρησιμοποιήσει, έχω χάσει ακέραιες εντολές min/max στο βασικό σύνολο εντολών (αν και πολλές αρχιτεκτονικές υποστηρίζουν min και max για καταχωρητές κινητής υποδιαστολής και SIMD). Αυτός είναι ένας λόγος που πρόσθεσα το ελάχ, Μέγιστη, δικος μου και Μέγιστη οδηγίες για το MRISC32 ISA. Ωστόσο, υπάρχουν δύο λόγοι για τους οποίους είναι πραγματικά απαραίτητα:
- Για να κάνω τους διανυσματικούς βρόχους αποτελεσματικούς, χρειαζόμουν έναν τρόπο για να προσδιορίσω γρήγορα τον αριθμό των διανυσματικών στοιχείων προς επεξεργασία σε κάθε επανάληψη βρόχου. ο ελάχ η οδηγία έλυσε κομψά αυτό το πρόβλημα (δηλ. υπολογίστε αριθμός_στοιχείων = ελάχ.(αριθμός_στοιχείων_αριστερά, διάνυσμα_μέγεθος_καταχώρισης)).
- Όλα τα SIMD ISA:s που γνωρίζω περιλαμβάνουν ακέραιες οδηγίες ελάχ./μέγ. και για καλούς λόγους. Έτσι, ήταν φυσικό να συμπεριληφθούν οδηγίες ελάχ./μέγ. για λειτουργίες διανύσματος MRISC32. Δεδομένου ότι όλες οι εντολές υποστηρίζουν τόσο διανυσματικούς όσο και βαθμωτούς τελεστές, φυσικά λαμβάνουμε βαθμωτές εντολές ελάχιστων/μέγιστων «δωρεάν».
Τώρα, καθώς οι οδηγίες min και max αποτελούν μέρος του MRISC32 ISA, μπορούν να χρησιμοποιηθούν σωστά. Για παράδειγμα, λάβετε υπόψη τον ακόλουθο κώδικα C, ο οποίος υπολογίζει z = min(abs(x), abs(y)):
int abs_x = (x < 0 ? -x : x);
int abs_y = (y < 0 ? -y : y);
int z = abs_x < abs_y ? abs_x : abs_y;
Αυτό μεταφράζεται από το GCC ως εξής:

Ανάλυση
Ενώ άλλες αρχιτεκτονικές βασίζονται σε αναθέσεις υπό όρους ή διακλαδώσεις για την επίλυση του προβλήματος, το MRISC32 μπορεί να χρησιμοποιήσει απευθείας τις οδηγίες min/max και:
- Εκτελέστε την ίδια εργασία σε λιγότερες οδηγίες.
- Αποφύγετε τη χρήση/τροποποίηση του καταχωρητή κωδικού συνθήκης (CC) (στην πραγματικότητα, το MRISC32 δεν διαθέτει καθόλου καταχωρητή κωδικού συνθήκης).
- Αποφύγετε τα κλαδιά.
Σημειώστε επίσης ότι οι ακέραιες οδηγίες min/max μπορούν να χρησιμοποιηθούν για την αποτελεσματική εφαρμογή λειτουργιών όπως abs (όπως φαίνεται παραπάνω) και clamping/saturation.
Στη λύση x86 μπορούμε να παρατηρήσουμε την επίδραση των καταστροφικών τελεστών (συνήθη για παλαιότερα CISC ISA:s): καθώς ένας από τους τελεστές πηγής είναι και ο τελεστής προορισμού, κάποια επιπλέον κιν χρειάζονται οδηγίες, κάτι που δεν ισχύει για τις περισσότερες αρχιτεκτονικές RISC (συμπεριλαμβανομένου του MRISC32).
Η οδηγία SHUF
Μία από τις αγαπημένες μου οδηγίες στο MRISC32 ISA είναι η ανακάτεμα οδηγίες (μπορείτε να διαβάσετε περισσότερα σχετικά εδώ).
ο ανακάτεμα Η οδηγία είναι πραγματικά μια οδηγία πολλαπλών χρήσεων που μπορεί να κάνει πολλά διαφορετικά πράγματα. Για παράδειγμα:
- Αναδιάταξη (ανακάτεμα) byte, π.χ. εναλλαγή της σειράς byte (μετατροπές endianness).
- Εκτελέστε προέκταση πρόσημου ή μηδενισμού, π.χ. byte -> λέξη.
- Αποσυσκευασία συσκευασμένων δεδομένων, π.χ. εξαγωγή της άνω μισής λέξης από μια λέξη.
- Εκτελέστε μάσκες byte, π.χ y = x & 0x00ff00ff.
Και φυσικά μπορείτε να κάνετε συνδυασμούς όλων των παραπάνω. Για παράδειγμα: αποσυσκευάστε μια μισή λέξη, αλλάξτε το endianness και το σημάδι επέκτασης – όλα με μία μόνο οδηγία.
Η οδηγία είναι εμπνευσμένη από το x86 SSSE3 PSHUFB οδηγίες, αλλά το κάνει ένα βήμα παραπέρα προσθέτοντας τη δυνατότητα υπογραφής-γεμίσματος μιας υποδοχής byte αντί να μηδενίζεται. Ένας από τους τελεστές στο ανακάτεμα Η οδηγία είναι μια λέξη 13-bit που ελέγχει τη λειτουργία, επομένως υπάρχουν κυριολεκτικά αρκετές χιλιάδες μοναδικές λειτουργίες που μπορείτε να εκτελέσετε με αυτήν.
Σε ένα RISC ISA με περιορισμένο χώρο εντολών, η ύπαρξη μιας μεμονωμένης εντολής που είναι τόσο ευέλικτη είναι πολύ πολύτιμη.
Και το καλύτερο μέρος είναι ότι είναι πραγματικά εύκολο εφαρμογή σε υλικό (είναι ουσιαστικά μια μήτρα εκ νέου καλωδίωσης).
Ενοποιημένα αρχεία μητρώου
Μία από τις πιο μοναδικές σχεδιαστικές επιλογές του MRISC32 ISA είναι ότι όλοι οι καταχωρητές μπορούν να χωρέσουν οποιονδήποτε τύπο δεδομένων, συμπεριλαμβανομένων των ακέραιων τιμών και των τιμών κινητής υποδιαστολής. Αυτό φέρνει μερικά πλεονεκτήματα:
- Ανεξάρτητα εάν μια CPU MRISC32 έχει υποστήριξη για κινητή υποδιαστολή σε υλικό ή όχι, η ABI παραμένει το ίδιο. Αυτό απλοποιεί πολλά πράγματα, συμπεριλαμβανομένου του σχεδιασμού μεταγλωττιστή και λειτουργικού συστήματος.
- Πολλές ακέραιες εντολές μπορούν να επαναχρησιμοποιηθούν για σκοπούς κινητής υποδιαστολής (για παράδειγμα, φόρτωση άμεσων τιμών, όπως συζητήθηκε προηγουμένως), γεγονός που μειώνει τον αριθμό των απαραίτητων εντολών στο ISA.
- Δεν υπάρχει ποινή για τη μεταφορά δεδομένων μεταξύ καταχωρητών κινητής υποδιαστολής και ακεραίων, κάτι που μπορεί να είναι επωφελές σε κώδικα που συνδυάζει τη χρήση ακεραίων και κινητής υποδιαστολής (αλγόριθμοι παρεμβολής, κωδικοποιητές βίντεο/ήχου και αποδόσεις γραφικών 3D είναι μερικά παραδείγματα).
Ένα ακόμη πράγμα που ανακάλυψα είναι ότι το GCC θα δημιουργήσει αυτόματα αρκετά αξιοπρεπή κώδικα για ορισμένες λειτουργίες κινητής υποδιαστολής, όπως π.χ. αντιγραφικόδεδομένου ότι είναι δωρεάν η χρήση ακέραιων πράξεων σε τιμές κινητής υποδιαστολής:

Σημειώστε ότι η παραλλαγή MRISC32 μπορεί να βελτιστοποιηθεί περαιτέρω χρησιμοποιώντας α bic οδηγία αντί για και για την εκκαθάριση του bit πρόσημου, το οποίο μειώνει το μέγεθος του κώδικα κατά μία εντολή (η ldhio η οδηγία καθίσταται περιττή), αλλά και πάλι η προεπιλεγμένη υλοποίηση του GCC είναι αρκετά εντάξει.
Η λύση RISC-V τα πάει ιδιαίτερα καλά εδώ, αφού προφανώς έχει μια ειδική οδηγία για την ένεση σημάτων. Ενδιαφέρων. x86 από την άλλη…
Το πλεονέκτημα RISC
Το MRISC32 ISA είναι α RISC αρχιτεκτονική φόρτωσης/καταστήματοςτο οποίο φέρνει αρκετά κοινά πλεονεκτήματα σε άλλες παρόμοιες αρχιτεκτονικές (π Εξουσία, MIPS, Αλφα και RISC-V). Μερικές από αυτές περιγράφονται παρακάτω.
Πολλά μητρώα
Επειδή υπάρχουν πολλά μητρώα προς χρήση (βλ Εγγραφές MRISC32), πολλές συναρτήσεις φύλλων δεν χρειάζεται να χρησιμοποιούν καθόλου τη στοίβα. Αυτό συμβαίνει επειδή μπορούν να περάσουν έως και 8 ορίσματα συνάρτησης σε καταχωρητές (έτσι τα ορίσματα συνάρτησης είναι πιθανό να υπάρχουν ήδη σε καταχωρητές όταν καλείται μια συνάρτηση) και έως και 26 βαθμωτοί καταχωρητές μπορούν να χρησιμοποιηθούν ελεύθερα εντός ενός εύρους συνάρτησης, γεγονός που μειώνει την πιθανότητα της διαρροής καταχωρητών στη στοίβα (βλ καταγράψτε την πίεση).
Για ένα παράδειγμα συνάρτησης που δεν χρησιμοποιεί τη στοίβα, βλ memcpy.s. Αυτό μπορεί να συγκριθεί με μια υλοποίηση ARM που χρειάζεται να χρησιμοποιήσει τη στοίβα (καθώς το ARM δεν έχει τόσους πολλούς καταχωρητές): memcpy.S.
Μη καταστροφικές οδηγίες
Δεδομένου ότι όλες οι εντολές MRISC32 δεν είναι καταστροφικές, δηλαδή οι τελεστές πηγής δεν τροποποιούνται, απαιτούνται λιγότερες οδηγίες σε σύγκριση με παλαιότερα CISC ISA: όπως x86 και 68k κ.λπ. τελεστές στους σωστούς καταχωρητές).
Συνεπής κωδικοποίηση εντολών
Όλες οι οδηγίες MRISC32 έχουν πλάτος 32 bit. Αυτό απλοποιεί σημαντικά την ανάκτηση και την αποκωδικοποίηση εντολών (τα πρώτα στάδια της διοχέτευσης της CPU) και βοηθά επίσης τους εντοπιστές σφαλμάτων και τους αποσυναρμολογητές, καθώς δεν χρειάζεται να μαντέψουν από πού ξεκινούν οι οδηγίες (π.χ. η αποσυναρμολόγηση του κώδικα ARM μπορεί να είναι δύσκολη αν δεν γνωρίζετε εάν ένα συγκεκριμένο τμήμα Ο κώδικας εκτελείται σε λειτουργία Thumb ή όχι, και ο καθορισμός του σημείου έναρξης και λήξης των οδηγιών x86 δεν είναι τετριμμένο).
Ένα άλλο σημαντικό πλεονέκτημα είναι ότι όλες οι εντολές είναι εγγυημένα ευθυγραμμισμένες με το μέγεθος της γραμμής της κρυφής μνήμης εντολών. Με άλλα λόγια, η CPU θα ποτέ καταλήγετε να διαβάζετε μόνο μέρος μιας εντολής από την κρυφή μνήμη (ούτε από οποιοδήποτε επίπεδο στην ιεραρχία της μνήμης, για αυτό το θέμα), επομένως δεν χρειάζεστε επιπλέον λογική για να συνδυάσετε οδηγίες από διαφορετικές γραμμές κρυφής μνήμης, για παράδειγμα.
Ευχαριστώ για την ανάγνωση! Ελπίζω να προσθέσω περισσότερη προοπτική για τα διανυσματικά μέρη του MRISC32 ISA σε μελλοντική ανάρτηση.