การเรียกซับรูทีนในยุคโบราณที่คอมพิวเตอร์ยังไม่มีสแตกและฮีป
- ในการประมวลผลสมัยใหม่ เรามองว่าสแตกและฮีปเป็นเรื่องปกติ แต่ในยุคแรกเริ่มของการประมวลผล คอมพิวเตอร์ทำงานได้โดยไม่มีทั้งสแตกและฮีป
- การจินตนาการถึงการประมวลผลโดยไม่มีการจัดสรรหน่วยความจำแบบไดนามิกไม่ใช่เรื่องยากนัก ทุกอย่างต้องใช้บัฟเฟอร์หน่วยความจำขนาดคงที่
- หากต้องจัดการข้อมูลขนาดแปรผัน ก็จะจองบัฟเฟอร์ขนาดคงที่ที่ใหญ่พอสำหรับรองรับข้อมูลที่คาดว่าจะเกิดขึ้น
- อาจมีการกำหนดค่าตอนคอมไพล์เพื่อให้ผู้ใช้ปรับความจุสูงสุดได้ หรือเขียนตัวจัดสรรแบบกำหนดเองที่สามารถ "จัดสรร" และ "คืน" หน่วยความจำจากบัฟเฟอร์ขนาดคงที่ได้
การเรียกฟังก์ชันโดยไม่มีสแตก
- คอมไพเลอร์จะกำหนดตัวแปรโกลบอลลับสำหรับพารามิเตอร์ขาเข้า ที่อยู่สำหรับคืนค่า และตัวแปรโลคัลของแต่ละฟังก์ชัน
- เพื่อสร้างการเรียกฟังก์ชัน คอมไพเลอร์จะกำหนดค่าพารามิเตอร์ไปยังตัวแปรโกลบอลลับเหล่านั้น กำหนดที่อยู่สำหรับคืนค่าไปยัง "ตัวแปรที่อยู่คืนค่า" ลับของฟังก์ชัน จากนั้นกระโดดไปยังจุดเริ่มต้นของฟังก์ชัน
- ฟังก์ชันจะอ่านพารามิเตอร์จากตัวแปรโกลบอลลับ และใช้ตัวแปรโกลบอลลับที่กำหนดไว้ล่วงหน้าซึ่งสอดคล้องกับตัวแปรโลคัลในเชิงตรรกะ
- เมื่อฟังก์ชันจบลง ก็จะกระโดดไปยังที่อยู่ซึ่งอยู่ใน "ตัวแปรที่อยู่คืนค่า" ลับของฟังก์ชัน
การปรับแต่ง ABI
- เพื่อเพิ่มประสิทธิภาพของ ABI ค่าบางส่วนจะถูกส่งผ่านรีจิสเตอร์แทนตัวแปรโกลบอล
- โปรเซสเซอร์ส่วนใหญ่มีรีจิสเตอร์ "ลิงก์" และคำสั่ง "branch with link" ซึ่งจะตั้งค่ารีจิสเตอร์ลิงก์ให้เป็นที่อยู่ถัดจากคำสั่ง "branch with link" โดยอัตโนมัติ
- มีการปรับแต่ง calling convention ให้ส่งพารามิเตอร์สองตัวแรกผ่านรีจิสเตอร์
ความเป็นไปไม่ได้ของการเรียกซ้ำแบบรีเคอร์ซีฟ
- การเรียกซ้ำแบบรีเคอร์ซีฟใช้การไม่ได้ เพราะมันจะเขียนทับตัวแปรที่อยู่คืนค่าด้วยที่อยู่คืนค่าของการเรียกซ้ำ ทำให้เมื่อการเรียกภายนอกเสร็จสิ้นแล้วกระโดดไปยังตำแหน่งที่ผิด
- ภาษาการเขียนโปรแกรมในยุคนั้นแก้ปัญหานี้ด้วยการประกาศว่าไม่รองรับรีเคอร์ชัน
บทสนทนาโบนัส
- คอมไพเลอร์บางตัวทำงานอย่างแยบยลยิ่งขึ้นด้วยการใช้โค้ดแก้ไขตัวเอง: ตัวแปรที่อยู่คืนค่าพิเศษนั้นแท้จริงแล้วคือช่องที่อยู่ของคำสั่งกระโดดที่ท้ายฟังก์ชัน
- หากโปรเซสเซอร์ไม่รองรับการกระโดดทางอ้อม วิธีนี้ก็ถูกใช้งานเพราะเป็นความจำเป็นในทางปฏิบัติ
- หลังจากที่คุณค่าการใช้งานของซับรูทีนได้รับการยอมรับ โปรเซสเซอร์จำนวนมากก็เพิ่มคำสั่งเรียกซับรูทีน โดยบันทึกที่อยู่คืนค่าไว้ในคำแรกของซับรูทีน และเริ่มการทำงานจากคำที่สองของซับรูทีน
- เมื่อต้องการคืนค่าจากซับรูทีน ก็จะทำการกระโดดทางอ้อมผ่านเลเบลเริ่มต้นของซับรูทีน
ความเห็นของ GN⁺
- บทความนี้อธิบายวิธีการเขียนโปรแกรมในยุคคอมพิวเตอร์ตอนต้นที่ยังไม่มีสแตกและฮีป ซึ่งช่วยให้เข้าใจพัฒนาการของเทคนิคการจัดการหน่วยความจำที่ใช้ในการพัฒนาซอฟต์แวร์สมัยใหม่
- วิธีการเขียนโปรแกรมในยุคที่ไม่มีสแตกและฮีปอาจดูแปลกมากและไม่มีประสิทธิภาพสำหรับนักพัฒนาสมัยใหม่ แต่เป็นพื้นฐานความรู้สำคัญในการทำความเข้าใจว่าเทคโนโลยีพัฒนามาอย่างไรผ่านประวัติศาสตร์ของการประมวลผล
- ข้อจำกัดด้านการเขียนโปรแกรมในยุคที่การเรียกซ้ำแบบรีเคอร์ซีฟเป็นไปไม่ได้ เป็นเกร็ดประวัติศาสตร์ที่น่าสนใจสำหรับนักพัฒนาที่ใช้ขั้นตอนวิธีแบบรีเคอร์ซีฟในปัจจุบัน
- หากมองอย่างวิพากษ์ วิธีการเขียนโปรแกรมยุคแรกเหล่านี้ก็แสดงให้เห็นว่ามันมีข้อจำกัดอย่างมากเกินกว่าจะรองรับความต้องการที่ซับซ้อนและหลากหลายในปัจจุบันได้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
รีวิวเชิงบวกต่อหนังสือ "The Art of Computer Programming"
คำอธิบายเกี่ยวกับวิธีที่อาร์เรย์สองชุดแชร์พื้นที่เดียวกันแบบไดนามิก
แชร์ลิงก์เรื่องเล่าสนุก ๆ ว่าการนำ recursive function เข้าสู่ภาษา ALGOL เคยเป็นประเด็นถกเถียง
แชร์ประสบการณ์การเขียน Forth interpreter สำหรับเครื่อง SUBLEQ และเครื่อง bit-serial
คำอธิบายเกี่ยวกับวิวัฒนาการทางเทคนิคของการเรียก subroutine บนโปรเซสเซอร์ PDP-8
แชร์ความชอบเรื่อง recursion จากผู้ใช้ที่มีประสบการณ์กับ functional programming มาอย่างยาวนาน
แชร์ประสบการณ์ออกแบบ RS232 serial multiplexer ราวปี 1991
กล่าวถึงสถานการณ์ในอดีตก่อนที่ heap จะขยายได้ ว่าโปรแกรมเมอร์ต้องพิจารณาการกระจายตัวที่เป็นไปได้ของอินพุตและกำหนดขนาดของพื้นที่เก็บชั่วคราวระหว่างทางให้เหมาะสม
คำอธิบายเกี่ยวกับยุคที่ไม่สามารถใช้ recursion ได้ แต่ยังใช้ tail recursion ได้
branch_with_linkที่ใช้สำหรับการเรียกครั้งแรกแล้ว จะต้องใช้ branch ปกติอธิบายวิธีที่คอมไพเลอร์ของ Enhanced GNU Awk จัดสรรตัวแปร global ลับให้กับบล็อก @let ที่อยู่นอกฟังก์ชัน
กล่าวถึงโพสต์ที่บรรยายโลกของบทความ "Goto considered harmful"