คุณอาจจะสนใจในนิสันและ Schocken ของการดำเนินงานของฟังก์ชั่น pdf ที่เชื่อมโยงเป็นส่วนหนึ่งของหลักสูตรออนไลน์ฟรี อธิบายถึงส่วนที่สองของการใช้งานเครื่องเสมือนซึ่งนักเรียนควรเขียนคอมไพเลอร์เสมือนภาษาเครื่องเป็นภาษาเครื่อง การใช้งานฟังก์ชันที่พวกเขาเสนอนั้นสามารถเรียกซ้ำได้เนื่องจากเป็นแบบสแต็ก
เพื่อแนะนำคุณเกี่ยวกับการใช้งานฟังก์ชัน: พิจารณารหัสเครื่องเสมือนต่อไปนี้:
หาก Swift คอมไพล์เป็นภาษาเครื่องเสมือนนี้บล็อกของรหัส Swift ต่อไปนี้:
mult(a: 2, b: 3) - 4
จะรวบรวมลงไปที่
push constant 2 // Line 1
push constant 3 // Line 2
call mult // Line 3
push constant 4 // Line 4
sub // Line 5
ภาษาเครื่องเสมือนได้รับการออกแบบรอบสแต็คระดับโลกpush constant n
ดันจำนวนเต็มไปยังสแต็กส่วนกลางนี้
หลังจากดำเนินการบรรทัดที่ 1 และ 2 สแต็กจะมีลักษณะดังนี้:
256: 2 // Argument 0
257: 3 // Argument 1
256
และ257
เป็นที่อยู่หน่วยความจำ
call mult
ดันหมายเลขบรรทัดส่งกลับ (3) ไปยังสแต็กและจัดสรรพื้นที่สำหรับตัวแปรโลคัลของฟังก์ชัน
256: 2 // argument 0
257: 3 // argument 1
258: 3 // return line number
259: 0 // local 0
... function mult
และมันจะไปต่อฉลาก รหัสภายในmult
จะถูกเรียกใช้งาน อันเป็นผลมาจากการเรียกใช้รหัสนั้นเราจะคำนวณผลคูณของ 2 และ 3 ซึ่งเก็บไว้ในตัวแปรท้องถิ่นที่ 0 ของฟังก์ชัน
256: 2 // argument 0
257: 3 // argument 1
258: 3 // return line number
259: 6 // local 0
ก่อนreturn
ing จาก mult คุณจะสังเกตเห็นบรรทัด:
push local 0 // push result
เราจะดันผลิตภัณฑ์ลงบนกอง
256: 2 // argument 0
257: 3 // argument 1
258: 3 // return line number
259: 6 // local 0
260: 6 // product
เมื่อเรากลับมาสิ่งต่อไปนี้จะเกิดขึ้น:
- ใส่ค่าสุดท้ายบนสแต็กไปยังที่อยู่หน่วยความจำของอาร์กิวเมนต์ที่ 0 (256 ในกรณีนี้) สิ่งนี้เป็นจุดที่สะดวกที่สุดในการวาง
- ทิ้งทุกอย่างในสแต็กจนถึงที่อยู่ของอาร์กิวเมนต์ที่ 0
- ไปที่หมายเลขบรรทัดส่งกลับ (3 ในกรณีนี้) แล้วเลื่อนไปข้างหน้า
หลังจากกลับมาเราพร้อมที่จะดำเนินการบรรทัดที่ 4 และสแต็คของเรามีลักษณะดังนี้:
256: 6 // product that we just returned
ตอนนี้เราดัน 4 เข้าสู่สแต็ก
256: 6
257: 4
sub
เป็นฟังก์ชันดั้งเดิมของภาษาเครื่องเสมือน ใช้สองอาร์กิวเมนต์และส่งคืนผลลัพธ์ในที่อยู่ปกตินั่นคืออาร์กิวเมนต์ที่ 0
ตอนนี้เรามี
256: 2 // 6 - 4 = 2
ตอนนี้คุณรู้แล้วว่าการเรียกใช้ฟังก์ชันทำงานอย่างไรจึงค่อนข้างง่ายที่จะเข้าใจว่าการเรียกซ้ำทำงานอย่างไร ไม่มีเวทมนตร์แค่กอง
ฉันได้ใช้งานsumInts
ฟังก์ชันของคุณในภาษาเครื่องเสมือนนี้:
function sumInts 0 // `0` means it has no local variables.
label IF
push argument 0
push argument 1
lte
if-goto ELSE_CASE
push constant 0
return
label ELSE_CASE
push constant 2
push argument 0
push constant 1
add
push argument 1
call sumInts // Line 15
add // Line 16
return // Line 17
// End of function
ตอนนี้ฉันจะเรียกมันว่า:
push constant 2
push constant 5
call sumInts // Line 21
รหัสดำเนินการและเราไปถึงจุดหยุดที่lte
จะกลับfalse
มา นี่คือลักษณะของสแต็ก ณ จุดนี้:
// First invocation
256: 2 // argument 0
257: 5 // argument 1
258: 21 // return line number
259: 2 // augend
// Second
260: 3 // argument 0
261: 5 // argument 1
262: 15 // return line number
263: 3 // augend
// Third
264: 4 // argument 0
265: 5 // argument 1
266: 15 // return line number
267: 4 // augend
// Fourth
268: 5 // argument 0
269: 5 // argument 1
270: 15 // return line number
271: 5 // augend
// Fifth
272: 6 // argument 0
273: 5 // argument 1
274: 15 // return line number
275: 0 // return value
ทีนี้มา "คลาย" การเรียกซ้ำของเรา return
0 และไปที่บรรทัดที่ 15 และเลื่อนไป
271: 5
272: 0
บรรทัดที่ 16: add
271: 5
บรรทัดที่ 17: return
5 และไปยังบรรทัดที่ 15 และไปข้างหน้า
267: 4
268: 5
บรรทัดที่ 16: add
267: 9
บรรทัดที่ 17: return
9 และไปยังบรรทัดที่ 15 และไปข้างหน้า
263: 3
264: 9
บรรทัดที่ 16: add
263: 12
บรรทัดที่return
17:12 และไปยังบรรทัดที่ 15 และไปข้างหน้า
259: 2
260: 12
บรรทัดที่ 16: add
259: 14
บรรทัดที่ 17: return
14 และไปที่สาย 21 และไปข้างหน้า
256: 14
ที่นั่นคุณมี recursion: goto
สดุดี