LLVM nedir? Swift, Rust, Clang ve daha fazlasının arkasındaki güç

Yeni diller ve mevcut dillerdeki gelişmeler, gelişmekte olan manzara boyunca mantar gibi çoğalıyor. Mozilla's Rust, Apple's Swift, Jetbrains'in Kotlin'i ve diğer birçok dil, geliştiricilere hız, güvenlik, rahatlık, taşınabilirlik ve güç için yeni seçenekler sunar.

Neden şimdi? Bunun büyük bir nedeni, özellikle derleyiciler olmak üzere, dil oluşturmak için yeni araçlardır. Bunların başında Swift dil yaratıcısı Chris Lattner tarafından Illinois Üniversitesi'nde bir araştırma projesi olarak geliştirilen açık kaynaklı bir proje olan LLVM geliyor.

LLVM, yalnızca yeni diller yaratmayı değil, aynı zamanda mevcut olanların gelişimini de geliştirmeyi kolaylaştırır. Dil yaratma görevinin en nankör parçalarının çoğunu otomatikleştirmek için araçlar sağlar: bir derleyici oluşturmak, çıkarılan kodu birden çok platforma ve mimariye taşımak, vektörleştirme gibi mimariye özgü optimizasyonlar oluşturmak ve ortak dil metaforlarını işlemek için kod yazmak istisnalar. Serbest lisansı, bir yazılım bileşeni olarak özgürce yeniden kullanılabileceği veya bir hizmet olarak dağıtılabileceği anlamına gelir.

LLVM'yi kullanan dillerin listesi birçok tanıdık isme sahiptir. Apple'ın Swift dili derleyici çerçevesi olarak LLVM'yi kullanır ve Rust, LLVM'yi araç zincirinin temel bir bileşeni olarak kullanır. Ayrıca, birçok derleyicinin kendisi de LLVM ile yakından bağlantılı bir proje olan Clang, C / C ++ derleyicisi (bu ad, "C-lang") gibi bir LLVM sürümüne sahiptir. Mono, .NET uygulaması, bir LLVM arka ucu kullanarak yerel kodda derleme seçeneğine sahiptir. Ve sözde bir JVM dili olan Kotlin, makineye özgü kodu derlemek için LLVM kullanan Kotlin Native adlı bir dil sürümü geliştiriyor.

LLVM tanımlı

LLVM, özünde, makineye özgü kodların programlı olarak oluşturulması için bir kitaplıktır. Bir geliştirici, ara temsil veya IR adı verilen bir formatta talimatlar oluşturmak için API'yi kullanır . LLVM daha sonra IR'yi bağımsız bir ikili olarak derleyebilir veya dil için bir yorumlayıcı veya çalışma zamanı gibi başka bir program bağlamında çalıştırmak için kod üzerinde bir JIT (tam zamanında) derlemesi gerçekleştirebilir.

LLVM'nin API'leri, programlama dillerinde bulunan birçok ortak yapı ve kalıbı geliştirmek için ilkeller sağlar. Örneğin, hemen hemen her dil bir işlev ve bir küresel değişken kavramına sahiptir ve çoğunun eşdizimleri ve C yabancı işlev arabirimleri vardır. LLVM, IR'sinde standart öğeler olarak işlevlere ve global değişkenlere sahiptir ve koroutinler oluşturmak ve C kitaplıkları ile arayüz oluşturmak için metaforlara sahiptir.

Bu belirli tekerlekleri yeniden icat etmek için zaman ve enerji harcamak yerine, LLVM'nin uygulamalarını kullanabilir ve dilinizin ilgiye ihtiyaç duyan kısımlarına odaklanabilirsiniz.

Go, Kotlin, Python ve Rust hakkında daha fazla bilgi edinin 

Git:

  • Google'ın Go dilinin gücüne dokunun
  • En iyi Go dili IDE'leri ve editörleri

Kotlin:

  • Kotlin nedir? Java alternatifi açıklandı
  • Kotlin çerçeveleri: JVM geliştirme araçları araştırması

Python:

  • Python nedir? Bilmen gereken her şey
  • Eğitim: Python'a nasıl başlanır
  • Her Python geliştiricisi için 6 temel kitaplık

Pas, paslanma:

  • Rust nedir? Güvenli, hızlı ve kolay yazılım geliştirme yapmanın yolu
  • Rust ile nasıl başlayacağınızı öğrenin 

LLVM: Taşınabilirlik için tasarlandı

LLVM'yi anlamak için, C programlama diliyle bir benzetmeyi düşünmek yardımcı olabilir: C bazen taşınabilir, yüksek seviyeli bir montaj dili olarak tanımlanır, çünkü sistem donanımıyla yakından eşleşebilen yapılara sahiptir ve neredeyse her sistem mimarisi. Ancak C, taşınabilir bir montaj dili olarak yalnızca bir noktaya kadar kullanışlıdır; bu özel amaç için tasarlanmamıştı.

Buna karşılık, LLVM'nin IR'si başından itibaren taşınabilir bir montaj olarak tasarlandı. Bu taşınabilirliği sağlamanın bir yolu, belirli bir makine mimarisinden bağımsız ilkelleri sunmaktır. Örneğin, tamsayı türleri, temel alınan donanımın (32 veya 64 bit gibi) maksimum bit genişliğiyle sınırlı değildir. 128 bitlik bir tam sayı gibi, gerektiği kadar bit kullanarak ilkel tam sayı türleri oluşturabilirsiniz. Ayrıca, belirli bir işlemcinin yönerge setiyle eşleşmesi için çıktı oluşturma konusunda endişelenmenize gerek yok; LLVM bunu sizin için de halleder.

LLVM'nin mimariden bağımsız tasarımı, şimdiki ve gelecekteki her türden donanımı desteklemeyi kolaylaştırır. Örneğin, IBM kısa süre önce z / OS, Linux on Power (IBM'in MASS vektörleştirme kitaplığı desteği dahil) ve LLVM'nin C, C ++ ve Fortran projeleri için AIX mimarilerini desteklemek için koda katkıda bulundu. 

LLVM IR'nin canlı örneklerini görmek istiyorsanız, ELLCC Project web sitesine gidin ve tarayıcıda C kodunu LLVM IR'ye dönüştüren canlı demoyu deneyin.

Programlama dilleri LLVM'yi nasıl kullanır?

LLVM için en yaygın kullanım durumu, bir dil için zamanın ötesinde (AOT) derleyicidir. Örneğin, önceden Clang projesi C ve C ++ 'yı yerel ikili dosyalara derler. Ancak LLVM, başka şeyleri de mümkün kılar.

LLVM ile tam zamanında derleme

Bazı durumlarda kodun önceden derlenmek yerine çalışma zamanında anında üretilmesi gerekir. Julia dili, örneğin, JIT kodunu derler, çünkü hızlı çalışması ve bir REPL (oku-değerlendir-yazdır döngüsü) veya etkileşimli komut istemiyle kullanıcıyla etkileşime girmesi gerekir. 

Python için bir matematik hızlandırma paketi olan Numba, JIT kodu işlemek için seçilen Python işlevlerini derler. Ayrıca, Numba ile dekore edilmiş kodu önceden derleyebilir, ancak (Julia gibi) Python, yorumlanmış bir dil olarak hızlı gelişim sunar. Bu tür bir kod üretmek için JIT derlemesini kullanmak, Python'un etkileşimli iş akışını önceden derlemeden daha iyi tamamlar.

Diğerleri, LLVM'yi bir JIT olarak kullanmanın, PostgreSQL sorgularını derlemek gibi, performansta beş kata kadar artış sağlayan yeni yollarını deniyor.

LLVM ile otomatik kod optimizasyonu

LLVM yalnızca IR'yi yerel makine koduna derlemez. Ayrıca, bağlama işlemi boyunca kodu yüksek düzeyde ayrıntı düzeyinde optimize etmek için programlı olarak yönlendirebilirsiniz. Optimizasyonlar, işlevleri satır içi yapmak, ölü kodu ortadan kaldırmak (kullanılmayan tür bildirimleri ve işlev bağımsız değişkenleri dahil) ve döndürme döngüleri gibi şeyler dahil olmak üzere oldukça agresif olabilir.

Yine, güç tüm bunları kendi başınıza uygulamak zorunda kalmamakta. LLVM bunları sizin için halledebilir veya gerektiği gibi kapatması için onu yönlendirebilirsiniz. Örneğin, biraz performans pahasına daha küçük ikili dosyalar istiyorsanız, derleyicinizin ön ucunun LLVM'ye döngü açmayı devre dışı bırakmasını söylemesini sağlayabilirsiniz.

LLVM ile alana özgü diller

LLVM, birçok genel amaçlı dil için derleyiciler üretmek için kullanılmıştır, ancak aynı zamanda oldukça dikey veya sorunlu bir etki alanına özel diller üretmek için de kullanışlıdır. Bazı açılardan, LLVM'nin en parlak olduğu yer burasıdır, çünkü böyle bir dil yaratmanın birçok zahmetini ortadan kaldırır ve iyi performans göstermesini sağlar.

Örneğin Emscripten projesi, LLVM IR kodunu alır ve JavaScript'e dönüştürür, teorik olarak LLVM arka ucuna sahip herhangi bir dilin tarayıcıda çalışabilen kodu dışa aktarmasına izin verir. Uzun vadeli plan, WebAssembly üretebilen LLVM tabanlı arka uçlara sahip olmaktır, ancak Emscripten LLVM'nin ne kadar esnek olabileceğinin iyi bir örneğidir.

LLVM'nin kullanılmasının başka bir yolu, mevcut bir dile etki alanına özgü uzantılar eklemektir. Nvidia, Nvidia CUDA Derleyicisini oluşturmak için LLVM'yi kullandı; bu, dillerin, kendisiyle birlikte gönderilen (daha yavaş) bir kitaplık aracılığıyla çağrılmak yerine, ürettiğiniz yerel kodun bir parçası olarak derlenen (daha hızlı) CUDA için yerel destek eklemesine olanak tanır.

LLVM'nin alana özgü dillerdeki başarısı, LLVM'de yarattıkları sorunları ele almak için yeni projeleri teşvik etti. En büyük sorun, bazı DSL'lerin, ön uçta çok fazla çalışma yapılmadan LLVM IR'ye çevrilmesinin zor olmasıdır. Çalışmalardaki çözümlerden biri, Çok Düzeyli Ara Temsil veya MLIR projesidir.

MLIR, daha sonra otomatik olarak LLVM IR'ye çevrilebilen karmaşık veri yapılarını ve işlemlerini temsil etmek için uygun yollar sağlar. Örneğin, TensorFlow makine öğrenimi çerçevesi, karmaşık veri akışı grafik işlemlerinin çoğunu MLIR ile yerel koda verimli bir şekilde derlenmiş olabilir.

LLVM ile çeşitli dillerde çalışmak

LLVM ile çalışmanın tipik yolu, rahat olduğunuz bir dilde kod kullanmaktır (ve tabii ki LLVM kütüphaneleri için desteği vardır).

İki yaygın dil seçeneği C ve C ++ 'dır. Birçok LLVM geliştiricisi, birkaç iyi nedenden dolayı bu ikisinden birini varsayılan olarak kullanır: 

  • LLVM'nin kendisi C ++ ile yazılmıştır.
  • LLVM'nin API'leri C ve C ++ enkarnasyonlarında mevcuttur.
  • Çoğu dil gelişimi, temel olarak C / C ++ ile gerçekleşme eğilimindedir

Yine de bu iki dil tek seçenek değil. Birçok dil C kitaplıklarını yerel olarak çağırabilir, bu nedenle bu tür herhangi bir dille LLVM geliştirmeyi gerçekleştirmek teorik olarak mümkündür. Ancak, LLVM'nin API'lerini zarif bir şekilde saran dilde gerçek bir kitaplığa sahip olmanıza yardımcı olur. Neyse ki, C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go ve Python dahil olmak üzere birçok dil ve dil çalışma zamanı bu tür kitaplıklara sahiptir.

Bir uyarı, LLVM'ye bazı dil bağlarının diğerlerinden daha az eksiksiz olabileceğidir. Örneğin Python ile birçok seçenek vardır, ancak her biri eksiksizliği ve faydası açısından farklılık gösterir:

  • Numba'yı oluşturan ekip tarafından geliştirilen llvmlite, Python'da LLVM ile çalışmak için mevcut yarışmacı olarak ortaya çıktı. Numba projesinin ihtiyaçları tarafından dikte edildiği gibi, LLVM'nin işlevselliğinin yalnızca bir alt kümesini uygular. Ancak bu alt küme, LLVM kullanıcılarının ihtiyaç duyduğu şeylerin büyük çoğunluğunu sağlar. (llvmlite, Python'da LLVM ile çalışmak için genellikle en iyi seçimdir.)
  • LLVM projesi, LLVM'nin C API'sine kendi bağlama kümesini korur, ancak şu anda korunmamaktadır.
  • LLVM için ilk popüler Python bağlayıcılığı olan llvmpy, 2015 yılında bakımdan düştü. Herhangi bir yazılım projesi için kötü, ancak LLVM'nin her sürümünde ortaya çıkan değişiklik sayısı göz önüne alındığında LLVM ile çalışırken daha kötü.
  • llvmcpy, C kitaplığı için Python bağlamalarını güncellemeyi, bunları otomatik bir şekilde güncel tutmayı ve Python'un yerel deyimlerini kullanarak bunları erişilebilir kılmayı amaçlamaktadır. llvmcpy hala erken aşamadadır, ancak LLVM API'leriyle şimdiden bazı temel çalışmalar yapabilir.

Bir dil oluşturmak için LLVM kitaplıklarını nasıl kullanacağınızı merak ediyorsanız, LLVM'nin kendi yaratıcılarının, Kaleidoscope adlı basit bir dil oluşturmanıza yardımcı olan C ++ veya OCAML kullanan bir öğreticisi vardır. O zamandan beri diğer dillere taşındı:

  • Haskell:  Orijinal öğreticinin doğrudan bağlantı noktası.
  • Python: Böyle bir bağlantı noktası öğreticiyi yakından takip ederken, diğeri etkileşimli bir komut satırı ile daha iddialı bir yeniden yazmadır. Her ikisi de LLVM'ye bağlama olarak llvmlite kullanır.
  • Rust  ve  Swift: LLVM'nin ortaya çıkmasına yardımcı olduğu iki dil için eğitimin bağlantı noktalarını almamız kaçınılmaz görünüyordu.

Son olarak, eğitim insan dillerinde de mevcuttur  . Orijinal C ++ ve Python kullanılarak Çince'ye çevrildi.

LLVM ne yapmaz

LLVM'nin sağladığı her şeyle, ne yapmadığını bilmek de faydalıdır.

Örneğin, LLVM bir dilin gramerini ayrıştırmaz. Lex / yacc, flex / bison, Lark ve ANTLR gibi birçok araç bu işi zaten yapıyor. Ayrıştırmanın yine de derlemeden ayrılması amaçlanmıştır, bu nedenle LLVM'nin bunların hiçbirini ele almaya çalışmaması şaşırtıcı değildir.

LLVM ayrıca, belirli bir dil etrafındaki daha geniş yazılım kültürünü doğrudan ele almaz. Derleyicinin ikili dosyalarını kurmak, bir kurulumdaki paketleri yönetmek ve araç zincirini yükseltmek - bunu kendi başınıza yapmanız gerekir.

Son olarak ve en önemlisi, LLVM'nin ilkel sağlamadığı dillerin hala ortak bölümleri vardır. Birçok dilde, ya belleği yönetmenin ana yolu olarak ya da RAII (C ++ ve Rust'un kullandığı) gibi stratejilere ek olarak bir çeşit çöp toplama bellek yönetimi vardır. LLVM size bir çöp toplama mekanizması sağlamaz, ancak kodun çöp toplayıcıları yazmayı kolaylaştıran meta verilerle işaretlenmesine izin vererek çöp toplamayı uygulamak için araçlar sağlar.

Ancak bunların hiçbiri, LLVM'nin sonunda çöp toplamayı uygulamak için yerel mekanizmalar ekleme olasılığını ortadan kaldırmaz. LLVM, altı ayda bir büyük bir sürümle hızla gelişiyor. Ve geliştirme hızı, yalnızca mevcut birçok dilin LLVM'yi geliştirme sürecinin merkezine koyması sayesinde artacaktır.