290 likes | 452 Views
שפת מכונה – מבוא. נושאים קוד מותנה Setting Testing Control Flow If-then-else Varieties of Loops Switch Statements. מבוסס על פרק 3 של Computer Systems – a programmers perspective / Bryant & O’hallrron. קוד מותנה. רגיסטרים ייעודיים בני סיבית אחת: CF Carry Flag SF Sign Flag
E N D
שפת מכונה – מבוא • נושאים • קוד מותנה • Setting • Testing • Control Flow • If-then-else • Varieties of Loops • Switch Statements מבוסס על פרק 3 של Computer Systems – a programmers perspective / Bryant & O’hallrron
קוד מותנה • רגיסטרים ייעודיים בני סיבית אחת: CF Carry Flag SF Sign Flag ZF Zero Flag OF Overflow Flag • מופעלים בעת חישוב פעולות אריתמטיות. למשל: add Src,Dest C analog: t = a + b • CF = 1 אם יש גלישה בחישוב Unsigned • ZF = 1 אם t == 0 • SF = 1 אם t < 0 • OF = 1 אם גלישה בחישוב signed • למשל, ראינו שבחיבור יש גלישה אם: (a>0 && b>0 && t<0) || (a<0 && b<0 && t>=0)
קוד מותנה (השמה מפורשת) • ניתן להשים ערך לסיביות אלה גם באופן מפורש, על ידי פקודת compare. compare Src2,Src1 • compare b,a – כמו חישובa-bבלי יעד. test Src2,Src1 • test b,a - כמו חישובa&bבלי יעד • ZF כאשר a&b == 0 • SF כאשר a&b < 0
קפיצות (jumping) • jX Instructions • קפיצה מותנית
דוגמה:Conditional Branch max: move 8(R1),R3 move 12(R1),R4 compare R4,R3 jle L9 move R3,R4 L9: x int max(int x, int y) { if (x > y) return x; else return y; } Body x-y signed המקרה של return y תוצאת הפונקציה נשמרת ב R4.
המשך דוגמה • C מאפשר פקודת goto - לא מומלץ! • נראה שִכְתוב של התוכנית הקודמת בדומה יותר לקוד היעד. int goto_max(int x, int y) { int tmp = x; int return_val = y; int ok = (x <= y); if (ok) goto done; return_val = x; done: return return_val; } move 8(R1),R3 # R3 = x move 12(R1),R4 # R4 = y compare R4,R3 # x : y jle L9 # if <= goto L9 move R3,R4 # R4 = x L9: # Done:
דוגמה עם לולאה C Code Goto Version int fact_do (int x) { int result = 1; do { result *= x; x = x-1; } while (x > 1); return result; } int fact_goto(int x) { int result = 1; loop: result *= x; x = x-1; if (x > 1) goto loop; return result; }
אחרי הידור... • Registers R3 x R4 result Goto Version int fact_goto (int x) { int result = 1; loop: result *= x; x = x-1; if (x > 1) goto loop; return result; } fact_goto: move $1,R4 # R4 = 1 move 8(R1),R3 # R3 = x L11: multiply R3,R4 # result *= x decrement R3 # x-- compare $1,R3 # Compare x : 1 jg L11 # if > goto loop
התבנית הכללית לתרגוםDo-While Goto Version C Code • הגוף יכול להיות כל statementשל C • בדר"כ פעולה מורכבת (compound): • ה test הוא ביטוי המחזיר מספר שלם = 0 false 0 true do Body while (Test); loop: Body if (Test) goto loop { Statement1; Statement2; … Statementn; }
דוגמה ללולאת while First Goto Version C Code • האם שקול לתוכנית הקודמת שהייתה מבוססת על do-while ? • לא! בגלל האיטרציה הראשונה. int fact_while(int x) { int result = 1; while (x > 1) { result *= x; x = x-1; }; return result; } int fact_while_goto(int x) { int result = 1; loop: if (!(x > 1)) goto done; result *= x; x = x-1; goto loop; done: return result; }
התרגום האמיתי של while Second Goto Version C Code • אותה לולאה פנימית • בדיקה נוספת בכניסה ללולאה. • כלומר, כמו do-while מלבד הבדיקה הראשונה. int fact_while(int x) { int result = 1; while (x > 1) { result *= x; x = x-1; }; return result; } int fact_while_goto2(int x) { int result = 1; if (!(x > 1)) goto done; loop: result *= x; x = x-1; if (x > 1) goto loop; done: return result; }
התבנית הכללית לתרגום while C Code while (Test) Body Goto Version Do-While Version if (!Test) goto done; loop: Body if (Test) goto loop; done: if (!Test) goto done; do Body while(Test); done:
לולאות for /* Compute x raised to nonnegative power p */ int pwr(int x, unsigned p) { int result; for (result = 1; p != 0; p = p>>1) { if (p & 0x1) result *= x; x = x*x; } return result; } • אלגוריתם • סיבוכיות: O(log p) Example 310 = 32 * 38 = 32 * ((32) 2) 2
דוגמה לחישוב של pwr /* Compute x raised to nonnegative power p */ int pwr(int x, unsigned p) { int result; for (result = 1; p != 0; p = p>>1) { if (p & 0x1) result *= x; x = x*x; } return result; }
דוגמה ללולאת For צורה כללית int result; for (result = 1; p != 0; p = p>>1) { if (p & 0x1) result *= x; x = x*x; } for (Init; Test; Update ) Body Init Test Update result = 1 p != 0 p = p >> 1 { if (p & 0x1) result *= x; x = x*x; } Body
“For” “While” For Version While Version for (Init; Test; Update ) Body Init; while (Test ) { Body Update ; } Do-While Version Goto Version Init; if (!Test) goto done; do { Body Update ; } while (Test) done: Init; if (!Test) goto done; loop: Body Update ; if (Test) goto loop; done:
Init Test result = 1 p != 0 Update p = p >> 1 הידור של לולאת“for” Goto Version result = 1; if (p == 0) goto done; loop: if (p & 0x1) result *= x; x = x*x; p = p >> 1; if (p != 0) goto loop; done: Init; if (!Test) goto done; loop: Body Update ; if (Test) goto loop; done: Body { if (p & 0x1) result *= x; x = x*x; }
פקודות Switch typedef enum {ADD, MULT, MINUS, DIV, MOD, BAD} op_type; char unparse_symbol(op_type op) { switch (op) { case ADD : return '+'; case MULT: return '*'; case MINUS: return '-'; case DIV: return '/'; case MOD: return '%'; case BAD: return '?'; } } • באג בדוגמה: אין ברירתמחדל.
Targ0: Code Block 0 jtab: Targ0 Targ1 Targ2 Targ1: Code Block 1 • • • Targ2: Code Block 2 Targn-1 • • • Targn-1: Code Block n–1 המבנה של הJump Table Jump Targets Switch Form Jump Table switch(op) { case val_0: Block 0 case val_1: Block 1 • • • case val_n-1: Blockn–1 } Approx. Translation target = JTab[op]; goto *target;
תזכורת: • compare מבצע R4 – 5 • cf = 1 אם יש גלישה על פי unsigned . • התנאי של ja הוא ~cf & ~zf דוגמה: Switch • Enumerated Values • ADD 0 • MULT 1 • MINUS 2 • DIV 3 • MOD 4 • BAD 5 • Branching Possibilities typedef enum {ADD, MULT, MINUS, DIV, MOD, BAD} op_type; char unparse_symbol(op_type op) { switch (op) { • • • // returns ASCII of op } } unparse_symbol: move 8(R1),R4 # R4 = op compare $5,R4 # Compare op : 5 ja .L49 # If > goto done jmp *.L57(,R4,4) # goto Table[op]
הסבר • Labels • תוויות מהצורה .Lxx מתורגם לכתובת על ידי ה assembler • מבנה הטבלה • כל יעד דורש 4 בתים. • הבסיס (Base address) ב L57 • קפיצות: jmp .L49 • היעד מסומן על ידי תוית .L49 jmp *.L57(,R4,4) • כתובת תחילת הטבלה .L57 • R4 מחזיק את op. משם - קפיצות של 4 • היעד נמצא ב .L57 + op*4 • כלומר: בפעולה אחת נמצא את הכתובת של הקוד לביצוע !
Jump Table Targets & Completion Table Contents .L51: move $43,R4 # ’+’ jmp .L49 .L52: move $42,R4 # ’*’ jmp .L49 .L53: move $45,R4 # ’-’ jmp .L49 .L54: move $47,R4 # ’/’ jmp .L49 .L55: move $37,R4 # ’%’ jmp .L49 .L56: move $63,R4 # ’?’ # Fall Through to .L49 .section .rodata .align 4 .L57: .long .L51 #Op = 0 .long .L52 #Op = 1 .long .L53 #Op = 2 .long .L54 #Op = 3 .long .L55 #Op = 4 .long .L56 #Op = 5 43 is the ASCII value of ‘+’ הפרוצדורה תחזיר את הערך ב R4.
חידה • חידה • אם op לא נמצא בטבלה ואין ברירת מחדל – איזה ערך מוחזר ? • תשובה • הערך שלop עצמו. • למה ? כי בהתחלת הפרוצדורה ביצענו השמה של op ל R4.
switch לעומת if • היתרון של Jump Table • יכול לבצע k-way branch ב - O(1). • החיסרון של Jump Table • גודל הטבלה כגודל הטווח בין הערך המינימאלי למקסימאלי. • כלומר, לא בהכרח פרופורציוני למספר הכניסות ב switch. • switch • - טוב כשהמקרים הם קבועים יחסית קטנים • - נמנע מ conditionals • שורה של תנאי if • - טוב אם יש מספר קטן • - איטי מדי אם יש מספר גדול של תנאים. • אופטימיזציה שלGCC : בוחר לבד לפי המבנה
דוגמה לSwitch 'דליל' /* Return x/111 if x is multiple && <= 999. -1 otherwise */ int div111(int x) { switch(x) { case 0: return 0; case 111: return 1; case 222: return 2; case 333: return 3; case 444: return 4; case 555: return 5; case 666: return 6; case 777: return 7; case 888: return 8; case 999: return 9; default: return -1; } } • לא מעשי ליישם כ jump table • ידרוש טבלה של 1000 כניסות. • התרגום ל if-then-else ידרוש 9 בדיקות.
קוד של Switch 'דליל' • המהדר הפך לרשימת if • משווה לערכים שונים של xוקופץ למקומות שונים בהתאם לתוצאה. ללא אופטימיזציות:jump table קוד if בעקבות אופטימיזציה של המהדר . . . L5: move $1,R4 jmp L19 L6: move $2,R4 jmp L19 L7: move $3,R4 jmp L19 L8: move $4,R4 jmp L19 . . . move 8(R1),R4 # get x compare $444,R4 # x:444 je L8 jg L16 compare $111,R4 # x:111 je L5 jg L17 testl R4,R4 # x:0 je L4 jmp L14 . . . מדוע דווקא הסדר הזה ?
444 111 777 0 222 555 888 -1 -1 -1 -1 333 666 999 מבנה של קוד Switch דליל • מארגן את המקרים לפי עץ בינארי. • זמן חיפוש לוגריתמי. • כיצד נוצר החיסכון ? המיון נעשה בשלב ההידור במקום בזמן ריצה. < > = 4 < > < > = = 1 7 < > = = = = 2 5 8 0 = = = 3 9 6
C Control if-then-else do-while while switch סיכום 1 • Assembler Control • jump • Conditional jump • המהדר חייב לשלב פקודות assembly כדי לממש פקודות מורכבות יותר.
סיכום 2 • טכניקות סטנדרטיות: • כל הלולאות מתורגמות ל do-while • פעולות switch מתורגמות ל jump tables • תנאים ב CISC • בדר"כ מחשבי CISC מכילים רגיסטרים מיוחדים לתנאים • תנאים ב RISC • משתמשים ברגיסטרים כלליים • פקודות מיוחדות להשוואה • למשל, על מחשבי Alpha: comparee $16,1,$1 • Sets register $1 to 1 when Register $16 <= 1