อาจเป็นคำถามแปลก ๆ
ผู้ชายกำลังเขียนคอมไพเลอร์ C ++ (หรือภาษาที่ไม่ใช่ VM): เขาจำเป็นต้องสามารถอ่าน / เขียนภาษาเครื่องดิบได้หรือไม่? มันทำงานอย่างไร
แก้ไข: ฉันหมายถึงเฉพาะผู้แปลที่รวบรวมรหัสเครื่องไม่ใช่ภาษาการเขียนโปรแกรมอื่น
อาจเป็นคำถามแปลก ๆ
ผู้ชายกำลังเขียนคอมไพเลอร์ C ++ (หรือภาษาที่ไม่ใช่ VM): เขาจำเป็นต้องสามารถอ่าน / เขียนภาษาเครื่องดิบได้หรือไม่? มันทำงานอย่างไร
แก้ไข: ฉันหมายถึงเฉพาะผู้แปลที่รวบรวมรหัสเครื่องไม่ใช่ภาษาการเขียนโปรแกรมอื่น
คำตอบ:
ไม่เลย. เป็นไปได้อย่างสมบูรณ์ (และมักจะชอบมากกว่า) สำหรับคอมไพเลอร์ของคุณที่จะปล่อยรหัสประกอบแทน แอสเซมเบลอร์จะดูแลการสร้างรหัสเครื่องจริง
วิธีการแยกความแตกต่างของการใช้งานที่ไม่ใช่ VM และการใช้งาน VM ไม่เป็นประโยชน์
สำหรับผู้เริ่มต้นการใช้ VM หรือการคอมไพล์ล่วงหน้ากับรหัสเครื่องเป็นวิธีที่แตกต่างกันในการใช้ภาษา ในกรณีส่วนใหญ่ภาษาสามารถนำมาใช้โดยใช้กลยุทธ์ใดกลยุทธ์หนึ่ง จริง ๆ แล้วฉันต้องใช้ล่าม C ++ ครั้งเดียว
นอกจากนี้ VMs จำนวนมากเช่น JVM ทั้งคู่มีรหัสเครื่องไบนารีและแอสเซมเบลอร์บางตัวเหมือนกับสถาปัตยกรรมทั่วไป
LLVM (ซึ่งใช้โดยคอมไพเลอร์เสียงดังกราว) สมควรได้รับการกล่าวถึงเป็นพิเศษที่นี่: มันกำหนด VM ซึ่งคำสั่งที่สามารถแสดงเป็นรหัสไบต์ชุดข้อความหรือโครงสร้างข้อมูลซึ่งทำให้ง่ายต่อการเปล่งจากคอมไพเลอร์ ดังนั้นแม้ว่ามันจะมีประโยชน์สำหรับการดีบัก (และเพื่อทำความเข้าใจสิ่งที่คุณกำลังทำ) คุณไม่จำเป็นต้องรู้เกี่ยวกับภาษาแอสเซมบลี แต่เกี่ยวกับ LLVM API เท่านั้น
สิ่งที่ดีเกี่ยวกับ LLVM คือ VM ของมันเป็นเพียงนามธรรมและรหัสไบต์มักจะไม่ถูกตีความ แต่ JITted โปร่งใสแทน ดังนั้นจึงเป็นไปได้ทั้งหมดที่จะเขียนภาษาที่เรียบเรียงอย่างมีประสิทธิภาพโดยไม่ต้องรู้เกี่ยวกับชุดคำสั่งของ CPU
ไม่จุดสำคัญของคำถามของคุณคือการรวบรวมเป็นคำที่กว้างมาก การรวบรวมสามารถเกิดขึ้นได้จากภาษาใด ๆ กับภาษาใด ๆ และแอสเซมบลี / รหัสเครื่องเป็นเพียงหนึ่งในหลายภาษาสำหรับเป้าหมายการรวบรวม ตัวอย่างเช่นภาษา Java และ. NET เช่น C #, F # และ VB.NET ทั้งหมดรวบรวมรหัสกลางบางชนิดแทนรหัสเฉพาะเครื่อง ไม่สำคัญว่าจะทำงานบน VM หรือไม่ภาษาก็ยังคงถูกคอมไพล์ นอกจากนี้ยังมีตัวเลือกในการรวบรวมภาษาอื่นเช่น C. C เป็นเป้าหมายการรวบรวมที่เป็นที่นิยมมากและมีเครื่องมือมากมายที่ทำได้ และในที่สุดคุณสามารถใช้เครื่องมือหรือไลบรารีเพื่อทำงานอย่างหนักในการสร้างรหัสเครื่องให้คุณ ตัวอย่างเช่นLLVMซึ่งสามารถลดความพยายามในการสร้างคอมไพเลอร์แบบสแตนด์อโลน
นอกจากนี้การแก้ไขของคุณก็ไม่สมเหตุสมผล มันเหมือนกับถามว่า "วิศวกรทุกคนจำเป็นต้องเข้าใจวิธีการทำงานของเครื่องยนต์หรือไม่และฉันถามเกี่ยวกับวิศวกรที่ทำงานกับเครื่องยนต์" หากคุณกำลังทำงานกับโปรแกรมหรือไลบรารีที่ปล่อยรหัสเครื่องคุณต้องเข้าใจ ประเด็นคือคุณไม่ต้องทำสิ่งนี้เมื่อเขียนคอมไพเลอร์ หลายคนทำก่อนคุณดังนั้นคุณต้องมีเหตุผลที่จริงจังที่จะทำมันอีกครั้ง
คอมไพเลอร์แบบคลาสสิกมีสามส่วนคือการวิเคราะห์คำศัพท์การแยกวิเคราะห์และการสร้างโค้ด การวิเคราะห์คำศัพท์แบ่งข้อความของโปรแกรมเป็นคำหลักภาษาชื่อและค่า การแยกตัวเลขว่าโทเค็นที่มาจากการวิเคราะห์คำศัพท์จะรวมกันในงบที่ถูกต้องสำหรับภาษา การสร้างรหัสใช้โครงสร้างข้อมูลที่สร้างโดย parser และแปลเป็นรหัสเครื่องหรือการแทนอื่น ๆ ทุกวันนี้การวิเคราะห์คำและการแยกวิเคราะห์อาจรวมกันเป็นขั้นตอนเดียว
ชัดเจนว่าคนที่เขียนตัวสร้างรหัสต้องเข้าใจรหัสเครื่องเป้าหมายในระดับลึกมากรวมถึงชุดคำสั่ง, ตัวประมวลผลไพพ์ไลน์และลักษณะการทำงานแคช มิฉะนั้นโปรแกรมที่ผลิตโดยคอมไพเลอร์จะช้าและไม่มีประสิทธิภาพ พวกเขาเป็นอย่างดีอาจจะสามารถอ่านและเขียนรหัสเครื่องตามที่แสดงด้วยเลขฐานแปดหรือเลขฐานสิบหก แต่โดยทั่วไปแล้วพวกเขาจะเขียนฟังก์ชันเพื่อสร้างรหัสเครื่องโดยอ้างอิงจากตารางของคำสั่งเครื่องภายใน ในทางทฤษฎีคนเขียน lexer และ parser อาจไม่รู้อะไรเกี่ยวกับการสร้างรหัสเครื่อง อันที่จริงคอมไพเลอร์สมัยใหม่บางตัวช่วยให้คุณเสียบรูทีนการสร้างรหัสของคุณเองซึ่งอาจปล่อยรหัสเครื่องสำหรับ CPU บางตัวที่ผู้เขียน lexer และ parser ไม่เคยได้ยินมาก่อน
อย่างไรก็ตามในทางปฏิบัติผู้เขียนคอมไพเลอร์ในแต่ละขั้นตอนรู้มากเกี่ยวกับสถาปัตยกรรมโปรเซสเซอร์ที่แตกต่างกันและที่ช่วยให้พวกเขาออกแบบโครงสร้างข้อมูลขั้นตอนการสร้างรหัสจะต้อง
นานมาแล้วฉันเขียนคอมไพเลอร์ที่แปลงระหว่างสองเชลล์สคริปต์ที่แตกต่างกัน มันไม่มีทางใกล้กับรหัสเครื่อง
คอมไพเลอร์เขียนต้องเข้าใจผลลัพธ์ของพวกเขาแต่นั่นมักจะไม่ใช่รหัสเครื่อง
โปรแกรมเมอร์ส่วนใหญ่จะไม่เขียนคอมไพเลอร์ที่เอาท์พุทรหัสเครื่องหรือรหัสแอสเซมบลี แต่คอมไพเลอร์แบบกำหนดเองอาจมีประโยชน์มากในโครงการจำนวนมาก
YACC เป็นคอมไพเลอร์ตัวหนึ่งที่ไม่ส่งสัญญาณรหัสเครื่อง….
คุณไม่จำเป็นต้องเริ่มต้นด้วยความรู้รายละเอียดของความหมายของอินพุตและเอาต์พุตภาษาของคุณ แต่คุณดีกว่าจบด้วยความรู้ที่มีรายละเอียดอย่างประณีตทั้งสองอย่างมิฉะนั้นคอมไพเลอร์ของคุณ ดังนั้นหากอินพุตของคุณคือ C ++ และเอาต์พุตของคุณเป็นภาษาเครื่องเฉพาะคุณจะต้องรู้ถึงความหมายของทั้งคู่
นี่คือรายละเอียดปลีกย่อยบางส่วนในการคอมไพล์รหัส C ++ กับเครื่อง: (ด้านบนสุดของหัวของฉันฉันแน่ใจว่ามีอีกมากที่ฉันลืม)
จะมีขนาดเท่าint
ไหร่ ตัวเลือก "ถูกต้อง" ที่นี่เป็นศิลปะตามขนาดตัวชี้ธรรมชาติของเครื่องประสิทธิภาพของ ALU สำหรับการดำเนินการทางคณิตศาสตร์ในขนาดต่างๆและตัวเลือกที่ทำโดยคอมไพเลอร์ที่มีอยู่สำหรับเครื่อง เครื่องมีเลขคณิต 64 บิตหรือไม่? หากไม่ใช่จากนั้นการเพิ่มจำนวนเต็มแบบ 32 บิตควรแปลเป็นคำสั่งในขณะที่การเพิ่มจำนวนเต็มแบบ 64 บิตควรแปลเป็นการเรียกใช้ฟังก์ชันเพื่อทำการเพิ่มแบบ 64 บิต เครื่องมีการเพิ่มแบบ 8 บิตและ 16 บิตหรือไม่หรือคุณต้องจำลองการทำงานด้วย ops และการปิดบังแบบ 32 บิต (เช่น DEC Alpha 21064) หรือไม่?
ระเบียบการเรียกที่ใช้โดยคอมไพเลอร์ห้องสมุดและภาษาอื่น ๆ บนเครื่องคืออะไร? พารามิเตอร์ถูกส่งไปบนสแต็กจากขวาไปซ้ายหรือจากซ้ายไปขวาหรือไม่? ทำพารามิเตอร์บางอย่างไปในการลงทะเบียนในขณะที่คนอื่นไปในกอง? ints และลอยในช่องว่างการลงทะเบียนที่แตกต่างกัน? พารามิเตอร์การจัดสรรรีจิสเตอร์จำเป็นต้องได้รับการดูแลเป็นพิเศษจากการโทรแบบ varargs หรือไม่? รีจิสเตอร์ใดบ้างที่ถูกบันทึกไว้สำหรับการโทร คุณสามารถเพิ่มประสิทธิภาพการโทรออกได้หรือไม่?
คำแนะนำการเลื่อนของแต่ละเครื่องทำอะไร? หากคุณขอให้เปลี่ยนจำนวนเต็ม 64 บิตเป็น 65 บิตผลลัพธ์คืออะไร (ในหลาย ๆ เครื่องผลลัพธ์จะเหมือนกับการขยับ 1 บิตส่วนอื่น ๆ ผลลัพธ์จะเป็น "0")
ซีแมนทิกส์ความสอดคล้องของหน่วยความจำคืออะไร? C ++ 11 มีซีแมนทิกส์ของหน่วยความจำที่กำหนดไว้เป็นอย่างดีซึ่งวางข้อ จำกัด ในการปรับให้เหมาะสมบางอย่างในบางกรณี แต่อนุญาตการปรับให้เหมาะสมในกรณีอื่น ๆ หากคุณกำลังรวบรวมภาษาที่ไม่ได้มีการกำหนดไว้อย่างดีความหมายของหน่วยความจำ (เช่นรุ่น C / C ++ ก่อน C ++ 11 และอีกหลายภาษาที่จำเป็นอื่น ๆ ทุกครั้ง) แล้วคุณจะมีการคิดค้นความหมายของหน่วยความจำที่คุณไปพร้อมและมักจะ คุณจะต้องคิดค้นซีแมนทิกส์ของหน่วยความจำที่ตรงกับซีแมนติกส์ของเครื่องมากที่สุด