Προγραμματισμός

Τι είναι το LLVM; Η δύναμη πίσω από τα Swift, Rust, Clang και άλλα

Νέες γλώσσες και βελτιώσεις σε υπάρχουσες, ξεφυτρώνουν σε όλο το αναπτυσσόμενο τοπίο. Το Mozilla's Rust, το Apple Swift, το Jetbrains's Kotlin και πολλές άλλες γλώσσες παρέχουν στους προγραμματιστές μια νέα γκάμα επιλογών για ταχύτητα, ασφάλεια, ευκολία, φορητότητα και ισχύ.

Γιατί τώρα? Ένας μεγάλος λόγος είναι τα νέα εργαλεία για τη δημιουργία γλωσσών - συγκεκριμένα, των μεταγλωττιστών. Και επικεφαλής μεταξύ τους είναι το LLVM, ένα έργο ανοιχτού κώδικα που αναπτύχθηκε αρχικά από τον δημιουργό γλωσσών Swift, Chris Lattner ως ερευνητικό έργο στο Πανεπιστήμιο του Ιλινόις.

Το LLVM διευκολύνει όχι μόνο τη δημιουργία νέων γλωσσών, αλλά και την ενίσχυση της ανάπτυξης των υπαρχόντων. Παρέχει εργαλεία για την αυτοματοποίηση πολλών από τα πιο άχαρα μέρη του έργου της δημιουργίας γλωσσών: δημιουργία ενός μεταγλωττιστή, μεταφορά του εξερχόμενου κώδικα σε πολλαπλές πλατφόρμες και αρχιτεκτονικές, δημιουργία βελτιστοποιήσεων για συγκεκριμένη αρχιτεκτονική όπως διανυσματοποίηση και σύνταξη κώδικα για τον χειρισμό κοινών γλωσσικών μεταφορών όπως εξαιρέσεις. Η ελεύθερη αδειοδότηση σημαίνει ότι μπορεί να επαναχρησιμοποιηθεί ελεύθερα ως στοιχείο λογισμικού ή να χρησιμοποιηθεί ως υπηρεσία.

Ο κατάλογος των γλωσσών που χρησιμοποιούν το LLVM έχει πολλά γνωστά ονόματα. Η γλώσσα Swift της Apple χρησιμοποιεί το LLVM ως πλαίσιο μεταγλώττισης και το Rust χρησιμοποιεί το LLVM ως βασικό στοιχείο της αλυσίδας εργαλείων της. Επίσης, πολλοί μεταγλωττιστές έχουν μια έκδοση LLVM, όπως το Clang, το μεταγλωττιστή C / C ++ (αυτό το όνομα, "C-lang"), το ίδιο ένα έργο που συνδέεται στενά με το LLVM. Το Mono, η εφαρμογή .NET, έχει την επιλογή να μεταγλωττίσει τον εγγενή κώδικα χρησιμοποιώντας ένα LLVM back end. Και ο Kotlin, ονομαστικά μια γλώσσα JVM, αναπτύσσει μια έκδοση της γλώσσας που ονομάζεται Kotlin Native που χρησιμοποιεί το LLVM για να μεταγλωττίσει τον μητρικό κώδικα.

Ορίζεται LLVM

Στην καρδιά του, το LLVM είναι μια βιβλιοθήκη για τη δημιουργία προγραμματισμένου κώδικα μηχανικού. Ένας προγραμματιστής χρησιμοποιεί το API για να δημιουργήσει οδηγίες σε μορφή που ονομάζεται ενδιάμεση εκπροσώπησηή IR. Στη συνέχεια, το LLVM μπορεί να μεταγλωττίσει το IR σε αυτόνομο δυαδικό ή να εκτελέσει μια συλλογή JIT (just-in-time) στον κώδικα που θα εκτελεστεί στο πλαίσιο ενός άλλου προγράμματος, όπως ένας διερμηνέας ή χρόνος εκτέλεσης για τη γλώσσα.

Τα API του LLVM παρέχουν πρωταρχικά στοιχεία για την ανάπτυξη πολλών κοινών δομών και προτύπων που βρίσκονται σε γλώσσες προγραμματισμού. Για παράδειγμα, σχεδόν κάθε γλώσσα έχει την έννοια μιας συνάρτησης και μιας παγκόσμιας μεταβλητής, και πολλές έχουν κορουτίνες και διασυνδέσεις ξένης λειτουργίας C. Το LLVM έχει συναρτήσεις και καθολικές μεταβλητές ως τυπικά στοιχεία στο IR του και έχει μεταφορές για τη δημιουργία κορουτίνων και τη διασύνδεση με βιβλιοθήκες C.

Αντί να ξοδεύετε χρόνο και ενέργεια ανακαλύπτοντας εκ νέου τους συγκεκριμένους τροχούς, μπορείτε απλώς να χρησιμοποιήσετε τις υλοποιήσεις του LLVM και να εστιάσετε στα μέρη της γλώσσας σας που χρειάζονται την προσοχή.

Διαβάστε περισσότερα για Go, Kotlin, Python και Rust

Πηγαίνω:

  • Πατήστε τη δύναμη της γλώσσας Go της Google
  • Τα καλύτερα IDE και συντάκτες γλώσσας Go

Κότλιν:

  • Τι είναι το Kotlin; Η εναλλακτική λύση Java εξήγησε
  • Πλαίσια Kotlin: Μια έρευνα για εργαλεία ανάπτυξης JVM

Πύθων:

  • Τι είναι το Python; Όλα όσα πρέπει να γνωρίζετε
  • Tutorial: Πώς να ξεκινήσετε με το Python
  • 6 βασικές βιβλιοθήκες για κάθε προγραμματιστή Python

Σκουριά:

  • Τι είναι η σκουριά; Ο τρόπος να κάνετε ασφαλή, γρήγορη και εύκολη ανάπτυξη λογισμικού
  • Μάθετε πώς να ξεκινήσετε με το Rust

LLVM: Σχεδιασμένο για φορητότητα

Για να κατανοήσετε το LLVM, μπορεί να σας βοηθήσει να εξετάσετε μια αναλογία με τη γλώσσα προγραμματισμού C: Το C μερικές φορές περιγράφεται ως φορητή γλώσσα συναρμολόγησης υψηλού επιπέδου, επειδή έχει κατασκευές που μπορούν να αντιστοιχούν στενά στο υλικό του συστήματος και έχει μεταφερθεί σχεδόν κάθε αρχιτεκτονική συστήματος. Αλλά το C είναι χρήσιμο ως φορητή γλώσσα συναρμολόγησης μόνο μέχρι ένα σημείο. δεν είχε σχεδιαστεί για τον συγκεκριμένο σκοπό.

Αντίθετα, το IR LLVM σχεδιάστηκε από την αρχή ως φορητό συγκρότημα. Ένας τρόπος για να επιτύχει αυτή τη φορητότητα είναι να προσφέρει πρωτόγονα ανεξάρτητα από οποιαδήποτε συγκεκριμένη αρχιτεκτονική μηχανών. Για παράδειγμα, οι ακέραιοι τύποι δεν περιορίζονται στο μέγιστο πλάτος bit του υποκείμενου υλικού (όπως 32 ή 64 bit). Μπορείτε να δημιουργήσετε πρωτόγονους ακέραιους τύπους χρησιμοποιώντας όσα bit χρειάζεστε, όπως έναν ακέραιο αριθμό 128 bit. Επίσης, δεν χρειάζεται να ανησυχείτε για τη δημιουργία εξόδου ώστε να ταιριάζει με το σύνολο εντολών ενός συγκεκριμένου επεξεργαστή. Το LLVM το φροντίζει και για εσάς.

Ο ουδέτερος σχεδιασμός της αρχιτεκτονικής της LLVM διευκολύνει την υποστήριξη υλικού κάθε είδους, του παρόντος και του μέλλοντος. Για παράδειγμα, η IBM συνέβαλε πρόσφατα κώδικα για την υποστήριξη του z / OS, του Linux on Power (συμπεριλαμβανομένης της υποστήριξης για τη βιβλιοθήκη MASS της IBM vectorization) και των αρχιτεκτονικών AIX για έργα LLVM C, C ++ και Fortran.

Αν θέλετε να δείτε ζωντανά παραδείγματα LLVM IR, μεταβείτε στον ιστότοπο του έργου ELLCC και δοκιμάστε τη ζωντανή επίδειξη που μετατρέπει τον κώδικα C σε LLVM IR απευθείας στο πρόγραμμα περιήγησης.

Πώς χρησιμοποιεί τις γλώσσες προγραμματισμού LLVM

Η πιο συνηθισμένη περίπτωση χρήσης για LLVM είναι ως μεταγλωττιστής εκ των προτέρων (AOT) για μια γλώσσα. Για παράδειγμα, το έργο Clang εκ των προτέρων συγκεντρώνει C και C ++ σε εγγενή δυαδικά αρχεία. Αλλά το LLVM καθιστά δυνατή και άλλα πράγματα.

Σύνταξη just-in-time με LLVM

Ορισμένες καταστάσεις απαιτούν τη δημιουργία κώδικα κατά τη διάρκεια της εκτέλεσης, αντί να μεταγλωττίζονται εκ των προτέρων. Η γλώσσα της Τζούλια, για παράδειγμα, το JIT μεταγλωττίζει τον κώδικά της, επειδή πρέπει να τρέχει γρήγορα και να αλληλεπιδρά με τον χρήστη μέσω REPL (read-eval-print loop) ή διαδραστικής προτροπής.

Το Numba, ένα πακέτο μαθηματικής επιτάχυνσης για Python, JIT-μεταγλωττίζει επιλεγμένες λειτουργίες Python σε κώδικα μηχανής. Μπορεί επίσης να καταρτίσει κώδικα διακοσμημένο με Numba εκ των προτέρων, αλλά (όπως η Julia) η Python προσφέρει ταχεία ανάπτυξη με την ερμηνεία μιας γλώσσας. Η χρήση της σύνταξης JIT για την παραγωγή τέτοιου κώδικα συμπληρώνει τη διαδραστική ροή εργασίας της Python καλύτερα από την εκ των προτέρων συλλογή.

Άλλοι πειραματίζονται με νέους τρόπους χρήσης του LLVM ως JIT, όπως η σύνταξη ερωτημάτων PostgreSQL, με αποτέλεσμα έως και πενταπλάσια αύξηση της απόδοσης.

Αυτόματη βελτιστοποίηση κώδικα με LLVM

Το LLVM δεν μεταγλωττίζει απλώς το IR στον εγγενή κώδικα του μηχανήματος. Μπορείτε επίσης να τον κατευθύνετε μέσω προγραμματισμού για να βελτιστοποιήσετε τον κώδικα με υψηλό βαθμό ευκρίνειας, σε όλη τη διαδικασία σύνδεσης. Οι βελτιστοποιήσεις μπορεί να είναι αρκετά επιθετικές, όπως πράγματα όπως η ενσωμάτωση συναρτήσεων, η εξάλειψη του νεκρού κώδικα (συμπεριλαμβανομένων των μη χρησιμοποιημένων δηλώσεων τύπου και ορίσματα λειτουργιών) και η κατάργηση των βρόχων

Και πάλι, η δύναμη είναι να μην χρειάζεται να τα εφαρμόσετε μόνοι σας. Το LLVM μπορεί να τα χειριστεί για εσάς ή μπορείτε να το κατευθύνετε για να τα απενεργοποιήσετε όπως απαιτείται. Για παράδειγμα, εάν θέλετε μικρότερα δυαδικά αρχεία με κόστος κάποια απόδοση, θα μπορούσατε να ζητήσετε από τη διεπαφή του μεταγλωττιστή να πει στο LLVM να απενεργοποιήσει την αποσυμπίεση του βρόχου.

Γλώσσες για συγκεκριμένο τομέα με LLVM

Το LLVM έχει χρησιμοποιηθεί για την παραγωγή μεταγλωττιστών για πολλές γλώσσες γενικού σκοπού, αλλά είναι επίσης χρήσιμο για την παραγωγή γλωσσών που είναι πολύ κάθετες ή αποκλειστικές σε έναν προβληματικό τομέα. Με κάποιους τρόπους, αυτό είναι το σημείο όπου το LLVM λάμπει πιο φωτεινά, γιατί αφαιρεί πολλά από τη δουλειά στη δημιουργία μιας τέτοιας γλώσσας και την κάνει να λειτουργεί καλά.

Το έργο Emscripten, για παράδειγμα, παίρνει τον κωδικό IR LLVM και τον μετατρέπει σε JavaScript, θεωρητικά επιτρέποντας σε οποιαδήποτε γλώσσα με back-end LLVM να εξάγει κώδικα που μπορεί να εκτελεστεί στο πρόγραμμα περιήγησης. Το μακροπρόθεσμο σχέδιο είναι να έχει LLVM back-end που μπορούν να παράγουν WebAssembly, αλλά το Emscripten είναι ένα καλό παράδειγμα για το πόσο ευέλικτο μπορεί να είναι το LLVM.

Ένας άλλος τρόπος με τον οποίο μπορεί να χρησιμοποιηθεί το LLVM είναι να προσθέσετε επεκτάσεις για συγκεκριμένο τομέα σε μια υπάρχουσα γλώσσα. Η Nvidia χρησιμοποίησε το LLVM για να δημιουργήσει το Nvidia CUDA Compiler, το οποίο επιτρέπει στις γλώσσες να προσθέσουν εγγενή υποστήριξη για το CUDA που μεταγλωττίζεται ως μέρος του εγγενούς κώδικα που δημιουργείτε (γρηγορότερα), αντί να καλείται μέσω μιας βιβλιοθήκης που αποστέλλεται μαζί του (πιο αργή).

Η επιτυχία του LLVM με γλώσσες για συγκεκριμένους τομείς έχει δώσει ώθηση σε νέα έργα στο πλαίσιο του LLVM για την αντιμετώπιση των προβλημάτων που δημιουργούν. Το μεγαλύτερο ζήτημα είναι πώς ορισμένα DSL είναι δύσκολο να μεταφραστούν σε LLVM IR χωρίς πολλή σκληρή δουλειά στο μπροστινό μέρος. Μία λύση στα έργα είναι το έργο Multi-Level Intermediate Representation ή MLIR.

Το MLIR παρέχει βολικούς τρόπους για την αναπαράσταση σύνθετων δομών και λειτουργιών δεδομένων, οι οποίοι στη συνέχεια μπορούν να μεταφραστούν αυτόματα σε IR LLVM. Για παράδειγμα, το πλαίσιο μηχανικής εκμάθησης TensorFlow θα μπορούσε να έχει πολλές από τις σύνθετες λειτουργίες γραφημάτων ροής δεδομένων που έχουν μεταγλωττιστεί αποτελεσματικά σε εγγενή κώδικα με MLIR.

Εργασία με το LLVM σε διάφορες γλώσσες

Ο τυπικός τρόπος για να εργαστείτε με το LLVM είναι μέσω κώδικα σε μια γλώσσα με την οποία είστε άνετα (και που υποστηρίζει φυσικά τις βιβλιοθήκες του LLVM).

Δύο κοινές επιλογές γλώσσας είναι C και C ++. Πολλοί προγραμματιστές LLVM έχουν προεπιλογή σε έναν από αυτούς τους δύο για πολλούς λόγους:

  • Το ίδιο το LLVM είναι γραμμένο σε C ++.
  • Τα API του LLVM διατίθενται σε ενσαρκώσεις C και C ++.
  • Μεγάλη ανάπτυξη γλωσσών τείνει να συμβαίνει με το C / C ++ ως βάση

Ωστόσο, αυτές οι δύο γλώσσες δεν είναι οι μόνες επιλογές. Πολλές γλώσσες μπορούν να καλέσουν εγγενώς σε βιβλιοθήκες Γ, οπότε θεωρητικά είναι δυνατόν να εκτελέσετε ανάπτυξη LLVM με οποιαδήποτε τέτοια γλώσσα. Αλλά βοηθά να έχουμε μια πραγματική βιβλιοθήκη στη γλώσσα που τυλίγει κομψά τα API του LLVM. Ευτυχώς, πολλές γλώσσες και ώρες εκτέλεσης γλωσσών έχουν τέτοιες βιβλιοθήκες, όπως C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go και Python.

Μια προειδοποίηση είναι ότι ορισμένες από τις δεσμεύσεις γλώσσας με το LLVM μπορεί να είναι λιγότερο ολοκληρωμένες από άλλες. Με την Python, για παράδειγμα, υπάρχουν πολλές επιλογές, αλλά καθεμία διαφέρει στην πληρότητα και τη χρησιμότητά της:

  • Το llvmlite, που αναπτύχθηκε από την ομάδα που δημιουργεί το Numba, έχει αναδειχθεί ως ο τρέχων υποψήφιος για συνεργασία με την LLVM στο Python. Υλοποιεί μόνο ένα υποσύνολο της λειτουργικότητας του LLVM, όπως υπαγορεύεται από τις ανάγκες του έργου Numba. Αλλά αυτό το υποσύνολο παρέχει τη συντριπτική πλειοψηφία αυτών που χρειάζονται οι χρήστες LLVM. (Το llvmlite είναι γενικά η καλύτερη επιλογή για συνεργασία με το LLVM στο Python.)
  • Το έργο LLVM διατηρεί το δικό του σύνολο συνδέσεων με το C API του LLVM, αλλά προς το παρόν δεν διατηρούνται.
  • Η llvmpy, η πρώτη δημοφιλής δέσμευση Python για το LLVM, έχασε τη συντήρηση το 2015. Κακό για οποιοδήποτε έργο λογισμικού, αλλά χειρότερα όταν συνεργαζόμαστε με το LLVM, δεδομένου του αριθμού των αλλαγών που έρχονται σε κάθε έκδοση του LLVM.
  • Το llvmcpy στοχεύει να ενημερώσει τις συνδέσεις Python για τη βιβλιοθήκη C, να τις ενημερώσει με αυτοματοποιημένο τρόπο και να τις κάνει προσβάσιμες χρησιμοποιώντας τα εγγενή ιδιώματα της Python. Το llvmcpy βρίσκεται ακόμη στα αρχικά στάδια, αλλά ήδη μπορεί να κάνει κάποια στοιχειώδη εργασία με τα API LLVM.

Αν είστε περίεργοι για το πώς να χρησιμοποιήσετε τις βιβλιοθήκες LLVM για να δημιουργήσετε μια γλώσσα, οι δημιουργοί του LLVM έχουν ένα σεμινάριο, χρησιμοποιώντας είτε C ++ είτε OCAML, που σας καθοδηγεί στη δημιουργία μιας απλής γλώσσας που ονομάζεται Kaleidoscope. Έκτοτε μεταφέρθηκε σε άλλες γλώσσες:

  • Χάσκελ:Μια απευθείας θύρα του αρχικού σεμιναρίου.
  • Πύθων: Μια τέτοια θύρα παρακολουθεί στενά το σεμινάριο, ενώ η άλλη είναι μια πιο φιλόδοξη επανεγγραφή με μια διαδραστική γραμμή εντολών. Και οι δύο χρησιμοποιούν το llvmlite ως συνδέσεις στο LLVM.
  • ΣκουριάκαιΤαχύς: Φαινόταν αναπόφευκτο να πάρουμε θύρες του σεμιναρίου σε δύο από τις γλώσσες που βοήθησε να δημιουργήσει το LLVM.

Τέλος, το σεμινάριο είναι επίσης διαθέσιμο σεο άνθρωπος Γλώσσες. Έχει μεταφραστεί στα κινέζικα, χρησιμοποιώντας τα πρωτότυπα C ++ και Python.

Τι δεν κάνει το LLVM

Με όλα όσα παρέχει το LLVM, είναι χρήσιμο να γνωρίζετε επίσης τι δεν κάνει.

Για παράδειγμα, το LLVM δεν αναλύει τη γραμματική μιας γλώσσας. Πολλά εργαλεία κάνουν ήδη αυτή τη δουλειά, όπως lex / yacc, flex / bison, Lark και ANTLR. Η ανάλυση πρέπει να αποσυνδεθεί από τη συλλογή ούτως ή άλλως, οπότε δεν αποτελεί έκπληξη το γεγονός ότι το LLVM δεν προσπαθεί να αντιμετωπίσει κανένα από αυτά.

Το LLVM επίσης δεν ασχολείται άμεσα με τη μεγαλύτερη κουλτούρα λογισμικού γύρω από μια δεδομένη γλώσσα. Εγκατάσταση των δυαδικών αρχείων του μεταγλωττιστή, διαχείριση πακέτων σε μια εγκατάσταση και αναβάθμιση της αλυσίδας εργαλείων - πρέπει να το κάνετε μόνοι σας.

Τέλος, και το πιο σημαντικό, εξακολουθούν να υπάρχουν κοινά μέρη γλωσσών για τα οποία το LLVM δεν παρέχει πρωτόγονα. Πολλές γλώσσες έχουν κάποιο τρόπο διαχείρισης μνήμης που συλλέγεται από σκουπίδια, είτε ως τον κύριο τρόπο διαχείρισης της μνήμης είτε ως συμπλήρωμα σε στρατηγικές όπως το RAII (που χρησιμοποιούν C ++ και Rust). Το LLVM δεν σας παρέχει μηχανισμό συλλογής απορριμμάτων, αλλά παρέχει εργαλεία για την εφαρμογή της συλλογής απορριμμάτων επιτρέποντας την επισήμανση κώδικα με μεταδεδομένα που διευκολύνει τη συγγραφή συλλεκτών απορριμμάτων.

Κανένα από αυτά, ωστόσο, δεν αποκλείει το ενδεχόμενο η LLVM να προσθέσει τελικά εγγενείς μηχανισμούς για την εφαρμογή της συλλογής απορριμμάτων. Το LLVM αναπτύσσεται γρήγορα, με μια μεγάλη κυκλοφορία κάθε έξι μήνες περίπου. Και ο ρυθμός ανάπτυξης είναι πιθανό να αυξηθεί μόνο χάρη στον τρόπο με τον οποίο πολλές τρέχουσες γλώσσες έθεσαν το LLVM στο επίκεντρο της διαδικασίας ανάπτυξής τους.

$config[zx-auto] not found$config[zx-overlay] not found