מה זה LLVM? הכוח מאחורי סוויפט, רוסט, קלאנג ועוד

שפות חדשות, ושיפורים בשפות הקיימות, מצטיינות בכל הנוף המפותח. החלודה של מוזילה, הסוויפט של אפל, הקוטלין של Jetbrains ושפות רבות אחרות מספקות למפתחים מגוון חדש של אפשרויות למהירות, בטיחות, נוחות, ניידות וכוח.

למה עכשיו? סיבה אחת גדולה היא כלים חדשים לבניית שפות - במיוחד מהדרים. והראשית ביניהן היא LLVM, פרויקט קוד פתוח שפותח במקור על ידי יוצר השפה המהירה כריס לטנר כפרויקט מחקר באוניברסיטת אילינוי.

LLVM מקל לא רק ליצור שפות חדשות אלא גם לשפר את הפיתוח של השפות הקיימות. הוא מספק כלים לאוטומציה של רבים מהחלקים המופרכים ביותר במשימת יצירת השפה: יצירת מהדר, העברת הקוד שהוצא למספר פלטפורמות וארכיטקטורות, יצירת אופטימיזציות ספציפיות לאדריכלות כגון וקטורציה וכתיבת קוד לטיפול במטפורות שפה נפוצות כמו חריגים. משמעות הרישוי הליברלי שלה היא שניתן לעשות בו שימוש חופשי כרכיב תוכנה או לפרוס אותו כשירות.

לסגל השפות המשתמשות ב- LLVM יש שמות מוכרים רבים. השפה המהירה של אפל משתמשת ב- LLVM כמסגרת המהדר שלה, ורוסט משתמשת ב- LLVM כמרכיב מרכזי בשרשרת הכלים שלה. כמו כן, למהדרים רבים יש מהדורת LLVM, כגון Clang, מהדר C / C ++ (זה השם, "C-lang"), עצמו פרויקט שקשור בקשר הדוק עם LLVM. למונו, הטמעת .NET, יש אפשרות לקמפל לקוד מקורי באמצעות LLVM אחורי. וקוטלין, בשפת JVM באופן סמלי, מפתח גרסה של השפה הנקראת Kotlin Native המשתמשת ב- LLVM לצורך הידור לקוד מקורי.

מוגדר LLVM

בבסיסה, LLVM היא ספרייה ליצירת תכנות קוד מקורי למכונה. מפתח משתמש ב- API כדי ליצור הוראות בפורמט הנקרא ייצוג ביניים , או IR. לאחר מכן, LLVM יכול לאסוף את ה- IR לבינארי עצמאי או לבצע אוסף JIT (בדיוק בזמן) על הקוד להפעלה בהקשר של תוכנית אחרת, כגון מתורגמן או זמן ריצה לשפה.

ממשקי ה- API של LLVM מספקים פרימיטיבים לפיתוח מבנים ודפוסים נפוצים רבים הנמצאים בשפות תכנות. לדוגמא, כמעט בכל שפה יש מושג של פונקציה ושל משתנה גלובלי, ולרבים יש קורוטינים וממשקי פונקציה זרה C. ל- LLVM פונקציות ומשתנים גלובליים כאלמנטים סטנדרטיים ב- IR שלו, ויש לו מטפורות ליצירת קורוטינים והתממשקות לספריות C.

במקום לבזבז זמן ואנרגיה על המצאת הגלגלים המסוימים האלה, אתה יכול פשוט להשתמש ביישומים של LLVM ולהתמקד בחלקים של השפה שלך שזקוקים לתשומת לב.

קרא עוד על Go, Kotlin, Python, and Rust 

ללכת:

  • הקש על העוצמה של שפת ה- Go של גוגל
  • IDEs ועורכי השפה הטובים ביותר

קוטלין:

  • מה זה קוטלין? חלופת Java הסבירה
  • מסגרות קוטלין: סקר של כלי פיתוח JVM

פִּיתוֹן:

  • מה זה פיתון? כל מה שאתה צריך לדעת
  • הדרכה: כיצד להתחיל עם Python
  • 6 ספריות חיוניות לכל מפתח פיתון

חֲלוּדָה:

  • מה זה חלודה? הדרך לפיתוח תוכנה בטוח, מהיר וקל
  • למד כיצד להתחיל עם חלודה 

LLVM: מיועד לניידות

כדי להבין את LLVM, זה עשוי לעזור לשקול אנלוגיה לשפת התכנות C: לפעמים C מתוארת כשפת הרכבה ניידת ברמה גבוהה, מכיוון שיש לה קונסטרוקציות שיכולות להתמקד מקרוב לחומרת המערכת, והיא הועברה כמעט כל ארכיטקטורת מערכת. אבל C שימושי כשפת הרכבה ניידת רק עד נקודה; הוא לא תוכנן למטרה מסוימת זו.

לעומת זאת, ה- IR של LLVM תוכנן מההתחלה להיות מכלול נייד. אחת הדרכים בה היא משיגה ניידות זו היא על ידי הצעת פרימיטיבים שאינם תלויים בכל ארכיטקטורת מכונות מסוימת. לדוגמה, סוגי מספרים שלמים אינם מוגבלים לרוחב הסיביות המרבי של החומרה הבסיסית (כגון 32 או 64 סיביות). ניתן ליצור סוגי מספרים שלמים פרימיטיביים תוך שימוש בכמה ביטים לפי הצורך, כמו מספר שלם של 128 סיביות. אתה גם לא צריך לדאוג לפלט יצירה שתתאים לערכת ההוראות של מעבד ספציפי; LLVM דואג לזה גם בשבילך.

העיצוב הניטרלי של ארכיטקטורה של LLVM מקל על תמיכה בחומרה מכל הסוגים, בהווה ובעתיד. לדוגמה, IBM תרמה לאחרונה קוד לתמיכה ב- z / OS שלה, Linux on Power (כולל תמיכה בספריית הווקטורציה של MASS של IBM), וארכיטקטורות AIX לפרויקטים C, C ++ ו- Fortran של LLVM. 

אם אתה רוצה לראות דוגמאות חיות של LLVM IR, עבור אל אתר ELLCC Project ונסה את ההדגמה החיה הממירה קוד C ל- LLVM IR ישירות בדפדפן.

כיצד שפות תכנות משתמשות ב- LLVM

מקרה השימוש הנפוץ ביותר ב- LLVM הוא כמהדר מבעוד מועד (AOT) לשפה. לדוגמא, פרויקט Clang מבעוד מועד מרכיב את C ו- C ++ לבינאריות מקומיות. אבל LLVM מאפשר גם דברים אחרים.

הידור בדיוק בזמן עם LLVM

במצבים מסוימים נדרש ליצור קוד בזמן העבודה בזמן הריצה, ולא להיערך מבעוד מועד. השפה של ג'וליה, למשל, JIT אוספת את הקוד שלה, מכיוון שהיא צריכה לרוץ במהירות ולקיים אינטראקציה עם המשתמש באמצעות REPL (קריאה-הערכה-הדפסת לולאה) או הנחיה אינטראקטיבית. 

נומבה, חבילת תאוצה למתמטיקה עבור פייתון, JIT אוספת פונקציות פייתון נבחרות לקוד המכונה. זה יכול גם לאסוף קוד מעוטר בנומבה מבעוד מועד, אך (כמו ג'וליה) פייתון מציע פיתוח מהיר על ידי היותו שפה מפורשת. שימוש באוסף JIT להפקת קוד כזה משלים את זרימת העבודה האינטראקטיבית של פייתון טוב יותר מאשר אוסף מבעוד מועד.

אחרים מתנסים בדרכים חדשות להשתמש ב- LLVM כ- JIT, כגון קומפילציה של שאילתות PostgreSQL, ומניבות עד פי חמישה ביצועים.

אופטימיזציה קוד אוטומטית עם LLVM

LLVM לא רק מהדר את ה- IR לקוד מכונה מקורי. ניתן גם לכוון אותו באופן פרוגרמטי כדי לייעל את הקוד ברמת פירוט גבוהה, לאורך כל תהליך הקישור. האופטימיזציות יכולות להיות אגרסיביות למדי, כולל דברים כמו שיבוץ פונקציות, ביטול קוד מת (כולל הצהרות סוג שאינן בשימוש וטיעוני פונקציות) וגלילת לולאות.

שוב, הכוח הוא בכך שלא תצטרך ליישם את כל זה בעצמך. LLVM יכול לטפל בהם בשבילך, או שאתה יכול לכוון אותו כדי להעביר אותם לפי הצורך. לדוגמא, אם ברצונך לקבל קבצים בינאריים קטנים יותר במחיר של ביצועים מסוימים, ייתכן שממשק הקומפיילר שלך יגיד ל- LLVM להשבית את גלילת הלולאה.

שפות ספציפיות לתחום עם LLVM

LLVM שימש לייצור מהדרים עבור שפות רבות לשימוש כללי, אך הוא שימושי גם להפקת שפות אנכיות מאוד או בלעדיות לתחום בעייתי. במובנים מסוימים, זה המקום בו LLVM זורח בצורה הבהירה ביותר, משום שהוא מסיר את המלאכה ביצירת שפה כזו וגורם לה לבצע ביצועים טובים.

פרויקט Emscripten, למשל, לוקח את קוד ה- IR LLVM וממיר אותו ל- JavaScript, בתיאוריה ומאפשר לכל שפה עם LLVM אחורי קוד לייצוא שיכול לרוץ בדפדפן. התוכנית לטווח הארוך היא להחזיק קצוות אחוריים מבוססי LLVM שיכולים לייצר WebAssemble, אך Emscripten היא דוגמה טובה לאופן שבו LLVM יכול להיות גמיש.

דרך נוספת שבה ניתן להשתמש ב- LLVM היא להוסיף סיומות ספציפיות לתחום לשפה קיימת. Nvidia השתמשה ב- LLVM כדי ליצור את Nvidia CUDA Compiler, המאפשר לשפות להוסיף תמיכה מקורית ל- CUDA שמתאגדת כחלק מהקוד המקורי שאתה מייצר (מהר יותר), במקום להיות מופעל באמצעות ספרייה שנשלחה איתו (לאט יותר).

ההצלחה של LLVM עם שפות ספציפיות לתחום דרבנה פרויקטים חדשים בתוך LLVM לטיפול בבעיות שהם יוצרים. הבעיה הגדולה ביותר היא כמה שקשה לתרגם כמה DSLs ל- LLVM IR בלי הרבה עבודה קשה בחזית. אחד הפתרונות בעבודה הוא ייצוג ביניים רב-רמות, או פרויקט MLIR.

MLIR מספק דרכים נוחות לייצג מבנים ופעולות נתונים מורכבים, אשר לאחר מכן ניתן לתרגם אוטומטית ל- LLVM IR. לדוגמה, מסגרת הלמידה המכונה של TensorFlow עשויה לכלול רבות מפעולות גרף זרימת הנתונים המורכבות שלה ביעילות לקוד מקורי עם MLIR.

עבודה עם LLVM בשפות שונות

הדרך האופיינית לעבוד עם LLVM היא באמצעות קוד בשפה בה אתה מרגיש נוח (ויש לה כמובן תמיכה בספריות LLVM).

שתי אפשרויות שפה נפוצות הן C ו- C ++. מפתחי LLVM רבים מחדירים את אחד משני אלה מכמה סיבות טובות: 

  • LLVM עצמו כתוב ב- C ++.
  • ממשקי ה- API של LLVM זמינים בגלגולים של C ו- C ++.
  • התפתחות שפה רבה נוטה לקרות עם C / C ++ כבסיס

ובכל זאת, שתי השפות הללו אינן הבחירות היחידות. שפות רבות יכולות להתקשר לספריות C באופן מקורי, כך שתאורטית ניתן לבצע פיתוח LLVM בכל שפה כזו. אבל זה עוזר שיש ספרייה ממשית בשפה שעוטפת באלגנטיות את ה- APIs של LLVM. למרבה המזל, בשפות רבות ובזמן הפעלה של שפות יש ספריות כאלה, כולל C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go ו- Python.

אזהרה אחת היא שחלק מכריכות השפה ל- LLVM עשויות להיות פחות מלאות מאחרות. עם Python, למשל, יש אפשרויות רבות, אך כל אחת מהן משתנה בשלמותה ובשימושיותה:

  • llvmlite, שפותח על ידי הצוות שיוצר את נומבה, התגלה כמתמודד הנוכחי לעבודה עם LLVM בפייתון. הוא מיישם רק קבוצת משנה של הפונקציונליות של LLVM, כפי שמכתיב את הצרכים של פרויקט נומבה. אך קבוצת משנה זו מספקת את הרוב המכריע של מה שמשתמשי LLVM זקוקים להם. (בדרך כלל llvmlite היא הבחירה הטובה ביותר לעבודה עם LLVM בפייתון.)
  • פרויקט LLVM מקיים מערך כריכות משלו ל- API של LLVM, אך כרגע הם אינם מתוחזקים.
  • llvmpy, מחייב הפיתון הפופולרי הראשון ל- LLVM, נפל מהתחזוקה בשנת 2015. גרוע מכל פרויקט תוכנה, אך גרוע יותר בעבודה עם LLVM, בהתחשב במספר השינויים שמתרחשים בכל מהדורה של LLVM.
  • מטרת llvmcpy היא לעדכן את כריכות ה- Python לספריית C, לעדכן אותן באופן אוטומטי ולהנגיש אותן באמצעות הניבים המקוריים של Python. llvmcpy עדיין נמצא בשלבים הראשונים, אך כבר יכול לעשות עבודה ראשונית עם ממשקי ה- API של LLVM.

אם אתה סקרן כיצד להשתמש בספריות LLVM לבניית שפה, ליוצרים של LLVM יש הדרכה, המשתמשת ב- C ++ או OCAML, המסייעת אותך ליצור שפה פשוטה הנקראת קליידוסקופ. מאז הועבר לשפות אחרות:

  • Haskell:  יציאה ישירה של ההדרכה המקורית.
  • פייתון: יציאה כזו עוקבת מקרוב אחר ההדרכה, ואילו השנייה היא שכתוב שאפתני יותר עם שורת פקודה אינטראקטיבית. שני אלה משתמשים ב- llvmlite כקישורים ל- LLVM.
  • Rust  ו  סוויפט: נראה שאין מנוס היינו מקבלים יציאות של הדרכה לשתי השפות שבהן LLVM עזרו להביא לידי קיום.

לבסוף, המדריך זמין גם  בשפות אנושיות . זה תורגם לסינית, באמצעות C ++ המקורי ו- Python.

מה LLVM לא עושה

עם כל מה ש- LLVM מספק, כדאי לדעת גם מה הוא לא עושה.

לדוגמה, LLVM אינו מנתח את דקדוק השפה. כלים רבים כבר עושים את העבודה הזו, כמו lex / yacc, flex / bison, Lark ו- ANTLR. ניתוח אמור להיות מנותק מהידור בכל מקרה, ולכן זה לא מפתיע LLVM לא מנסה לטפל בכל זה.

LLVM גם לא מתייחס ישירות לתרבות התוכנה הגדולה יותר סביב שפה נתונה. התקנת הבינאריות של המהדר, ניהול חבילות בהתקנה ושדרוג שרשרת הכלים - עליך לעשות זאת בעצמך.

לבסוף, והכי חשוב, ישנם עדיין חלקים נפוצים בשפות ש- LLVM אינו מספק פרימיטיביים עבורם. בשפות רבות יש איזושהי ניהול זיכרון שנאסף, כדרך העיקרית לניהול הזיכרון או כתוספת לאסטרטגיות כמו RAII (בהן C ++ ו- Rust משתמשות). LLVM אינו נותן לך מנגנון לאיסוף אשפה, אך הוא מספק כלים ליישום איסוף האשפה בכך שהוא מאפשר לסמן קוד במטא נתונים שמקל על כתיבת אספני האשפה.

אולם כל זה אינו שולל את האפשרות כי LLVM עשוי בסופו של דבר להוסיף מנגנונים מקוריים ליישום איסוף אשפה. LLVM מתפתח במהירות, עם שחרור משמעותי כל חצי שנה בערך. וקצב ההתפתחות עשוי להתגבר רק בזכות האופן שבו שפות רבות כיום מציבות את LLVM בלב תהליך הפיתוח שלהן.