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