Presentation is loading. Please wait.

Presentation is loading. Please wait.

יצירת קוד ASU פרק 9. 2 יצירת קוד syntax analysis semantic analysis code generation syntax tree intermediate code דרישות יצירת קוד בעל איכות טובה (ניצול.

Similar presentations


Presentation on theme: "יצירת קוד ASU פרק 9. 2 יצירת קוד syntax analysis semantic analysis code generation syntax tree intermediate code דרישות יצירת קוד בעל איכות טובה (ניצול."— Presentation transcript:

1 יצירת קוד ASU פרק 9

2 2 יצירת קוד syntax analysis semantic analysis code generation syntax tree intermediate code דרישות יצירת קוד בעל איכות טובה (ניצול יעיל של משאבי המכונה) האלגוריתמים ליצירת קוד צריכים להיות יעילים במידה סבירה (מכיוון שחלק מהבעיות הן NP-complete, עושים שימוש באלגוריתמים היוריסטיים)

3 3 שיקולים עיקריים בבניית code generator תכנון נאות של שפת הביניים, כולל העברה של אינפורמציה מה-front end (הנחה – אין שגיאות בקוד הביניים) שפת המטרה absolute machine code  תוכניות com ב-DOS  לא ממש קיים בארכיטקטורות מודרניות relocatable code  קבצי obj (קבצי tpu, וכו') assembly language

4 4 שיקולים עיקריים בבניית code generator ניהול זיכרון היכן נמצא משתנה במהלך הביצוע? מיפוי התוכנית לזיכרון; חישוב תוויות בחירת פקודות מכונה הקצאת רגיסטרים (pairing, assignment, allocation) בחירת סדר החישוב תוך התחשבות ב- pipelining וב- caching

5 5 מבנה מכונת המטרה הנחות לצורך הדיון byte addressability 4-byte words n רגיסטרים – R 0,... R n-1 מבנה הפקודות OP source, destination צורת האופרנדים added costaddressformmode 1MMabsolute 0RRregister 1c + content ( R )c ( R )indexed 0content ( R )* Rindirect register 1content ( c + content ( R ) )* c ( R )indirect indexed 1c (constant – not an address) # cliteral

6 6 דוגמאות MOV R 0,123 העתק את הערך של רגיסטר 0 לכתובת 123 בזכרון. עלות כוללת = 2. ADD 4(R 0 ),123 הוסף את הערך שבכתובת הזכרון {4 ועוד ערך רגיסטר 0}, לכתובת 123. עלות כוללת = 3. SUB *4(R 0 ),R 1 החסר את הערך שבתא הזכרון, שכתובתו נמצאת בכתובת R 0 +4, מ-R 1. (R 1 := R 1 - *(4+R 0 )) עלות כוללת = 2. MOV #1,R 0 כתוב את הערך "1" ל-R 0. עלות כוללת = 2.

7 7 run time storage management נתרכז בפקודות call, return, halt הקצאה סטטית (לא ע"ג מחסנית) three address code /* code for c */ action 1 call p action 2 halt /* code for p */ action 3 return activation record for p (88 bytes) activation record for c (64 bytes) return address0:return address0: buf4:arr8: i56: j60: n84: goto *callee.static_area move #here + 20, callee.static_area goto callee.code_area

8 8 run time storage management – דוגמא /* code for c */ ACTION 1 100: /* save return address 140 */MOV # 140, 364120: /* call P */GOTO 200132: ACTION 2 140: HALT160:... /* code for p */ ACTION 3 200: /* return to address saved in location 364 */GOTO *364220:... /* 300-363 hold activation record for c */ /* return address */300: /* local data for c */304:... /* 364-451 hold activation record for p */ /* return address */364: /* local data for p */368:

9 9 run time storage management – ניהול מחסנית main MOV #stackstart, SP … code for the first procedure … HALT calling a procedure ADD #caller.recordsize, SP MOV #here + 16, *SP GOTO callee.code_area SUB #caller.recordsize, SP return from a procedure GOTO *0 ( SP ) הנחה סמויה – caller.recordzise הוא ערך סטטי רגיסטר מיוחד, מצביע לראש המחסנית. יצביע לראש רשומת ההפעלה הנוכחית.

10 10 ניהול מחסנית – דוגמא three address code /* code for s */ action 1 call q action 2 halt /* code for p */ action 3 return /* code for q */ action 4 call p action 5 call q action 6 call q return

11 11 code for qcode for s conditional jumpACTION 4 300:initialize the stackMOVE #600,SP100: ADD #qsize,SP320:ACTION 1 108: push return addressMOV #344,*SP328:call sequence beginsADD #ssize,SP128: call pGOTO 200336:push return addressMOV #152,*SP136: SUB #qsize,SP344:call qGOTO 300144: ACTION 5 352:restore SPSUB #ssize,SP152: ADD #qsize,SP372:ACTION 2 160: push return addressMOV #396,*SP380:HALT180: call qGOTO 300388:... SUB #qsize,SP396:code for p ACTION 6 404:ACTION 3 200: ADD #qsize,SP424:returnGOTO *0(SP)220: push return addressMOV #448,*SP432:... call qGOTO 300440: SUB #qsize,SP448: returnGOTO *0(SP)456:... stack starts here600: ניהול מחסנית – דוגמא

12 12 דוגמא – נניח ש- x := 0 היא שורה בתוכנית המקור הקצאה סטטית נניח ש- x אמור להמצא בהיסט ( (offset12 static [12] := 0 – קוד ביניים (רביעיה) אם האזור הסטטי מתחיל בכתובת 100, נקבל: MOV #0, 112 – קוד מכונה הקצאה מקומית במחסנית נניח ש- x משתנה מקומי ה'חי' ברשומה המוצבעת ע"י SP t 1 := 12 + SP * t 1 := 0 בפקודות מכונה – MOV #0, 12 ( SP ) קוד ביניים (רביעות) MOV #0, 112 – קוד מכונה חישוב כתובות עבור משתנים

13 13 basic blocks and flow graphs בלוק בסיסי – סדרת פקודות עם כניסה יחידה (לפקודה הראשונה) ויציאה יחידה (מהפקודה האחרונה). היציאה היא אולי הפניה או הפניה מותנה של הבקרה משפט מהסוג x := y + z מגדיר את x ומשתמש ב- y ו- z משתנה הוא חי בנקודה נתונה אם נעשה שימוש בערך שלו לאחר הנקודה האמורה. := a * at1t1 := a * bt2t2 := 2 * t 2 t3t3 := t 1 + t 3 t4t4 := b * bt5t5 := t 4 + t 5 t6t6

14 14 פירוק תוכנית לבלוקים בסיסיים Input: A sequence of three-address statements Output: A list of basic blocks with each three-address statement in exactly one block Method We first determine the set of leaders, the first statements of basic blocks. The rules we use are the following:  The first statement is a leader  Any statement that is the target of a conditional of unconditional goto is a leader  Any statement that immediately follows a goto or conditional goto statement is a leader For each leader, its basic block consists of a leader and all statements up to but not including the next leader or the end of the program

15 15 פירוק תוכנית לבלוקים בסיסיים– דוגמא פשוטה 'מהחיים' int fib(int n) { if (n<=1) return 1; return fib(n-1) + fib(n-2); } “gcc –S fib.c” creates fib.s:.fib: mflr 0; r0 := lr stw 29,-12(1); *r1-12 := r29 stw 31,-4(1); *r1-4 := r31 stw 0,8(1); *r1+8 := r0 (lr) stwu 1,-72(1); r1 := r1-72, *r1=r1 mr 31,1; r31 := r1 stw 3,96(31); *r31+96 := r3 (‘n’) lwz 0,96(31); r0 := *r31+96 (‘n’) cmpwi 0,0,1; cr0 := r0 ~ 1 bgt 0,L..3; if cr0.gt goto L..3 li 3,1; r3 := 1 b L..2; goto L..2 (return 1) L..3: lwz 9,96(31); r9 := *r31+96 (‘n’) addi 0,9,-1; r0 := r9 - 1 mr 3,0; r3 := r0 bl.fib; call fib(n-1) mr 29,3; r29 := r3 lwz 9,96(31); r9 := *r31+96 (‘n’) addi 0,9,-2 ; r0 := r9 - 2 mr 3,0; r3 := r0 bl.fib; call fib(n-2) mr 9,3; r9 := r3 add 0,29,9; r0 := r29 + r9 mr 3,0; r3 := r0 b L..2; goto L..2 L..2: lwz 1,0(1); r1 := *r1 lwz 0,8(1); r0 := *r1+8 mtlr 0; lr := r0 lwz 29,-12(1); r29 := *r1-12 lwz 31,-4(1); r31 := *r1-4 blr; goto lr (return)

16 16 טרנספורמציות בתוך בלוקים בסיסיים ביטויים משותפים ביטול "קוד מת" השמה למשתנה שלא חי לאחר מכן החלפת שם של משתנה זמני אפשר תמיד להשתמש בשמות חדשים ולקבל בלוק בצורה נורמלית צריך לשמור על הטיפוס החלפת סדר בין פקודות פישוטים אלגבריים := b + ca := a – db := b + cc := a – dd := y ^ 2x := x * 1x := x + 0x d := b x := y * y

17 17 (control) flow graph צמתי הגרף – הבלוקים הבסיסיים קשתות הגרף – קשת מקשרת שני בלוקים אם קיימת הפנית בקרה מהראשון לשני (או פשוט המשך ישיר של זרימת הבקרה) לולאה – קבוצה קשירה היטב בעלת כניסה יחידה לולאה פנימית – טבעת ללא תת-טבעות הערה – עושים שימוש בשמות הבלוקים הבסיסיים ולא במספרים סידוריים של הרביעיות המתאימות (על מנת לאפשר הזזה של הבלוקים בסיסיים) B1B1 prod := 0 i := 1 B2B2 t 1 := 4 * i t 2 := a [ t 1 ] t 3 := 4 * i t 4 := b [ t 3 ] t 5 := t 2 * t 4 t 6 := prod + t 5 prod := t 6 t 7 := i + 1 i := t 7 if i <= 20 goto B 2

18 18 next-use information עבור כל שורה בבלוק נתון, נרצה לשמור (עבור כל משתנה אליו מתייחסת שורה זו): האם המשתנה חי מתי השימוש הבא במשתנה. אם בשורה i מבצעים השמה ל- x ואם משתמשים ב- x בשורה j, ואם יש אפשרות לזרימת בקרה מ- i ל- j ללא השמה נוספת ל- x, אזי ב- j ישנו שימוש ב- x שחושב ב- i החישוב נעשה ברמת הבלוק הבודד (זהירות: קריאות לפרוצדורות...) החישוב נעשה על-ידי סריקה של הבלוק מסופו לעבר תחילתו הנחות שמרניות: כל המשתנים שאינם מקומיים בבלוק, חיים בסוף הבלוק (כלומר, נעשה בהם שימוש בהמשך). סריקות מתקדמות יותר מאפשרות דיוק רב יותר.

19 19 next use information חישוב ה- next-use – ברמת הבלוק הבודד – מהסוף להתחלה. נעזר ב- symbol table. נניח שבטיפול מהסוף להתחלה הגענו למשפט i: x := y op z 1. נצמיד ל- i את האינפורמציה שנאספה ב- symbol table על y, x ו- z 2. נקבע ב- symbol table x – "לא חי"; "אין שימוש בהמשך" 3. נקבע ב- symbol table y, z – "חיים"; "השימוש הבא – ב- i" הערה – הסדר בין 2 ו- 3 – חשוב למשל, במקרה בו x  y

20 20 תהליך פשוט ליצירת קוד הגישה – ניצור קוד כך שעבור כל פקודה נזכור היכן נמצאים האופרנדים הנחות התאמה טובה בין שפת הרביעיות לשפת המכונה נשמור תוצאות ברגיסטרים אלא אם  יש צורך ברגיסטר למטרה אחרת  צריך לסגור את הבלוק דוגמאותa := b + c איך יודעים אם "אפשר לוותר"? לפי מידע ה-next-use... אם b ו- c ברגיסטרים ואפשר לוותר על b ולהשאיר במקומו את aADD R j, R i אם R i מכיל את b ואפשר לוותר עליו, אבל c בזכרוןADD C, R i אם נצטרך את c שובMOV C, R j ADD R j, R i

21 21 מעקב אחרי משתנים ורגיסטרים עבור רגיסטרים – רושמים מה יש בכל רגיסטר (descriptors) בתחילת כל בלוק כל הרגיסטרים ריקים במהלך ייצור הקוד, כל רגיסטר יכיל משתנה אחד או יותר מתאר הכתובות עוקב היכן נמצא כל משתנה (רגיסטר, מחסנית, מקום בזיכרון...). כשיש פקודת load או store, משתנה ימצא במספר מקומות

22 22 code generation algorithm Input: A sequence of three-address statements constituting a basic block. For each three-address statement x := y op z we perform the following: 1. Invoke the function getreg to determine the location L where the result of the computation y op z should be stored. L will usually be a register, but it could also be a memory location. 2. Consult the address descriptor for y to determine y', (one of) the current location(s) of y. Prefer the register for y' if the value of y is currently both in memory and a register. If the value of y is not already in L, generate the instruction MOV y', L to place a copy of y in L. 3. Generate the instruction OP z', L, where z' is the current location of z. Again, prefer a register to a memory location if z is in both. Update the address descriptor of x to indicate that x is in location L. If L is a register, update its descriptor to indicate that it contains the value of x. 4. If the current values of y and/or z have no next uses, are not live on exit from the block, and are in registers, alter the register descriptor to indicate that, after execution of x:= y op z, those registers no longer will contain y and/or z, respectively.

23 23 יצירת קוד – הערות הטיפול בפעולות אונריות – דומה x := y מצריך עדכון המתארים עם סיום העבודה ברמת הבלוק הבסיסי שומרים את ערכי המשתנים (אלא אם יש לנו אינפורמציה של live-dead)

24 24 הקצאת רגיסטר (הפונקציה getreg) נתון – x := y op z המטרה – הקצאת רגיסטר ל- x 1.אם y ברגיסטר L שאינו משמש לאכסון משתנים נוספים, ואם ל- y אין שימוש בהמשך, החזר את L, עדכן את מתארי הכתובת והרגיסטר.  זכרו כי הוראות מסוג x := y גורמות לרגיסטר להחזיק את הערך של כמה משתנים בו-זמנית. 2.אם 1 נכשל, מצא רגיסטר פנוי L והחזר אותו. 3.אם 2 נכשל, ואם ל- x יש שימוש בהמשך, מצא רגיסטר תפוס L. שמור את הערך של L בזיכרון (אם אינו נמצא שם), עדכן את מתארי הכתובת והרגיסטר והחזר את L.  המשתנה (או משתנים) שהיה לפני-כן ב-L שמור עכשיו בזכרון בלבד.  איך בוחרים איזה רגיסטר לפנות לטובת x? 4.אם לא נעשה שימוש ב- x בהמשך הבלוק, או אם אין בנמצא רגיסטר מתאים, החזר את כתובתו בזיכרון של x.

25 25 הקצאת רגיסטרים address descriptorregister descriptorcode generatedstatements registers empty t in R0R0 contains tMOV a, R0 SUB b, R0 t := a – b t in R0 u in R1 R0 contains t R1 contains u MOV a, R1 SUB c, R1 u := a – c u in R1 v in R0 R0 contains v R1 contains u ADD R1, R0v := t + u d in R0 d in R0 and memory R0 contains dADD R1, R0 MOV R0, d d := v + u d := (a – b) + (a – c) + (a – c)

26 26 יצירת קוד לטיפול במערכים i in stacki in memory Mii in register Ri statement costcodecostcodecostcode 4 MOV Si(A), R MOV b(R), R 4 MOV Mi, R MOV b(R), R 2MOV b(Ri), Ra:= b [ i ] 5 MOV Si(A), R MOV b, a(R) 5 MOV Mi, R MOV b, a(R) 3MOV b, a(Ri)a [ i ] := b offset יחסית לתחילת ה- activation record תחילת ה- activation record מניחים ש- a הוא משתנה סטטי

27 27 טיפול במצביעים p in stackp in memory Mpp in register Rp statement costcodecostcodecostcode 4 MOV Sp(A), R MOV *R, R 3 MOV Mp, R MOV *R, R 2MOV *Rp, aa:= * p 5 MOV a, R MOV R, *Sp(A) 4 MOV Mp, R MOV a, *R 2MOV a, *Rp* p := a

28 28 register allocation and assignment allocation – איזו אינפורמציה תאוכסן ברגיסטר? assignment – למה בדיוק יוקצה כל רגיסטר? חשיבות הנושא הקטנת הקוד ייעול התוכנית גישות עיקריות הקצאה קבועה של רגיסטרים לצרכים מיוחדים  כתובת חזרה  ערך מוחזר  אכסון מצביע לראש המחסנית הקצאה גלובלית

29 29 global register allocation המטרה – הימנעות משמירת ערכים המצויים ברגיסטרים בסוף כל בלוק על ידי מעקב גלובלי "גלובלי" עשוי עדיין להיות מקומי – בתוך אותה פרוצדורה, למשל. דגש מיוחד על הלולאות הפנימיות המלצות המתכנת (המלה השמורה register בשפת C) גישה כמותית – usage count מקצים מקום ברגיסטר למשתנה בו נעשה שימוש נרחב חשיבות מיוחדת לאינדקס הרץ של לולאות מטפלים בלולאות הפנימיות תחילה לאחר מכן, הקצאת רגיסטרים ללולאות החיצוניות  השיטה דומה. אם אין מספיק רגיסטרים הן ללולאה חיצונית והן לפנימית, מבצעים spill לפני הכניסה ללולאה הפנימית

30 30 הקצאת רגיסטרים באמצעות צביעה של גרפים 1. נניח שמספר הרגיסטרים לא חסום ונייצר קוד (מכונה אבסטרקטית) 2. נקצה רגיסטרים כך שעלות ה- spill תהיה קטנה בניה וניתוח של ה- register interference graph 1. הצמתים – הרגיסטרים הסימבוליים 2. קשת בגרף מקשרת שני צמתים אם אחד מהם חי במקום בו השני מוגדר 3. נצבע את הגרף במספר קטן של צבעים – כמספר הרגיסטרים הפיזיים הבעיה היא NP Complete משתמשים באלגוריתמים היוריסטיים הערה – צריך להשתדל להימנע מלהכניס spill code לעניבות הפנימיות

31 31 ייצוג החישוב של בלוק בסיסי בעזרת DAG עלים – משתנים (שונים) או קבועים (שונים) צמתים פנימיים מסומנים על ידי האופרטורים המתאימים לצמתים נצמיד שמות משתנים על פי התקדמות החישוב t 1 := 4 * i t 2 := a [ t 1 ] t 3 := 4 * i t 4 := b [ t 3 ] t 5 := t 2 * t 4 t 6 := prod + t 5 prod := t 6 t 7 := i + 1 i := t 7 if i <= 20 goto (1) (1) 1i0i0 4ba 20t 7, i+t1, t3t1, t3 * (1)<=t4t4 [ ]t2t2 t5t5 *prod 0 t 6, prod+

32 32 בנית ה- DAG מטפלים בכל משפט בבלוק x := y + z מוצאים איפה נמצא הערך הנוכחי של y ו- z בונים צומת חדש המסומן  +ומחברים אותו לשני הבנים (אם לא קיים כזה); מסמנים אותו ב- x אם x (לאx 0 ) כבר מסומן, יש לבטל את הסימון הקודם אין יוצרים צומת חדש עבור משפטי השמה פשוטים x := y

33 33 ייצוג בלוקים בסיסיים כ- DAGs עלים – משתנים (שונים) או קבועים (שונים) צמתים פנימיים מסומנים על ידי האופרטורים המתאימים לצמתים נצמיד שמות משתנים על פי התקדמות החישוב t 1 := 4 * i t 2 := a [ t 1 ] t 3 := 4 * i t 4 := b [ t 3 ] t 5 := t 2 * t 4 t 6 := prod + t 5 prod := t 6 t 7 := i + 1 i := t 7 if i <= 20 goto (1) (1) 1i0i0 4ba 20t 7, i+t1, t3t1, t3 * (1)<=t4t4 [ ]t2t2 t5t5 *prod 0 t 6, prod+ t1t1 prod := prod + t 5 i := i + 1

34 34 שימושים של DAGs כייצוג של בלוקים בסיסיים זיהוי אוטומטי של ביטויים משותפים זיהוי המשתנים בהם נעשה שימוש בבלוק זיהוי הפקודות בהן חושבו ערכים בעלי שימוש בסוף הבלוק ייצור קוד יעיל (בפרט – אין צורך בהשמות) זיהוי המשתנים החיים סדר חישוב קביל – באמצעות מיון טופולוגי

35 35 side effects הקושי – aliasing הפתרון – השמה לאיבר של מערך מבטלת את האפשרות להשתמש בצמתים המכילים התייחסות אליו הערה – בעיה דומה נגרמת על ידי מצביעים הערה – בעיה דומה נגרמת על ידי פרוצדורות נושא ה- aliasing הוא מכשול מרכזי לאופטימיזציה של תוכניות x := a [ i ] a [ j ] := y z := x  x := a [ i ] a [ j ] := y z := a [ i ]


Download ppt "יצירת קוד ASU פרק 9. 2 יצירת קוד syntax analysis semantic analysis code generation syntax tree intermediate code דרישות יצירת קוד בעל איכות טובה (ניצול."

Similar presentations


Ads by Google