ΠΛΕ- 027 Μικροεπεξεργαστές 3ο μάθημα: γλώσσα μηχανής MIPS, προγραμματισμός assembly Αρης Ευθυμίου Πηγές διαφανειών: συνοδευτικές διαφάνειες αγγλικης εκδοσης του βιβλιου
Stored Program Computers Αναπαράσταση εντολών ως δυαδικοί αριθμοί, όπως τα δεδομένα Εντολές και δεδομένα αποθηκεύονται στη μνήμη Τα προγράμματα αλλάζουν εύκολα όπως και τα δεδομένα Προγράμματα μπορούν να επεξεργάζονται προγράμματα e.g., compilers, linkers, ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 45
Aναπαράσταση εντολών Εντολές MIPS Κωδικοποιούνται ως 32- bit λέξεις Μικρός αριθμός από μορφότυπα κωδικοποιούν αριθμό πράξης (operajon code - opcode), αριθμούς καταχωρητών, Αριθμοί καταχωρητών $t0 $t7 κωδικοποιούνται 8 15 $t8 $t9 κωδικοποιούνται 24 25 $s0 $s7 κωδικοποιούνται 16 23... ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 46
Μορφότυπο εντολών R Πεδία: op rs rt rd shamt funct 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits op: κωδικός πράξης (opcode) rs: πρώτος καταχωρητής πηγής (source) rt: δεύτερος καταχωρητής πηγής rd: καταχωρητής προορισμού/αποτελέσματος (desjnajon) shamt: ποσότητα ολίσθησης - για εντολές shiv μόνο, αλλιώς 00000 2 funct:επέκταση κωδικού πράξης (funcjon code) Οι εντολές ολίσθησης δεν χρησιμοποιούν το πεδίο rs ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 47
R- format, παράδειγμα op rs rt rd shamt funct 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits add $t0, $s1, $s2 special $s1 $s2 $t0 0 add 0 17 18 8 0 32 000000 10001 10010 01000 00000 100000 00000010001100100100000000100000 2 = 02324020 16 ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 48
Μορφότυπο εντολών I op rs rt constant or address 6 bits 5 bits 5 bits 16 bits Αριθμητικές πράξεις με σταθερά, εντολές load/store, διακλαδώσεις (υπό συνθήκη) rs: πρώτος καταχωρητής πηγής rt: καταχωρητής προορισμού για load, αριθμητικές πράξεις, 2 ος καταχωρητής πηγής για store Σταθερά με πρόσημο: 2 15 to +2 15 1 Διεύθυνση: offset (απόκλιση;) προστίθεται στην διευθυνση βάσης που περιέχει ο καταχωρητής rs ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 49
Διευθυνσιοδότηση διακλαδώσεων Οι διακλαδώσεις χρησιμοποιούν το μορφότυπο Ι Opcode, δύο καταχωρητές, δεύθυνση στόχου διακλάδωσης Οι περισσότεροι στόχοι δεν απέχουν πολύ Μπροστά ή πίσω (επαναλλήψεις) Η διεύθυνση δίνεται (υπολογίζεται) σχετικά με το PC PC- relajve addressing Target address = PC + 4 + offset offset = address * 4, αφού όλες οι εντολές είναι ευθυγραμισμένες Το PC έχει ήδη αυξηθεί κατά 4 λεπτομέρεια υλοποίησης που περνάει στην αρχιτεκτονική Πλεονέκτημα; ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 50
Μορφότυπο J op address 6 bits 26 bits Διευθυνσιοδότηση αλμάτων Οι στόχοι αλμάτων (j and jal) μπορούν να είναι οπουδήποτε στο τμήμα κώδικα (text segment) Η πλήρης διεύθυνση κωδικοποιείται στην εντολή Ψεύτο- απευθείας διεύθυνση ((Pseudo)Direct addressing) Στόχος = PC 31 28 : (address 4) ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 51
Παράδειγμα στόχων διακλαδώσεων Βρόγχος από προηγούμενο παράδειγμα: while (save[i] == k) i += 1; Εστω ότι ξεκινάει από τη θέση 80000 Loop: sll $t1, $s3, 2 80000 0 0 19 9 2 0 add $t1, $t1, $s6 80004 0 9 22 9 0 32 lw $t0, 0($t1) 80008 35 9 8 0 bne $t0, $s5, Exit 80012 5 8 21 2 addi $s3, $s3, 1 80016 8 19 19 1 j Loop 80020 2 20000 Exit: 80024 ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 52
Μακρινή διακλάδωση Αν ο στόχος διακλάδωσης είναι πολύ μακριά για να κωδικοποιηθεί σε 16- bit απόκλιση, ο assembler μετατρέπει τον κώδικα Παράδειγμα beq $s0,$s1, L1 bne $s0,$s1, L2 j L1 L2: ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 53
Περίληψη τρόπων διευθυνσιοδότησης addressing mode ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 54
Αντιγραφή string στη C Copy x[] to y[] char *p, *q; p = &x[0]; /* p = x */ /* set p to address of 1 st char of x */ q = &y[0]; /* q = y also OK */ /* set q to address of 1 st char of y */ while((*q++ = *p++)!= \0 ) ; Σωστό ή λάθος; ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 55
Αντιγραφή String σε MIPS Διευθύνσεις των x και y στη μνήμη p, q στους καταχωρητές: $s1, $s2 lw $t1, Base Address (e.g., BA) lw $s1,0($t1) # $s1 = p lw $s2,4($t1) # $s2 = q Loop: lb $t2,0($s1) # $t2 = *p sb $t2,0($s2) # *q = $t2 addi $s1,$s1,1 # p = p + 1 addi $s2,$s2,1 # q = q + 1 beq $t2,$zero,exit # if *p == 0, Exit j Loop # go to Loop Exit: # N characters => N*6 + 3 instrucjons ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 56
Πίνακες και δείκτες Χρήση πίνακα απαιτεί Υπολογισμός απόστασης (offset): πολλαπλασιασμός με το μέγεθος στοιχείου Πρόσθεση στη διεύθυνση βάσης του πίνακα Δείκτες αντιστοιχούν κατευθείαν στις διευθύνσεις μνήμης Μπορούν να αποφύγουν τους περισσότερους υπολογισμούς
Παράδειγμα: μηδενισμός πίνακα clear1(int array[], int size) { int i; for (i = 0; i < size; i += 1) array[i] = 0; } move $t0,$zero # i = 0 loop1: sll $t1,$t0,2 # $t1 = i * 4 add $t2,$a0,$t1 # $t2 = # &array[i] sw $zero, 0($t2) # array[i] = 0 addi $t0,$t0,1 # i = i + 1 slt $t3,$t0,$a1 # $t3 = # (i < size) bne $t3,$zero,loop1 # if ( ) # goto loop1 clear2(int *array, int size) { int *p; for (p = &array[0]; p < &array[size]; p = p + 1) *p = 0; } move $t0,$a0 # p = & array[0] sll $t1,$a1,2 # $t1 = size * 4 add $t2,$a0,$t1 # $t2 = # &array[size] loop2: sw $zero,0($t0) # Memory[p] = 0 addi $t0,$t0,4 # p = p + 4 slt $t3,$t0,$t2 # $t3 = #(p<&array[size]) bne $t3,$zero,loop2 # if ( ) # goto loop2
Σύγκριση πίνακα - δείκτη Ο πολλαπλασιασμός μετατρέπεται ( strength reduced ) σε ολίσθηση Ο κώδικας με πίνακα χρειάζεται την ολίσθηση μέσα στο βρόγχο αντίθετα, ο δείκτης απλά αυξάνεται Ο μεταφραστής μπορεί να κάνει την μετατροπή Καλύτερα το πρόγραμμα να είναι εύκολο στην κατανόηση και ασφαλές
Υπορουτίνες Οι υπορουτίνες είναι η βάση της εφαρμογής της ιδέας της αφαιρετικότητας στο λογισμικό Οι επεξεργαστές (και οι μεταφραστές,...) συνεργάζονται για να γίνονται οι κλήσεις και οι επιστροφές πιο γρήγορα και το πέρασμα παραμέτρων και επιστροφή τιμών ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 60
Κλήση υπορουτινας: 6 βήματα 1. Τοποθέτηση παραμέτρων σε θέση προσπελάσιμη από την υπορουτίνα 2. Μεταφορά ελέγχου εκτέλεσης στην υπορουτίνα 3. Απόκτηση αποθηκευτικού χώρου για ότι χρειάζεται η υπορουτίνα 4. Εκτέλεση εργασίας της υπορουτίνας 5. Τοποθέτηση αποτελέσματος σε θέση προσπελάσιμη από το πρόγραμμα που κάλεσε την υπορουτίνα και αποκατάσταση καταχωρητών που χρησιμοποιήθηκαν 6. Επιστροφή στη κατάλληλη εντολή (μια υπορουτίνα μπορεί να κληθεί από πολλά σημεία του προγράμματος) ΠΛΕ 027 Μικροεπεξεργαστές 2011-2012 61
Συμβάσεις κλήσης υπορουτίνας MIPS Χρησιμοποίησε καταχωρητές όσο γίνεται περισσότερο είναι πολύ ταχύτερο από προσπέλαση μνήμης Σύμβαση (convenjon) χρήσης καταχωρητών: $a0 $a3: 4 καταχωρητές για μεταφορά παραμέτρων (argument) $v0 $v1: 2 καταχωρητές για επιστροφή τιμών (value) $ra: καταχωρητής διεύθυνσης επιστροφής (return address) jal sub κλήση υπορουτίνας jr $ra επιστροφή
Αποκατάσταση τιμών καταχωρητών Χρειάζεται μια κατάληλη δομή μνήμης για να κρατάει προηγούμενες τιμές καταχωρητών ώστε να μπορούμε να τις επαναφέρουμε στην επιστροφή και να διαγράφουμε τις τιμές (τοπικές μεταβλητές automajc) Χρησιμοποιούμε τη δομή στοίβας (stack: last- in- first- out queue) Push: βάλε τα δεδομένα στη στοίβα Pop: αφαίρεσε τα δεδομένα από τη στοίβα Η στοίβα είναι στη μνήμη. Γιατί; Χρειάζεται ένας δείκτης: $sp (stack pointer) Η σύμβαση στο MIPS είναι ότι η στοίβα μεγαλώνει από μεγάλες προς μικρές διευθύνσεις Push μειώνει $sp, Pop αυξάνει $sp
Παράδειγμα τελικής υπορουτίνας Τελικές υπορουτίνες (leaf φύλλα) δεν καλούν άλλες υπορουτίνες αυτό ισχύει και για υπορουτίνες βιβλιοθηκών (π.χ. prinš) ούτε είναι αναδρομικές C κώδικας: int leaf_example (int g, h, i, j) { int f; f = (g + h) - (i + j); return f; } παράμετροι g,, j στους καταχωρητές $a0,, $a3 f στον $s0 (πρέπει να σώσουμε το $s0 στη στοίβα) Αποτέλεσμα στο $v0
Παράδειγμα σε MIPS κώδικας MIPS: leaf_example: addi $sp, $sp, -4 sw $s0, 0($sp) add $t0, $a0, $a1 add $t1, $a2, $a3 sub $s0, $t0, $t1 add $v0, $s0, $zero lw $s0, 0($sp) addi $sp, $sp, 4 jr $ra Save $s0 on stack Procedure body Result Restore $s0 Return
Μη τελικές υπορουτίνες Οι περισσότερες υπορουτίνες καλούν άλλες Αν ακολουθούσαμε τα παραπάνω η διεύθυνση επιστροφής και οι παράμετροι της καλούσας (caller) υπορουτίνας θα χανόταν Πρέπει λοιπόν να σώσουμε στη στοίβα: Τη διεύθυνση επιστροφής ($ra) Ο,τι παραμέτρους ή προσωρινούς καταχωρητές ($t) θα χρειαστούν αργότερα και να τις επαναφέρουμε μετά την επιστροφή Γιατί χαλάσαμε την ομοιομορφία; είναι λογισμικό, δεν έχει τόση σημασία η ομοιομορφία! είναι πιο γρήγορες οι κλήσεις/επιστροφές
Παράδειγμα μή- τελικής ρουτίνας Κώδικας C (παραγοντικό factorial): int fact (int n) { if (n < 1) return f; else return n * fact(n - 1); } Παράμετρος n στο καταχωρητή $a0 Αποτέλεσμα στο $v0
Παράδειμα, ΜIPS MIPS code: fact: addi $sp, $sp, -8 # adjust stack for 2 items sw $ra, 4($sp) # save return address sw $a0, 0($sp) # save argument slti $t0, $a0, 1 # test for n < 1 beq $t0, $zero, L1 addi $v0, $zero, 1 # if so, result is 1 addi $sp, $sp, 8 # pop 2 items from stack jr $ra # and return L1: addi $a0, $a0, -1 # else decrement n jal fact # recursive call lw $a0, 0($sp) # restore original n lw $ra, 4($sp) # and return address addi $sp, $sp, 8 # pop 2 items from stack mul $v0, $a0, $v0 # multiply to get result jr $ra # and return
Τοπικά δεδομένα στη στοίβα Κατανομή χώρου για τοπικά δεδομένα γίνεται από την καλούμενη υποροτίνα (callee) π.χ., «αυτόματες» (τοπικές εκτός από stajc) μεταβλητές C Πλάισιο υπορουτίνας (frame / acjvajon record) Χρησιμοποιείται από μερικούς μεταφραστές
Σύμβαση χρήσης καταχωρητών $a0 $a3: παράμετροι (reg s 4 7) $v0, $v1: επιστροφή τιμών (reg s 2 and 3) $t0 $t9: προσωρινές μεταβλητές Δεν διατηρούνται από την καλούμενη υπορουτίνα $s0 $s7: διατηρούμενες μεταβλητές (saved) Πρέπει να αποθηκεύονται/επαναφέρονται από την καλούμενη υπορουτίνα $gp: δείκτης καθολικών δεοδομένων (reg 28) $sp: stack pointer (reg 29) $fp: frame pointer (reg 30) $ra: return address (reg 31)
Διάταξη μνήμης Text: κώδικας Stajc data: καθολικές μεταβλητές συμπ. stajc στη C Ο $gp αρχικοποιείται στη μέση του χώρου και με ±offsets γίνεται προσπέλαση Dynamic data: σωρός malloc in C, new in Java Stack: automajc storage
Παράδειγμα: ταξινόμηση σε C Ταξινόμηση bubble sort σε C Swap procedure (leaf) void swap(int v[], int k) { int temp; temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; } v in $a0, k in $a1, temp in $t0
The Procedure Swap swap: sll $t1, $a1, 2 # $t1 = k * 4 add $t1, $a0, $t1 # $t1 = v+(k*4) # (address of v[k]) lw $t0, 0($t1) # $t0 (temp) = v[k] lw $t2, 4($t1) # $t2 = v[k+1] sw $t2, 0($t1) # v[k] = $t2 (v[k+1]) sw $t0, 4($t1) # v[k+1] = $t0 (temp) jr $ra # return to calling routine
The Sort Procedure in C Μή- τελική (καλεί τη swap) void sort (int v[], int n) { int i, j; for (i = 0; i < n; i += 1) { for (j = i 1; j >= 0 && v[j] > v[j + 1]; j -= 1) { swap(v,j); } } } v: $a0, k: $a1, i: $s0, j: $s1
Κορμός (body) της υπορουτίνας move $s2, $a0 # save $a0 into $s2 move $s3, $a1 # save $a1 into $s3 move $s0, $zero # i = 0 for1tst: slt $t0, $s0, $s3 # $t0 = 0 if $s0 $s3 (i n) beq $t0, $zero, exit1 # go to exit1 if $s0 $s3 (i n) addi $s1, $s0, 1 # j = i 1 for2tst: slti $t0, $s1, 0 # $t0 = 1 if $s1 < 0 (j < 0) bne $t0, $zero, exit2 # go to exit2 if $s1 < 0 (j < 0) sll $t1, $s1, 2 # $t1 = j * 4 add $t2, $s2, $t1 # $t2 = v + (j * 4) lw $t3, 0($t2) # $t3 = v[j] lw $t4, 4($t2) # $t4 = v[j + 1] slt $t0, $t4, $t3 # $t0 = 0 if $t4 $t3 beq $t0, $zero, exit2 # go to exit2 if $t4 $t3 move $a0, $s2 # 1st param of swap is v (old $a0) move $a1, $s1 # 2nd param of swap is j jal swap # call swap procedure addi $s1, $s1, 1 # j = 1 j for2tst # jump to test of inner loop exit2: addi $s0, $s0, 1 # i += 1 j for1tst # jump to test of outer loop Move params Outer loop Inner loop Pass params & call Inner loop Outer loop
Πλήρης ρουτίνα sort: addi $sp,$sp, 20 # make room on stack for 5 registers sw $ra, 16($sp) # save $ra on stack sw $s3,12($sp) # save $s3 on stack sw $s2, 8($sp) # save $s2 on stack sw $s1, 4($sp) # save $s1 on stack sw $s0, 0($sp) # save $s0 on stack # procedure body exit1: lw $s0, 0($sp) # restore $s0 from stack lw $s1, 4($sp) # restore $s1 from stack lw $s2, 8($sp) # restore $s2 from stack lw $s3,12($sp) # restore $s3 from stack lw $ra,16($sp) # restore $ra from stack addi $sp,$sp, 20 # restore stack pointer jr $ra # return to calling routine
Συνχρονισμός Δύο πυρήνες μοιράζονται μέρος της μνήμης P1 γράφει, μετά ο P2 διαβάζει Πρόβλημα αν δεν συνχρονιστούν Το αποτέλεσμα εξαρτάται από τη σειρά εκτέλεσης Χρειάζεται βοήθεια από το υλικό αδιαίρετη (atomic) read/write στη μνήμη κανείς άλλος δεν πρέπει να αλλάξει την τιμή πριν ολοκληρωθεί η αδιαίρετη προσπέλαση Συνήθως είναι μία εντολή Π.χ., αδιαίρετη ανταλλαγή (atomic swap) μεταξύ καταχωρητή και θέσης μνήμης
Συνχρονισμός στο MIPS Load linked: ll rt, offset(rs) Store condijonal: sc rt, offset(rs) Πετυχαίνει αν η θέση δεν έχει αλλαχθεί από τότε που εκτελέστηκε η ll Επιστρέφει 1 στον rt Αποτυγχάνει αν η θέση έχει αλλάξει Returns 0 in rt
Παράδειγμα Υλοποίηση αδιαίρετης ανταλλαγής try: add $t0,$zero,$s4 #copy exchange value ll $t1,0($s1) #load linked sc $t0,0($s1) #store conditional beq $t0,$zero,try #branch store fails add $s4,$zero,$t1 #put load value in $s4 Στο τέλος η $s4 και η τιμή στη θέση μνήμης που δείχνει η $S1 έχουν ανταλαχθεί αδιαίρετα
Πλάνες Ισχυρές εντολές μεγαλύτερη απόδοση πράγματι θέλουμε εκτέλεση λιγότερων εντολών αλλά πολύπλοκες εντολές υλοποιούνται δύσκολα Μπορεί να αυξήσουν τον χρόνο εκτέλεσης όλων των εντολών Οι μεταφραστές μπορούν να παράγουν καλό κώδικα από απλές εντολές Χρησιμοποίηση assembly για μεγαλύτερη απόδοση οι σύγχρονοι μεταφραστές είναι καλοί! περισσοτερος κώδικας περισσότερα λάθη και χαμηλή παραγωγικότητα
Πλάνες Συμβατότητα προς τα πίσω το σέτ εντολών δεν αλλάζει αλλά νέες εντολές προστίθενται x86 instrucjon set
Παγίδες Διαδοχικές λέξεις δεν αντιστοιχούν σε διαδοχικές διευθύνσεις Η μνήμη θέλει διευθύνσεις σε byte Χρήση δείκτη σε αυτόματη μεταβλητή (στοίβας) μετά από την επιστροφή από την υπορουτίνα π.χ. περνώντας δείκτη πίσω σε μια παράμετρο
Περίληψη Βασικές εντολές MIPS Προγραμματισμός σε assembly μετάφραση από C κλήση υπορουτινών Κωδικοποίηση εντολών Επόμενο μάθημα: γλώσσα περιγραφής υλικού Verilog