webcourse.cs.technion.ac.il

Download Report

Transcript webcourse.cs.technion.ac.il

236360 ‫תורת הקומפילציה‬
6 ‫הרצאה‬
‫שפות ביניים‬
Intermediate Languages/Representations
Aho, Sethi and Ullman – Chapter 6
Cooper and Torczon – Chapter 5
1
‫יצירת קוד ביניים‬
syntax analysis
syntax tree
semantic analysis
decorated syntax tree
intermediate code generator
intermediate code
machine independent optimizations
intermediate code
code generator
2
‫חשיבות קוד הביניים‬
‫שימוש בשיטות אופטימיזציה שאינן תלויות במכונה מסויימת‬
‫אפשרות לייצר קוד עבור מכונות שונות באמצעות אותו ‪front end‬‬
‫שימוש באותו ‪ back end‬עבור שפות שונות – מספר ‪front ends‬‬
‫אם כתבנו ‪ front-ends m‬ו‪ ,back ends n-‬אז ניתן לשלב אותם ולקבל‬
‫‪ n*m‬קומפיילרים‪.‬‬
‫‪C#‬‬
‫‪Pascal‬‬
‫‪Java‬‬
‫‪C‬‬
‫‪Intermediate Language‬‬
‫‪Intel‬‬
‫‪Cray‬‬
‫‪PowerPC‬‬
‫‪3‬‬
‫ייצוג ביניים – ‪intermediate representation‬‬
‫ייצוגים אפשריים‬
‫‪syntax tree ‬‬
‫‪postfix notation ‬‬
‫‪ – three address code ‬זו הצורה שאנו נבחר לעבוד איתה‪.‬‬
‫‪ ‬שני אופרנדים ותוצאה אחת‪.‬‬
‫בד"כ עובדים בשני שלבים‪:‬‬
‫‪ ‬תרגום מונחה דקדוק יוצר עץ ניתוח תחבירי ‪ +‬סימונים בצמתים‬
‫‪ ‬את העץ מתרגמים ל‪three address code -‬‬
‫נדבר ראשית על ייצוג העץ‪ ,‬לפעמים נשתמש ב‪DAG -‬‬
‫(‪ )Directed Acyclic Graph‬במקום בעץ‪.‬‬
‫‪4‬‬
:‫שלושה ייצוגים אפשריים‬
postfix-‫ ו‬,DAGs ,decorated syntax trees
a := b * –c + b * –c
‫ייצוג כעץ‬
DAG -‫ייצוג כ‬
assign
assign
+
a
+
a
*
b
:‫נתון‬
*
uminus
c
b
*
uminus
b
c
uminus
c
postfix -‫ייצוג ב‬
a b c uminus * b c uminus * + assign
5
‫ תרגום מונחה דקדוק ליצירת עץ מעוטר‬:‫דוגמה‬
‫פונקציות עזר‬
‫ – יצירת עלה‬mkleaf 
‫ – יצירת צומת חדש עבור אופרטור אונרי‬mkunode 
‫ – יצירת צומת חדש עבור אופרטור בינארי‬mknode 
‫ – מצביע לטבלת הסמלים‬id.place 
production
S → id := E
E → E1 + E 2
E → E1 * E2
E → – E1
E → ( E1 )
E → id
semantic rule
S.nptr := mknode ( ' assign ' , mkleaf ( id , id.place ), E.nptr )
E.nptr := mknode ( ' + ' , E1.nptr , E2.nptr )
E.nptr := mknode ( ' * ' , E1.nptr , E2.nptr )
E.nptr := mkunode ( ' uminus ' , E1.nptr )
E.nptr := E1.nptr
E.nptr := mkleaf ( id , id.place )
‫ בפונקציות‬mknode -‫ ו‬,mkunode ,mkleaf ‫הערה – אפשר להחליף את‬
DAG ‫המחזירות מצביע לצמתים קיימים על מנת ליצור‬
6
‫ייצוג בזיכרון של עץ מעוטר‬
a := b * –c + b * –c
assign
*
7

id
b
uminus

id
c
id
a
+



*

id
b
uminus

id
c

0
id
b
1
id
c
2
uminus
1
3
*
0
4
id
b
5
id
c
6
uminus
5
7
*
4
6
8
+
3
7
9
id
a
10
assign
9
11
···
2
8
‫‪three address code‬‬
‫אחרי שבונים את העץ‪ ,‬צריך לתרגם לשפת הביניים שבחרנו‪.‬‬
‫אנו נעבוד עם ‪.three-address-code‬‬
‫הצורה הכללית של פקודה‪:‬‬
‫אופרטור‬
‫‪x := y op z‬‬
‫↑‬
‫↑‬
‫↑‬
‫‪ 3‬הכתובות‬
‫‪ ,y ,x‬ו‪ z-‬הם ‪ 3‬שמות‪ ,‬קבועים‪ ,‬או משתנים זמניים שנוצרו ע"י הקומפיילר‪.‬‬
‫‪ op‬הוא אופרטור כלשהו‪.‬‬
‫האופרטורים שנשתמש בהם יהיו פשוטים‪ ,‬כך שיהיה קל לעבור מהם לשפת‬
‫מכונה‪.‬‬
‫‪8‬‬
three address code
assign
assign
+
a
*
b
*
unimus
c
t1
t2
t3
t4
t5
a
9
:=
:=
:=
:=
:=
:=
+
a
–c
b * t1
–c
b * t3
t2 + t4
t5
b
*
unimus
b
unimus
c
c
t1
t2
t3
a
:=
:=
:=
:=
–c
b * t1
t2 + t2
t3
‫קוד ביניים – סוגי המשפטים‬
‫‪.1‬‬
‫‪.2‬‬
‫‪.3‬‬
‫‪.4‬‬
‫‪.5‬‬
‫‪.6‬‬
‫משפטי השמה עם פעולה בינארית‬
‫משפטי השמה עם פעולה אונרית‬
‫משפטי העתקה‬
‫קפיצה בלתי מותנה‬
‫קפיצה מותנה‬
‫פרמטרים וקריאה לפרוצדורות‬
‫‪indexed assignments .7‬‬
‫‪ .8‬השמה של כתובות ומצביעים‬
‫‪x := y op z‬‬
‫‪x := op y‬‬
‫‪x := y‬‬
‫‪goto L‬‬
‫‪if x relop y goto L‬‬
‫‪param x‬‬
‫‪call p, n‬‬
‫‪return y‬‬
‫] ‪x := y [ i‬‬
‫‪x [ i ] := y‬‬
‫‪x := addr y‬‬
‫‪x := * y‬‬
‫‪* x := y‬‬
‫= ‪relop‬‬
‫‪relational op‬‬
‫)‪(==, >=, etc.‬‬
‫‪n = actual‬‬
‫‪number of‬‬
‫‪parameters‬‬
‫קריאה‬
‫לפרוצדורה‪:‬‬
‫‪param x1‬‬
‫…‬
‫‪param xn‬‬
‫‪call p,n‬‬
‫‪10‬‬
‫איך בוחרים אופרטורים?‬
‫הבחירה של אוסף פקודות מתאים היא חשובה‪.‬‬
‫אוסף מצומצם‪:‬‬
‫‪ ‬קל ליצור קוד מכונה‪,‬‬
‫‪ ‬הקוד יהיה פחות יעיל‪ ,‬ומעמסה גדולה יותר תיפול על ה‪optimizer -‬‬
‫‪ ‬הקוד יהיה ארוך והטיפול בו יהיה מסורבל‬
‫‪ ‬אי ניצול יכולות של מכונות חכמות‬
‫אופרטורים רבים‪:‬‬
‫‪ ‬קוד יותר יעיל אך קשה יותר לייצרו וממנו קשה לייצר קוד עבור מכונות‬
‫פשוטות‪.‬‬
‫‪11‬‬
‫יצירת קוד ביניים בעל ‪ 3‬כתובות על ידי תרגום‬
‫מונחה דקדוק‬
‫ככלל‪ ,‬נניח ‪ bottom-up parsing‬כך שדברים מחושבים לפני שמשתמשים‬
‫בתוצאת החישוב‪.‬‬
‫השיטה – שימוש במשתנים זמניים‬
‫‪( S.code‬או ‪ – )E.code‬תכונה המכילה את הקוד הנוצר עבור ‪( S‬או ‪.)E‬‬
‫‪ – E.var‬שם של משתנה שעתיד להכיל את הערך של ‪E‬‬
‫‪ – newtemp‬פונקציה המחזירה שם של משתנה חדש‬
‫‪12‬‬
‫ כתובות על ידי תרגום‬3 ‫יצירת קוד ביניים בעל‬
‫מונחה דקדוק‬
production
semantic rule
S → id := E
S.code := E.code || gen ( id.var ' := ' E.var )
E → E1 + E 2
E.var := newtemp;
E.code := E1.code || E2.code || gen ( E.var ' := ' E1.var ' + ' E2.var )
E → E1 * E2
E.var := newtemp;
E.code := E1.code || E2.code || gen ( E.var ' := ' E1.var ' * ' E2.var )
E → – E1
E.var := newtemp;
E.code := E1.code || gen ( E.var ' := ' ' uminus ' E1.var )
E → ( E1 )
E.var := E1.var
E.code := ‘(’ || E1.code || ‘)’
E → id
E.var := id.var ;
E.code := ' '
13
)labels( ‫ דוגמא לשימוש בתוויות‬:while ‫פסוק‬
S → while E do S1
S.begin:
E.code
if E.var = 0 goto S.after
S1.code
goto S.begin
S.after:
.‫ ותוויות‬,‫נוסיף תכונות למשתנים‬
‫ – פונקציה היוצרת תווית חדשה‬newlabel
‫ – תווית המסמנת את תחילת‬S.begin
‫הקוד‬
‫ – תווית המסמנת את סוף הקוד‬S.after
false ‫ – מייצג את‬0
···
production
semantic rule
S → while E do S1
S.begin := newlabel ;
S.after := newlabel ;
S.code := gen ( S.begin ' : ' ) || E.code ||
gen ( ' if ' E.var ' = ' ' 0 ' ' goto ' S.after ) ||
S1.code || gen ( ' goto ' S.begin ) || gen (S.after ' : ' )
14
‫מבנה נתונים לייצוג של ‪:3-address code‬‬
‫ייצוג סטנדרטי הוא ע"י רביעיות כך שכל שורה נכתבת לתוך משתנה זמני‪.‬‬
‫‪op, arg1, arg2, result‬‬
‫↑‬
‫↑‬
‫↑‬
‫מצביעים לטבלת הסמלים‬
‫‪arg 1‬‬
‫‪op‬‬
‫‪result‬‬
‫‪arg 2‬‬
‫‪c‬‬
‫‪uminus‬‬
‫)‪(0‬‬
‫‪b‬‬
‫*‬
‫)‪(1‬‬
‫‪c‬‬
‫‪uminus‬‬
‫)‪(2‬‬
‫‪t4‬‬
‫‪t3‬‬
‫‪b‬‬
‫*‬
‫)‪(3‬‬
‫‪t5‬‬
‫‪t4‬‬
‫‪t2‬‬
‫‪+‬‬
‫)‪(4‬‬
‫‪t5‬‬
‫=‪:‬‬
‫)‪(5‬‬
‫‪t1‬‬
‫‪t2‬‬
‫‪t1‬‬
‫‪t3‬‬
‫‪a‬‬
‫‪t1 = - c‬‬
‫‪t2 = b * t1‬‬
‫‪t3 = - c‬‬
‫‪t4 = b * t3‬‬
‫‪t5 = t2 * t4‬‬
‫‪a = t5‬‬
‫יתרון‪ :‬פשוט ‪ +‬אין בעיה להעתיק ולהזיז קטעי קוד (וזה חשוב לאופטימיזציות)‪.‬‬
‫עלות – מחייב לשמור את ה‪ temporaries -‬בטבלת הסמלים‬
‫‪15‬‬
‫ייצוג נוסף של ‪3-address code‬‬
‫‪( op, arg1, arg2‬התוצאה מובנת כמספר השורה)‬
‫שלשות ‪:‬‬
‫↑‬
‫↑‬
‫מצביעים לטבלת הסמלים או למספר הסידורי של השורה המחשבת את הערך‬
‫‪arg 2‬‬
‫)‪(0‬‬
‫)‪(2‬‬
‫)‪(3‬‬
‫)‪(4‬‬
‫‪arg 1‬‬
‫‪c‬‬
‫‪b‬‬
‫‪c‬‬
‫‪b‬‬
‫)‪(1‬‬
‫‪a‬‬
‫‪op‬‬
‫‪uminus‬‬
‫*‬
‫‪uminus‬‬
‫*‬
‫‪+‬‬
‫‪assign‬‬
‫)‪(0‬‬
‫)‪(1‬‬
‫)‪(2‬‬
‫)‪(3‬‬
‫)‪(4‬‬
‫)‪(5‬‬
‫אין צורך ב‪result -‬‬
‫אבל‪ :‬אי אפשר להזיז קוד ‪ +‬פעולה טרנרית כמו ‪ x [ i ] := y‬דורשת שתי שורות‬
‫‪arg 2‬‬
‫‪i‬‬
‫)‪(0‬‬
‫‪arg 1‬‬
‫‪y‬‬
‫‪x‬‬
‫‪op‬‬
‫][=‬
‫‪assign‬‬
‫] ‪x := y [ i‬‬
‫)‪(0‬‬
‫)‪(1‬‬
‫‪arg 2‬‬
‫‪i‬‬
‫‪y‬‬
‫‪arg 1‬‬
‫‪x‬‬
‫)‪(0‬‬
‫‪op‬‬
‫=][‬
‫‪assign‬‬
‫)‪(0‬‬
‫)‪(1‬‬
‫‪x [ i ] := y‬‬
‫‪16‬‬
‫ייצוג שלישי של ‪3-address code‬‬
‫‪ – indirect triples‬השלשות מופרדות מהסדר ביניהן‬
‫‪arg 1‬‬
‫‪op‬‬
‫‪arg 2‬‬
‫‪c‬‬
‫‪uminus‬‬
‫‪0‬‬
‫‪b‬‬
‫*‬
‫‪1‬‬
‫‪c‬‬
‫‪uminus‬‬
‫‪2‬‬
‫)‪(2‬‬
‫‪b‬‬
‫*‬
‫‪3‬‬
‫)‪(3‬‬
‫)‪(1‬‬
‫‪+‬‬
‫‪4‬‬
‫)‪(4‬‬
‫‪a‬‬
‫‪assign‬‬
‫‪5‬‬
‫)‪(0‬‬
‫רשימת פקודות‬
‫‪ Execution‬לפי סדר הביצוע‬
‫‪Order‬‬
‫הרצוי שלהן‬
‫‪10‬‬
‫‪11‬‬
‫‪0‬‬
‫‪1‬‬
‫‪15‬‬
‫‪12‬‬
‫עתה ניתן לשנות סדר ביצוע‪ ,‬להזיז קטעי קוד‪ ,‬ולחסוך במקום אם קיימות‬
‫שלשות זהות‬
‫(לא פותר את הפעולה הכפולה עבור פעולות טרנריות‪).‬‬
‫‪17‬‬
‫‪ Types‬והקצאות זיכרון למשתנים‬
‫ניתוח ה‪ types-‬חשוב מאד לבדיקת שגיאות‪,‬‬
‫אבל חשוב גם על‪-‬מנת לאפשר הקצאת מקום בגודל נכון למשתנים‬
‫במחסנית (או באובייקט) וחישוב ‪ offset‬לכל אחד מהם‪ ,‬ואף לחשב‬
‫כתובות בתוך מערכים‪.‬‬
‫‪...‬‬
‫משתנים קודמים‬
‫ל‪employee-‬‬
‫‪Offset for variable employee‬‬
‫מקום למשתנה ‪employee‬‬
‫‪employee‬‬
‫רשומת הפעלה‬
‫למתודה‬
‫‪...‬‬
‫‪18‬‬
‫הכרזות והקצאת זכרון‬
.offset -‫ תרגום מונחה דקדוק עם פעולות סמנטיות לחישוב ה‬:‫דוגמא‬
.‫ עם גודל השטח שהוקצה עד עתה‬offset ‫נשמור משתנה גלובלי‬
.offset ‫לכל משתנה בפרוצדורה – נכניס לטבלת הסמלים ונקבע לו‬
production
semantic rule
P→
PD
{ offset := 0 }
D→D D
D → T id ;
{ enter ( id.name, T.type, offset ); offset := offset + T.width }
T → integer
{ T.type := integer ; T.width := 4 }
T → real
{ T.type := real ; T.width := 8 }
T → T1 [ num ]
{ T.type := array ( num.val, T1.type ) ;
T.width := num.val  T1.width }
T → *T1
T.type := pointer (T1.type ) ; T.width := 4 }
19
enter(money, real, 4)
offset = offset + 4
enter(count, int, 0)
offset = offset + 4
‫הכרזות‬
P
D4
D1
D5
D2
T1
id
T2
int
count
real
id
T3
money
T4
T1.type = int
T1.width = 4
id.name = count
20
id
T2.type = real
T2.width = 4
id.name = money
int
[
num
98
]
balances
‫הכרזות והקצאת זיכרון‬
‫ שבו נפעיל את‬top-down ‫ בהתחלה עובד מצוין לניתוח‬offset ‫האיפוס של‬
?bottom-up ‫ אך מה עושים עם ניתוח‬.‫ בתור הכלל הראשון‬P → D
LR parsing-‫ גם ב‬,‫ וכלל שתמיד נראה ראשון‬marker ‫ נוסיף‬:‫טריק סטנדרטי‬
production
semantic rule
P→
PD
{ offset := 0 }
D→D D
D → T id ;
{ enter ( id.name, T.type, offset ); offset := offset + T.width }
T → integer
{ T.type := integer ; T.width := 4 }
T → real
{ T.type := real ; T.width := 8 }
T → T1 [ num ]
{ T.type := array ( num.val, T1.type ) ;
T.width := num.val  T1.width }
T → *T1
T.type := pointer (T1.type ) ; T.width := 4 }
21
‫הכרזות והקצאת זיכרון‬
‫ שבו נפעיל את‬top-down ‫ בהתחלה עובד מצוין לניתוח‬offset ‫האיפוס של‬
?bottom-up ‫ אך מה עושים עם ניתוח‬.‫ בתור הכלל הראשון‬P → D
LR parsing-‫ גם ב‬,‫ וכלל שתמיד נראה ראשון‬marker ‫ נוסיף‬:‫טריק סטנדרטי‬
production
P→
P D
semantic rule
P → MD
M→Є
{_____________
offset := 0 }
{ offset := 0 }
D→D D
D → T id ;
{ enter ( id.name, T.type, offset ); offset := offset + T.width }
T → integer
{ T.type := integer ; T.width := 4 }
T → real
{ T.type := real ; T.width := 8 }
T → [ num ] T1
{ T.type := array ( num.val, T1.type ) ;
T.width := num.val  T1.width }
T → *T1
P
M
T.type := pointer (T1.type ) ; T.width := 4 }
Є
22
D
‫לסיכום – ייצוג של קוד ביניים‬
‫קוד ביניים סטנדרטי הוא חשוב‪ ,‬ניתן להפריד בין ה‪ front-end-‬שתלוי בשפת‬
‫המקור‪ ,‬לבין ה‪ back-end-‬שתלוי במכונת היעד‪ ,‬ולשלב כל ‪ front-end‬עם‬
‫כל ‪.back-end‬‬
‫השלבים הקודמים בונים עץ מעוטר (עם ‪)attributes‬‬
‫נתרגם אותו אל ‪ three-address-code‬שהיא שפת ביניים סטנדרטית‪.‬‬
‫אפשר לעשות זאת ע"י פעולות סמנטיות בתרגום מונחה דקדוק‪.‬‬
‫‪ ‬אוספים את הקוד לתוויות של משתני הדקדוק‪.‬‬
‫‪ Three-address-code‬ניתן לייצוג ע"י רביעיות או שלשות (ישירות או‬
‫עקיפות)‪.‬‬
‫ניתוח ה‪ types-‬חיוני עבור קביעת מקום למשתנים בזיכרון‪ ,‬וגם את זה ניתן‬
‫לעשות באמצעות פעולות סמנטיות בתרגום מונחה דקדוק‪...‬‬
‫‪23‬‬
‫יצירת קוד ביניים‬
‫‪24‬‬
‫יצירת קוד‬
‫אפשרות א' – צבירת קוד ב‪ attributes-‬של משתנים בעץ הגזירה (למשל‪,‬‬
‫בתכונות מסוג ‪ .)code‬כך עשינו עד עתה‪.‬‬
‫אפשרות ב' – יצירת קובץ המכיל את הקוד תוך כדי תהליך הקומפילציה‬
‫‪ ‬אפשרות זו מעשית (לגזירת ‪ )bottom-up‬אם לכל חוק דקדוק תכונת ה‪-‬‬
‫‪ code‬של אגף שמאל של החוק מתקבלת משרשור תכונות ה‪ code -‬של‬
‫המשתנים באגף ימין של החוק על פי סדר ידוע (אולי בצירוף מחרוזות‬
‫קבועות נוספות)‬
‫‪ ‬חסרון‪ :‬מקשה על ביצוע מניפולציות על הקוד‪.‬‬
‫‪ ‬במספר שקפים הקרובים נדגים את אפשרות ב'‪ .‬כמובן שניתן בקלות‬
‫לחזור לצבירת קוד בתכונות של משתני הדקדוק שבגזירה‪.‬‬
‫‪25‬‬
‫ביטויים ומשפטי השמה‬
‫דקדוק המסגרת‪ :‬התוכנית מכילה הגדרות של משתנים (כמו קודם) ופרוצדורות‪.‬‬
‫} ‪D → D ; D | T id { N D1 ; S‬‬
‫‪N→‬‬
‫‪P→MD‬‬
‫‪M→‬‬
‫ביטויים ומשפטי השמה‪:‬‬
‫} ) ‪{ p := lookup ( id.name ) ; if p  nil then emit ( p ' := ' E.var ) else error‬‬
‫‪S → id := E‬‬
‫} ) ‪{ E.var := newtemp() ; emit ( E.var ' := ' E1.var ' + ' E2.var‬‬
‫‪E → E1 + E 2‬‬
‫‪ Lookup‬מחזיר את הכתובת של המשתנה בזיכרון‪.‬‬
‫‪ Emit‬פולט שורת קוד מתאימה בפורמט ‪ three-address-code‬לתוך הקובץ‪.‬‬
‫הטיפול כאן (ובד"כ בהמשך) הוא לפי ‪ bottom-up parsing‬ולכן מקצים‬
‫למשתנה מקום בפעם הראשונה שפוגשים אותו = כמשתנה השמאלי בכלל‬
‫הדקדוק‪.‬‬
‫‪26‬‬
‫ביטויים ומשפטי השמה‬
.‫ התוכנית מכילה הגדרות של משתנים (כמו קודם) ופרוצדורות‬:‫דקדוק המסגרת‬
P→MD
M→
D → D ; D | T id { N D1 ; S }
N→
:‫ביטויים ומשפטי השמה‬
S → id := E
{ p := lookup ( id.name ) ; if p  nil then emit ( p ' := ' E.var ) else error ) }
E → E1 + E 2
{ E.var := newtemp() ; emit ( E.var ' := ' E1.var ' + ' E2.var ) }
E → E1 * E2
{ E.var := newtemp() ; emit ( E.var ' := ' E1.var ' * ' E2.var ) }
E → – E1
{ E.var := newtemp() ; emit ( E.var ' := ' ' uminus ' E1.var ) }
E → ( E1 )
{ E.var := E1.var }
E → id
{ p := lookup ( id.name); if p  nil then E.var := p else error }
‫עץ אין יותר שימוש‬-‫ כשיוצאים מתת‬:‫ניתן לייעל במקום הדרוש למשתנים זמניים‬
.‫ ניתן לנהל את המשתנים במחסנית‬.‫במשתנים הפנימיים שלו‬
27
‫ביטויים בוליאניים‬
.1-‫ כ‬true ‫ ואת‬0-‫ כ‬false ‫נייצג את‬
E → E1 or E2
{ E.var := newtemp() ; emit ( E.var ' := ' E1.var ' or ' E2.var ) }
E → E1 and E2
{ E.var := newtemp() ; emit ( E.var ' := ' E1.var ' and ' E2.var ) }
E → not E1
{ E.var := newtemp() ; emit ( E.var ' := ' ' not ' E1.var ) }
E → ( E1 )
{ E.var := E1.var }
E → true
{ E.var := newtemp() ; emit ( E.var ' := ' ' 1 ' ) }
E → false
{ E.var := newtemp() ; emit ( E.var ' := ' ' 0 ' ) }
‫חשוב לשים לב – כתובת המטרה ניתנת לחישוב תוך כדי יצירת הקוד‬
28
‫חישוב ביטויים בוליאניים ע"י קפיצה‬
.1-‫ כ‬true ‫ ואת‬0-‫ כ‬false ‫נייצג את‬
E → id1 relop id2 { E.var := newtemp() ; (lookup ‫(נחפף את‬
emit ( ' if ' id1.var relop.op id2.var ' goto ' nextstat + 2 ) ;
emit ( E.var ' := ' ' 0 ' ) ;
emit ( ' goto ' nextstat + 1 ) ;
emit ( E.var ' := ' ' 1 ' ) }
.‫ אבל נראה קודם דוגמא‬...‫למה זה מועיל? לחישוב מקוצר‬
29
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
‫‪E‬‬
‫‪E‬‬
‫‪E‬‬
‫‪f‬‬
‫<‬
‫‪or‬‬
‫‪and‬‬
‫‪e‬‬
‫‪E‬‬
‫‪d‬‬
‫<‬
‫‪E‬‬
‫‪b‬‬
‫<‬
‫‪a‬‬
‫‪c‬‬
‫‪30‬‬
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
‫‪E‬‬
‫‪E‬‬
‫‪E‬‬
‫‪f‬‬
‫<‬
‫‪or‬‬
‫‪and‬‬
‫‪e‬‬
‫‪E‬‬
‫‪d‬‬
‫<‬
‫‪E‬‬
‫‪b‬‬
‫‪c‬‬
‫<‬
‫‪if a < b goto 103‬‬
‫‪T1 := 0‬‬
‫‪goto 104‬‬
‫‪T1 := 1‬‬
‫‪a‬‬
‫‪100:‬‬
‫‪101:‬‬
‫‪102:‬‬
‫‪103:‬‬
‫‪31‬‬
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
E
E
a
100:
101:
102:
103:
<
or
b
if a < b goto 103
T1 := 0
goto 104
T1 := 1
E
c
104:
105:
106:
107:
32
E
<
and
d
if c < d goto 107
T2 := 0
goto 108
T2 := 1
E
e
<
f
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
E
E
a
100:
101:
102:
103:
<
or
b
if a < b goto 103
T1 := 0
goto 104
T1 := 1
E
E
c
104:
105:
106:
107:
<
and
E
d
e
f
if c < d goto 107
T2 := 0
goto 108
T2 := 1
108:
109:
110:
111:
112:
33
<
if e < f goto 111
T3 := 0
goto 112
T3 := 1
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
E
E
a
100:
101:
102:
103:
<
or
b
if a < b goto 103
T1 := 0
goto 104
T1 := 1
E
E
c
104:
105:
106:
107:
<
and
E
d
e
f
if c < d goto 107
T2 := 0
goto 108
T2 := 1
108:
109:
110:
111:
112:
34
<
if e < f goto 111
T3 := 0
goto 112
T3 := 1
T4 := T2 and T3
‫ביטויים בוליאניים בייצוג מספרי – דוגמא‬
E
E
a
100:
101:
102:
103:
<
or
b
if a < b goto 103
T1 := 0
goto 104
T1 := 1
E
c
104:
105:
106:
107:
35
E
<
and
E
d
e
<
f
if c < d goto 107
T2 := 0
goto 108
T2 := 1
108:
109:
110:
111:
112:
113:
if e < f goto 111
T3 := 0
goto 112
T3 := 1
T4 := T2 and T3
T5 := T1 or T4
‫ביטויים בוליאניים – חישוב מקוצר‬
‫בניגוד לביטויים אריתמטיים‪ ,‬בביטויים בוליאניים ניתן לחסוך בחישוב כי‬
‫לעיתים ניתן לדעת מה התוצאה כבר באמצע החישוב‪.‬‬
‫למשל‪ ,‬בביטוי ‪ ,E1 or E2‬אם ‪ E1‬הוא ‪ true‬הרי שלא חשוב לנו מה ערכו של‬
‫‪.E2‬‬
‫חישוב כזה נקרא ‪ lazy evaluation‬או‬
‫‪.short circuit boolean evaluation‬‬
‫‪36‬‬
:‫דוגמא‬
a < b or (c < d and e < f) :‫ניזכר בביטוי של קודם‬
100: if a < b goto 103
101: T1 := 0
102: goto 104
103: T1 := 1
104: if c < d goto 107
105: T2 := 0
106: goto 108
107: T2 := 1
108: if e < f goto 111
109: T3 := 0
110: goto 112
111: T3 := 1
112: T4 := T2 and T3
113: T5 := T1 and T4
37
:‫חישוב מקוצר‬
100: if a < b goto 105
101: if !(c < d) goto 103
102: if e < f goto 105
103: T := 0
104: goto 106
105: T := 1
106:
‫תכונות של חישוב מקוצר‬
‫‪ .1‬האם החישוב מקוצר שקול לחישוב רגיל?‬
‫‪ .2‬מתי אסור להשתמש בחישוב מקוצר?‬
‫‪ .3‬מתי חייבים להשתמש בחישוב מקוצר?‬
‫תשובות‪:‬‬
‫‪ .1‬לא – יתכנו ‪ side-effects‬לחישוב ביטוי בוליאני‪.‬‬
‫דוגמא קלאסית‪if ( (i > 0) and (i++ < 10) ) A[i]=i else B[i]=i; :‬‬
‫‪ .2‬כאשר הגדרת השפה לא מרשה זאת‪.‬‬
‫‪ .3‬כאשר הגדרת השפה מחייבת קיצור‪ ,‬והמתכנת עלול להתבסס על כך‪.‬‬
‫דוגמא קלאסית‪:‬‬
‫;)‪if ( (file=open(“c:\grades”) or (die) ) printfile(file‬‬
‫‪38‬‬
‫טיפול בהפניות בקרה‪.if, else, while :‬‬
‫נחזור לאגור את הקוד בתכונה (‪ )attribute‬בשם קוד‪.‬‬
‫ההבדל בין ‪ emit‬ל‪ gen :gen -‬מחזירה את הפקודה שנוצרה; ‪ emit‬מדפיסה‬
‫אותה ל‪.buffer -‬‬
‫נתבונן בקפיצות מותנות‪:‬‬
‫‪S → if B then S1‬‬
‫‪| if B then S1 else S2‬‬
‫‪| while B do S1‬‬
‫אפשרות אחת היא לעבוד כמו קודם‪ ,‬לייצר קוד ל‪ B-‬לייצר קוד ל‪ ,S-‬ואז לייצר‬
‫קפיצה לתחילת ‪ S‬או סוף ‪ S‬כתלות בערך של ‪.B‬‬
‫אבל באופן יעיל יותר‪ ,‬אפשר פשוט לייצר קוד שבזמן החישוב של ‪ B‬יקפוץ למקום‬
‫הנכון ברגע שיתגלה מה ערכו של ‪.B‬‬
‫‪39‬‬
‫טיפול בהפניות בקרה‪.if, else, while :‬‬
‫מסתבר שיש כאן בעיה עם ההחלטה לאן לקפוץ בזמן הניתוח‪...‬‬
‫כאשר מנתחים את העץ שנפרש מ‪ B-‬עבור ”‪ "if B then S‬לא יודעים למה ‪S‬‬
‫יתפתח ואיפה מתחיל ונגמר הקוד של ‪ ,S‬אבל צריך לייצר קפיצות למקומות אלו‪.‬‬
‫השיטה – לכל ביטוי ‪ B‬נצמיד שתי תוויות‪ ,B.true :‬ו‪ B.false-‬שהן התוויות אליהן‬
‫החישוב צריך לעבור אם ‪ B‬הוא ‪( true‬או ‪ false‬בהתאמה)‪.‬‬
‫לכל פסוק ‪ S‬נחזיק תווית ‪ next‬שאומרת מה הכתובת של הקוד שאחריו‪.‬‬
‫‪S‬‬
‫‪S‬‬
‫‪then‬‬
‫‪B‬‬
‫‪if‬‬
‫משוואה סמנטיות‬
‫מתאימה‪:‬‬
‫‪B.false = S.next‬‬
‫לגבי ‪ ,B.true‬נייצר‬
‫‪ label‬בין הקוד של ‪B‬‬
‫לקוד של ‪ S‬ונייחס לו‬
‫את ‪.B.true‬‬
‫‪40‬‬
‫התכונה ‪next‬‬
‫בגזירה של פסוק ‪ ,S‬נייצר את הקוד עם התווית שאחריו‪:‬‬
‫; )(‪S.next = newlabel‬‬
‫; )‪P.code = S.code || label(S.next‬‬
‫‪P→S‬‬
‫; )(‪S1.next = newlabel‬‬
‫;‪S2.next = S.next‬‬
‫‪S.code = S1.code || gen(S1.next ‘:’ ( || S2.code‬‬
‫‪S → S1 S2‬‬
‫התכונה ‪ S.next‬היא נורשת‪ :‬הילד מקבל אותה כשהוא נגזר מאביו‪.‬‬
‫תכונת ה‪ code-‬היא נוצרת‪ :‬האבא מקבל אותה בעת גזירת ילדיו‪.‬‬
‫ה‪ S.next label-‬היא סימבולית‪ .‬הכתובת המתאימה לה תיוודע רק אחרי‬
‫שנגזור את כל הביטוי של ‪.S‬‬
‫‪41‬‬
If B then S
‫ עם‬B ‫ → קוד לחישוב‬to B.true
‫ → קפיצות החוצה‬to B.false
B.true:
S ‫קוד לחישוב‬
B.false:
...
S → if B then S1 {B.true := newlabel() ;
B.false := S.next ;
S1.next := S.next ;
S.code := B.code || gen ( B.true ' : ' ) || S1.code
}
‫ הן תכונות נורשות‬S1.next -‫ ו‬B.false
‫ היא תכונה נוצרת‬S.code
42
If B then S1 else S2
B.code
B.true:
S1.code
goto S.next
B.false:
S2.code
S.next:
...
→ to B.true
→ to B.false
‫ לא‬B.false-‫ ו‬B.True
‫נקבעים ע"י ההורים ולא‬
‫ אבל הם‬.‫ע"י הילדים‬
‫נקבעים בזמן גזירה‬
‫ הוא ילד ולכן‬B ‫שבה‬
.‫נחשבים נורשים‬
S → if B then S1 else S2 { B.true := newlabel ();
B.false := newlabel ();
S1.next := S.next ;
‫נורש‬
S2.next := S.next ;
S.code := B.code || gen ( B.true ' : ' ) || S1.code ||
‫נוצר‬
gen ( ' goto ' S.next ) || gen ( B.false ' : ' ) || S2.code
}
43
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
.false ‫ אם הוא‬B.false-‫ ול‬true B ‫ אם הערך של‬B.true-‫נייצר קוד שקופץ ל‬
B → B1 or B2
{ B1.true := B.true ; B1.false := newlable() ; B2.true := B.true ;
B2.false := B.false ; B.code := B1.code || gen ( B1.false ' : ') || B2.code
}
B → B1 and B2
{ B1.true := newlabel (); B1.false := B.false ; B2.true := B.true ;
B2.false := B.false ; B.code := B1.code || gen ( B1.true ' : '( || B2.code }
B → not B1
{ B1.true := B.false ; B1.false := B.true ; B.code := B1.code }
B → ( B1 )
{ B1.true := B.true ; B1.false := B.false ; B.code := B1.code }
B → id1 relop id2 { B.code := gen ( ' if ' id1.var relop.op id2.var ' goto ' B.true ) ||
gen ( ' goto ' B.false ) }
B → true
{ B.code := gen ( ' goto ' B.true ) }
B → false
{ B.code := gen ( ' goto ' B.false ) }
?‫איזו צורת חישוב מוצגת כאן? מקוצרת או מלאה‬
44
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
‫נייצר קוד שקופץ ל‪ B.true-‬אם הערך של ‪ true B‬ול‪ B.false-‬אם הוא ‪.false‬‬
‫; ‪{ B1.true := B.true ; B1.false := newlable() ; B2.true := B.true‬‬
‫‪B2.false := B.false ; B.code := B1.code || gen ( B1.false ' : ') || B2.code‬‬
‫}‬
‫‪B → B1 or B2‬‬
‫נתבונן לדוגמא ב‪.B1.false label-‬‬
‫הכתובת של ה‪ label-‬ניתנת לחישוב רק אחרי שנדע את כל הקוד של ‪ B1‬וכל‬
‫הקוד שלפני ‪.B1‬‬
‫‪45‬‬
‫דוגמא‬
S
then
B
if
B1
and
S1
B2
S -> if B then S1
true
false
S1.next = S.next;
B.true = newlabel();
B.false = S1.next;
S.code = B.code ||
B.true: || S1.code
46
‫דוגמא‬
S
then
B
if
B1
and
S1
B2
B -> B1 and B2
true
false
B -> true
B.code = goto B.true;
B -> false
B.code = goto B.false;
47
S1.next = S.next;
B1.true = newlabel();
B1.false = B.false;
B2.false = B.false;
B2.true = B.true;
B.code = B1.code ||
B1.true: || B2.code
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
‫במהלך החישוב הסמנטי נעבור על העץ מלמטה למעלה (למעשה אפשר‬
‫לעשות זאת במהלך ה‪ )bottom-up parsing-‬ונבנה את הקוד‪ ,‬תוך השארת‬
‫מקום ליעדי קפיצה שעדיין אינם ידועים‪.‬‬
‫לאחר מכן נצטרך לסרוק את העץ מלמעלה למטה ולשתול את יעדי הקפיצה‪.‬‬
‫האם אפשר לעשות הכל בסריקה יחידה?‬
‫‪48‬‬
‫‪ – Backpatching‬תיקון לאחור‬
‫מטרתנו להסתפק במעבר אחד על העץ בזמן היצירה שלו‪ ,‬ללא המעבר הנוסף‪.‬‬
‫‪ ‬השיטה‪ :‬נשמור לכל ‪ label‬את אוסף הכתובות של פקודות שמדלגות אליו‪.‬‬
‫‪ ‬ברגע שנדע את הכתובת של ה‪ ,label-‬נלך על רשימת הכתובות ונכניס‬
‫בפקודות הקפיצה המתאימות את הכתובת האמיתית של ה‪.label-‬‬
‫‪ ‬יתרון‪ :‬מעבר ‪ DFS‬יחיד יספיק (חישבו על אלפי שורות קוד)‪.‬‬
‫‪ ‬חסרון‪ :‬נצטרך להקצות מקום לרשימות של הכתובות‪.‬‬
‫נדגיש שפתרונות שהזכרנו בעבר לא יעבדו‪.‬‬
‫‪ ‬הגזירה אינה ‪( S-attributed‬יש גם תכונות נורשות‪ ,‬למשל ‪.)next‬‬
‫‪ ‬היא לא ‪( L-attributed‬התכונות הנורשות אינן בהכרח נורשות‪-‬משמאל)‬
‫‪ ‬לכן לא נוכל לחשב את התכונות תוך כדי הניתוח‪.‬‬
‫‪49‬‬
‫דוגמא להבהרת הקושי‬
‫‪S‬‬
‫‪S2‬‬
‫‪else‬‬
‫‪S1‬‬
‫‪then‬‬
‫חישבו על פסוק ‪.if-then-else‬‬
‫על‪-‬מנת לחשב את ‪ S1.next‬צריך כבר לדעת את הקוד של כל הבלוקים ‪,B‬‬
‫‪ ,S1‬ו‪( S2-‬כדי לדעת מהי הכתובת שאחריהם)‪.‬‬
‫מצד שני‪ ,‬כדי לחשב את הקוד של ‪ S1‬צריך להעביר לו את ‪ ,S1.next‬או‬
‫‪ ,S.next‬אבל ערך זה לא ידוע לפני החישוב של ‪.S1‬‬
‫‪B‬‬
‫‪if‬‬
‫כאמור‪ ,‬לא נוכל לחשב את הקוד של ‪ S1‬עם כל כתובות הקפיצה‪ ,‬אבל נוכל‬
‫לחשב אותו עד כדי "השארת מקום" להכנסה מאוחרת יותר של ‪.S1.next‬‬
‫בשיטת ה‪ backpatching-‬נבנה את הקוד ונשאיר לעצמנו רשימה עבור ה‪-‬‬
‫‪ label‬הסימבולי ‪ S1.next‬של כל שורות הקוד שבהן יש קפיצה אליו‪.‬‬
‫כשנדע את ערכו של ‪ ,S1.next‬נעבור על הרשימה ונעדכן‪.‬‬
‫‪50‬‬
‫פונקציות ליצירה וטיפול בהתחייבויות‬
‫) ‪ – makelist ( addr‬יצירת רשימת התחייבויות חדשה המכילה את הכתובת‬
‫‪ .addr‬התוצאה – מצביע לרשימה של כתובות של פקודות‪.‬‬
‫‪ addr ‬הוא מספר שורה ברשימת הרביעיות שלנו‬
‫‪ ‬המשמעות‪ :‬יש לתקן את הפקודה שבשורה ‪ addr‬כשיתקבל מידע רלוונטי‬
‫) ‪ – merge ( p1, p2‬איחוד הרשימות אליהם מצביעים ‪ p1‬ו‪ .p2 -‬מחזיר‬
‫מצביע לאיחוד הרשימות‪.‬‬
‫‪ ‬כלומר‪ ,‬שתי הרשימות מכילות פקודות שצריכות לקפוץ לאותו מקום‪.‬‬
‫) ‪ – backpatch ( p, addr‬קביעת הכתובת ‪ addr‬ככתובת הקפיצה בכל‬
‫אחת מהפקודות (רביעיות) שברשימה אליה מצביע ‪p‬‬
‫‪51‬‬
‫אגירת הקוד‬
‫נניח (כהרגלנו) ניתוח ‪ bottom-up‬כך שהקוד נוצר בסדר הנכון (שמאל לימין‪,‬‬
‫מלמטה למעלה)‪.‬‬
‫הניתוח הסמנטי יתבצע במהלך הניתוח התחבירי והקוד ייפלט לתוך ‪buffer‬‬
‫עם פקודת ‪( emit‬פשוט כדי שיהיה נוח לחשוב על כתובות של פקודות)‪.‬‬
‫אפשר גם לאסוף את הקוד בתוך תכונה‪ ,‬כל עוד יש דרך לשמור מצביע על‬
‫שורת קוד (שעליה יתבצע ‪.)backpatch‬‬
‫כזכור‪ ,‬לכל ביטוי ‪ B‬הצמדנו שתי תוויות‪ ,B.true :‬ו‪ B.false-‬שהן התוויות‬
‫אליהן החישוב צריך לעבור אם ‪ B‬הוא ‪( true‬או ‪ false‬בהתאמה)‪.‬‬
‫עתה תהיינה לנו גם זוג רשימות ‪ ,B.truelist :‬ו‪ B.falselist-‬שאומרות באילו‬
‫פקודות צריך לחזור ולעדכן את הכתובות של ‪ ,B.true :‬ו‪ B.false-‬כשמגלים‬
‫את ערכיהם‪.‬‬
‫בנוסף‪ ,‬לכל פסוק ‪ S‬שעבורו החזקנו ‪ label‬סימבולי ‪ ,S.next‬נחזיק עתה גם‬
‫רשימה ‪.S.nextlist‬‬
‫‪52‬‬
‫אגירת הקוד ‪ -‬המשך‬
‫‪ ,B.truelist‬ו‪ B.falselist-‬הן תכונות נוצרות‪ :‬הצאצאים מספרים לאב איפה‬
‫יש קוד שצריך לתקן‪.‬‬
‫כאשר עולים לאב של ‪ B‬עצמו‪ ,‬נדע מה הכתובת הרלוונטית ונוכל לבצע‬
‫‪ backpatch‬ולהכניס אותה לכל הפקודות שנרשמו ברשימה‪.‬‬
‫באופן דומה‪ ,‬ל‪ S.nextlist-‬תכונות דומות‪.‬‬
‫נשתמש בפונקציה ‪ nextinstr‬שתחזיר את הכתובת של הפקודה הבאה‪.‬‬
‫‪53‬‬
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
.B ‫ לפי הערך של‬B.false-‫ או ל‬B.true-‫ הקוד שקופץ ל‬,‫כזכור‬
:‫קודם היה‬
B → id1 relop id2 { B.code := gen ( ' if ' id1.var relop.op id2.var ' goto ' B.true ) ||
gen ( ' goto ' B.false ) }
B → true
{ B.code := gen ( ' goto ' B.true ) }
B → false
{ B.code := gen ( ' goto ' B.false ) }
:backpatching ‫ועכשיו עם‬
B → id1 relop id2
B → true
B → false
54
{ B.truelist := makelist ( nextinstr ) ;
B.falselist := makelist ( nextinstr + 1 ) ;
emit ( ' if ' id1.var relop.op id2.var ' goto_ ' ) || emit ( ' goto_ ' ) }
{ B.truelist := makelist ( nextinstr ) ; emit ( ' goto_ ' ) }
{ B.falselist := makelist ( nextinstr ) ; emit ( ' goto_ ' ) }
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
.B ‫ לפי הערך של‬B.false-‫ או ל‬B.true-‫ הקוד שקופץ ל‬,‫כזכור‬
:‫קודם היה‬
B → not B1
{ B1.true := B.false ; B1.false := B.true ; B.code := B1.code }
B → ( B1 )
{ B1.true := B.true ; B1.false := B.false ; B.code := B1.code }
:backpatching ‫ועכשיו עם‬
B → not B1
B → ( B1 )
55
{ B.truelist := B1.falselist ; B.falselist := B1.truelist }
{ B.truelist := B1.truelist ; B.falselist := B1.falselist }
‫חישוב ביטויים בוליאניים על ידי הפנית בקרה‬
.B ‫ לפי הערך של‬B.false-‫ או ל‬B.true-‫ הקוד שקופץ ל‬,‫כזכור‬
:‫קודם היה‬
B → B1 or B2
{ B1.true := B.true ; B1.false := newlable() ; B2.true := B.true ;
B2.false := B.false ; B.code := B1.code || gen ( B1.false ' : ') || B2.code
}
B → B1 and B2
{ B1.true := newlabel (); B1.false := B.false ; B2.true := B.true ;
B2.false := B.false ; B.code := B1.code || gen ( B1.true ' : '( || B2.code }
:backpatching ‫ועכשיו עם‬
B → B1 or M B2
B → B1 and M B2
M→
56
{ backpatch ( B1.falselist, M.instr ) ;
B.truelist := merge ( B1.truelist, B2.truelist ) ; B.falselist := B2.falselist
}
{ backpatch ( B1.truelist, M.instr ) ; B.truelist := B2.truelist ;
B.falselist := merge ( B1.falselist, B2.falselist ) }
{ M.instr := nextinstr }
‫טריק ה‪ marker-‬הסטנדרטי‬
‫למשל‪ B → B1 or M B2 :‬ו‪.M →  -‬‬
‫בזמן הגזירה של ‪ M‬משיגים את הכתובת של תחילת ‪.B1‬‬
‫‪B‬‬
‫‪B1‬‬
‫‪M‬‬
‫‪or‬‬
‫‪B1‬‬
‫‪‬‬
‫‪57‬‬
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
and
B.t = {102}
B.f = {103}
c
58
<
B.t = {104}
B.f = {103, 105}
M.i = 104
B.t = {104}
B.f = {105}

d
e
<
f
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
and
B.t = {102}
B.f = {103}
c
100 if a < b goto ___
101 goto ___
59
<
B.t = {104}
B.f = {103, 105}
M.i = 104
B.t = {104}
B.f = {105}

d
e
<
f
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
B.t = {104}
B.f = {103, 105}
and
B.t = {102}
B.f = {103}
B.t = {104}
B.f = {105}

c
<
d
102
103
if c < d goto ___
goto ___
100 if a < b goto ___
101 goto ___
60
M.i = 104
e
<
f
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
B.t = {104}
B.f = {103, 105}
and
B.t = {102}
B.f = {103}
M.i = 104
B.t = {104}
B.f = {105}

c
<
d
102
103
if c < d goto ___
goto ___
e
<
f
100 if a < b goto ___
101 goto ___
104
105
61
if e < f goto ___
goto ___
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
B.t = {104}
B.f = {103, 105}
and
B.t = {102}
B.f = {103}
M.i = 104
B.t = {104}
B.f = {105}

c
<
d
102
103
if c < d goto 104
goto ___
e
<
f
100 if a < b goto ___
101 goto ___
104
105
62
if e < f goto ___
goto ___
‫תיקון לאחור‬
B.t = {100, 104}
B.f = {103, 105}
B.t = {100}
B.f = {101}
a
<
or
M.i = 102

b
B.t = {104}
B.f = {103, 105}
and
B.t = {102}
B.f = {103}
M.i = 104
B.t = {104}
B.f = {105}

c
<
d
102
103
if c < d goto 104
goto ___
e
<
f
100 if a < b goto ___
101 goto 102
104
105
63
if e < f goto ___
goto ___
IF :‫פסוקים עם הפנית בקרה‬
S → if B then M1 S1 N else M2 S2
{ backpatch ( B.truelist , M1.instr ) ;
backpatch ( B.falselist , M2.instr ) ;
S.nextlist := merge (S1.nextlist,N.nextlist,S2.nextlist ) }
N→
{ N.nextlist := makelist ( nextinstr ) ; emit ( ' goto_ ' ) }
M→
{ M.instr := nextinstr }
B → ( B1 )
{ B.truelist := B1.truelist ; B.falselist := B1.falselist }
S → if B then M S1
{ backpatch (B.truelist , M.instr ) ;
S.nextlist := merge ( B.falselist, S1.nextlist ) }
64
While :‫פסוקים עם הפנית בקרה‬
S → while M1 B do M2 S1
{ backpatch ( S1.nextlist, M1.instr ) ;
backpatch ( B.truelist , M2.instr ) } ;
S.nextlist := B.falselist ;
emit ( ' goto_ ' M1.instr ) }
S → begin L end { S.nextlist := L.nextlist }
S→A
{ S.nextlist := makelist ( ) }
L → L1 ; M S
{ backpatch (L1.nextlist , M.instr ) ; L.nextlist := S.nextlist }
L→S
{ L.nextlist := S.nextlist }
65
‫לסיכום‬
‫יצירת קוד משולבת בניתוח הסמנטי (בפעולות הסמנטיות)‬
‫או שפולטים קוד לבפר באמצעות ‪ ,emit‬או שאוספים את הקוד כתכונה של‬
‫המשתנים הנגזרים‪ ,‬וכשגומרים‪ ,‬ה‪ code-‬של המשתנה ההתחלתי ‪ S‬הוא הקוד‬
‫הנדרש‪.‬‬
‫הניתוח של ביטויים לוגיים ופסוקי הפניית הבקרה הם יותר מסובכים‪.‬‬
‫ראשית צריך להחליט על ביצוע מקוצר (או לא)‪.‬‬
‫שנית‪ ,‬צריך לדאוג להכנסת כתובות הקפיצה אחרי שמגלים את ערכן‪.‬‬
‫אפשרות אחת‪ :‬שימוש ב‪ labels-‬סימבוליים ומעבר נוסף לעידכונם לכתובת‬
‫אמיתית‪.‬‬
‫אפשרות שניה ‪ :backpatching -‬מעבר יחיד על העץ‪ ,‬אך שמירת רשימה של‬
‫כל המקומות אליהן צריך להכניס את הכתובת של ‪ label‬מסוים‪ .‬כשמגלים את‬
‫מיקום ה‪ ,label-‬מעדכנים את כל הפקודות לפי הכתובות שברשימה‪.‬‬
‫‪66‬‬