
मैं habrayuzer का स्वागत करता हूं, इस लेख में हम LLVM संकलक की आंतरिक संरचना के बारे में बात करेंगे। आप LLVM के बारे में सामान्य तौर पर
यहाँ या
llvm.org पर पढ़ सकते हैं। जैसा कि आप जानते हैं, एलएलवीएम (सशर्त रूप से) में तीन भाग होते हैं - बायटेकोड, संकलन रणनीति और उर्फ एलएलवीएम अवसंरचना वातावरण। मैं बाद का विचार करूंगा।
सामग्री:
- एलएलवीएम विधानसभा
- ग्रहण के लिए स्नैप
- पर्यावरण वास्तुकला
- एलएलवीएम एपीआई
- अनुकूलन हैलो, विश्व!
यह आलेख GNV / Linux (Ubuntu 10.04) के तहत LLVM 2.7+ चला रहा है। उपयोग की जाने वाली सामग्री आधिकारिक
दस्तावेज हैं । सामग्री आईएसपी आरएएस में एलएलवीएम अनुसंधान के अनुभव के एक वर्ष पर आधारित हैं।
एलएलवीएम विधानसभा
LLVM स्रोतों को
आधिकारिक परियोजना पृष्ठ से , संग्रह में और रिपॉजिटरी दोनों से लिया जा सकता है। एलएलवीएम का निर्माण करना काफी सरल है, लेकिन इसकी अपनी विशेषताएं हैं: पहले एलएलवीएम खुद इकट्ठा होता है, फिर फ्रंट-एंड (हमारे मामले में llvm-gcc), जिसके बिना सी / सी ++ कार्यक्रमों को संकलित करना संभव नहीं होगा। यह माना जाता है कि सिस्टम में सभी आवश्यक पुस्तकालय मौजूद हैं, अन्यथा आपको कॉन्फ़िगरेशन के दौरान किसी भी तरह से दुर्व्यवहार किया जाएगा। इसलिए, हम स्रोत पेड़ की जड़ में हैं, LLVM संकलित करें:
mkdir build && cd build
# कॉन्फ़िगरेशन
.. / विन्यस्त करें --enable-jit --enable -अनुकूलित -साझा-साझा- व्याख्यात्मक = / उपसर्ग - प्राप्य -लक्ष्य = host-only
# - साझा-साझा आपको LLVM कर्नेल को एक बड़े साझा लाइब्रेरी के रूप में बनाने की अनुमति देता है
# के बजाय / उपसर्ग स्थापना के लिए आवश्यक पथ सेट करें
# - योग्य-लक्ष्य = होस्ट-केवल का मतलब केवल होस्ट आर्किटेक्चर समर्थन है
# अतिरिक्त त्वरण के लिए, आप जोड़ने योग्य-अभिकथन कर सकते हैं, वे हर कदम पर कोड में हैं, जिसमें शामिल हैं चक्रों में
# विधानसभा
make -j2 CXXFLAGS = -O3
# -जेएन बहुत प्रतीकात्मक है, ज्यादातर समय, सभी एक ही, सब कुछ एक धारा में एकत्र किया जाएगा,
# इसके बारे में कुछ नहीं किया जा सकता है
# CXXFLAGS = -O3 अतिरिक्त GCC अनुकूलन देता है।
# आप जीसीसी 4.5+ के मामले में लिंक-टाइम अनुकूलन को सक्षम करने के लिए -flto भी जोड़ सकते हैं
# स्थापना
स्थापित करें
अब llvm-gcc का निर्माण करें:
# कोड ले लो
svn co http: // llvm.org / svn / llvm-project / llvm-gcc- 4.2 / ट्रंक llvm-gcc
cd llvm-gcc && mkdir build && cd build
# कॉन्फ़िगरेशन
.. / विन्यस्त करें --enable-llvm = / path / to / llvm / build --prefix = / prefix / of / llvm --program-prefix = llvm- --enable-languages = c, c ++
# / पथ / to / llvm / build का अर्थ है पिछले चरण में बनाए गए उसी बिल्ड फ़ोल्डर का पथ
# # उपसर्ग / / llvm - सुविधा के लिए, उपसर्ग LLVM उपसर्ग से मेल खाता है
# --प्रोग्राम-उपसर्ग = llvm- को llvm-gcc या llvm-g ++ जैसे सामान्य द्विआधारी नाम प्राप्त करने की आवश्यकता होती है
# विधानसभा
make -j2 CFLAGS = -O3
# स्थापना
स्थापित करें
यदि सब कुछ ठीक रहा, तो, उदाहरण के लिए, ऐसा चेक पास होगा:
/ उपसर्ग / का / llvm / llvm-gcc -v
...
gcc संस्करण 4.2.1 ( Apple Inc. build 5658 के आधार पर ) ( LLVM build )
ग्रहण के लिए स्नैप
सुविधा के लिए, मैं आपको बताता हूं कि ग्रहण में एलएलवीएम स्रोत कोड का संकलन और अध्ययन कैसे करें (बेशक, सी / सी ++ समर्थन के साथ)। एक नया प्रोजेक्ट C ++
/ File-New - C ++ प्रोजेक्ट बनाएँ, नाम "llvm"
/ प्रोजेक्ट नाम सेट
करें ,
डिफ़ॉल्ट स्थान अनचेक
करें / डिफ़ॉल्ट स्थान का उपयोग करें / और एलएलवीएम स्रोत ट्री के लिए पथ सेट करें, प्रोजेक्ट प्रकार को Makefile
/ Project प्रकार पर सेट करें
: मेकफाइल प्रोजेक्ट - खाली प्रोजेक्ट / । अगला, "समाप्त करें"
/ समाप्त करें पर क्लिक
करें । जबकि ग्रहण फाइलों को पार्स करता है और नेविगेशन इंडेक्स बनाता है, प्रोजेक्ट प्रॉपर्टीज
/ प्रोजेक्ट - प्रॉपर्टीज / पर जाएं । बिल्डर
/ बिल्डर सेटिंग्स की सेटिंग में, डिफ़ॉल्ट बिल्ड कमांड को अनचेक
करें / डिफ़ॉल्ट बिल्ड कमांड का उपयोग करें / और "मेक -j2 CXXFLAGS = -O3" दर्ज करें। बिल्ड डायरेक्टरी
/ बिल्ड डायरेक्टरी / में, "/ बिल्ड" जोड़ें, ताकि यह "$ {वर्कस्पेस_लोक: / llvm} / बिल्ड" हो। व्यवहार टैब / व्यवहार / पर जाएं और बिल्ड लक्ष्य
/ बिल्ड (इंक्रीमेंटल बिल्ड) के दाईं ओर
/ "सभी" मिटा दें। उत्तरार्द्ध आवश्यक नहीं है और कंसोल में मैनुअल असेंबली की पहचान के लिए आवश्यक है। हो गया! आप कक्षाओं के माध्यम से सुरक्षित रूप से कूद सकते हैं, # देखें और बिल्ड बटन
/ बिल्ड / पर क्लिक करें। संभावित समस्याओं (जैसे कोई) से बचने के लिए, आप पेड़ से परीक्षण हटा सकते हैं।
पर्यावरण वास्तुकला
LLVM में libLLVM2.x.so (कर्नेल) का उपयोग करके कई निष्पादन शामिल हैं।
ऑप्ट एक उपकरण है जो बाइटकोड से संचालित होता है: यह किसी भी क्रम में उपलब्ध मशीन-स्वतंत्र अनुकूलन को रोल कर सकता है, विभिन्न प्रकार के विश्लेषण कर सकता है और प्रोफाइलिंग जोड़ सकता है। अनुकूलन स्तर O0, O1, O2 और O3 हैं।
llc - टारगेट मशीन के असेंबलर को bytecode कोड जनरेटर। मशीन आश्रित अनुकूलन करता है। अनुकूलन स्तर O0, O1, O2 और O3 भी हैं।
llvm-ld -
बाईटेकोड लिंकर, अर्थात उपकरण एक बड़ी ई.पू. फ़ाइल में कई बाइटकोड फ़ाइलों को जोड़ता है। उसी समय, लिंक-टाइम ऑप्टिमाइज़ेशन रोल कर रहे हैं, जो रचनाकारों को बहुत गर्व है।
LIT JIT संकलन क्षमता के साथ एक बाईटेकोड दुभाषिया है।
llvm-dis - bytecode (bc) समकक्ष पाठ प्रतिनिधित्व (ll) कनवर्टर के लिए।
llvm-as एक टेक्स्ट रिप्रेजेंटेशन (ll) है
जिसे बायटेकोड (bc) कनवर्टर है।
llvm-ar - एक में कई फाइलों का पैकर। कुछ प्रकार के bzip2 एनालॉग।
llvmc LLVM ड्राइवर है, जो लैंग्वेज पार्सर (यानी फ्रंट-एंड: llvm-gcc या clang) कहता है, ऑप्ट, llc (लक्ष्य मशीन के लिए निर्दिष्ट बैक-एंड), असेंबलर और लक्ष्य मशीन के लिंकर। सामान्य योजना को आंकड़े में दिखाया गया है।

यह ध्यान देने योग्य है कि सभी प्रसिद्ध आर्किटेक्चर (x86, x86-64, ARM सहित) बैक-एंड के रूप में समर्थित हैं, और यहां तक कि सी भाषा के रूप में इस तरह के विदेशी। उत्तरार्द्ध का मतलब है कि आप बायटेकोड को सीधे सी में स्रोत में बदल सकते हैं ... लेकिन यह संभावना नहीं है कि आप इसे वेरिएबल नामों के बिना पसंद करेंगे (या बल्कि, बायटेकोड में सौंपे गए अन्य नामों के साथ), गोटो लूप्स, कमांड्स का एक बदला हुआ क्रम, और बहुत कुछ के साथ। लेकिन और कैसे, चमत्कार नहीं होते। वैसे, बाइटकोड को संभवतः जावा या .NET वर्चुअल मशीन कोड में बदल दिया जा सकता है।
"लोहा" बैक-एंड-एस का एक दिलचस्प कार्यान्वयन। LLVM बिल्ड में एक प्रारंभिक चरण में, एक उपयोगिता जिसे
tblgen कहा जाता है,
दिखाई देती है । यह एक विशिष्ट वर्णनात्मक भाषा में कोडांतरक विवरण को C ++ कोड में भी परिवर्तित करता है, जो बाद में कोड पीढ़ी को लागू करने वाली कक्षाओं में #included होता है। इस प्रकार, कमांड सिस्टम में कुछ बदलना या जोड़ना बहुत आसान है, और समर्थन एकीकृत है। जीसीसी के विपरीत, आपको क्रॉस-संकलन पर कड़ी मेहनत नहीं करनी होगी: जब आप llc को कॉल करते हैं, तो बस निर्दिष्ट करें -आर्म = armv7, जहां armv7 के स्थान पर कोई भी समर्थित आर्किटेक्चर हो सकता है। मैं यहाँ पर एलएलवीएम और मूलभूत अंतर के बारे में नहीं बता सकता हूँ, कहते हैं, जावा: एलएलवीएम बायटेकोड आम तौर पर उस प्लेटफ़ॉर्म पर निर्भर करता है जिस पर इसे प्राप्त किया जाता है और टारगेट मशीन जिसके लिए इसे संकलित किया जाता है। पहला ओएस-आश्रित पुस्तकालयों के साथ जोड़ने के कारण है, दूसरा कोडक आवेषण और कोड के पोर्टिंग (सशर्त संकलन) के कारण विभिन्न आर्किटेक्चर के लिए है।
कोड के साथ कुछ फ़ोल्डरों का मान:
- शामिल - हेडर फाइलें
- उपकरण - उपरोक्त उपकरणों का कार्यान्वयन
- रनटाइम - प्रोफाइल संग्रह कार्यान्वयन
- उपकरण - एलएलवीएम उपयोगिता उपयोगिताओं का कार्यान्वयन, incl। tblgen
- उदाहरण - LLVM उदाहरण
- lib - मुख्य भाग
- lib / विश्लेषण - पास जो विश्लेषण करते हैं (पास के लिए नीचे देखें)
- lib / CodeGen - कोड पीढ़ी, मशीन पर निर्भर पास का कार्यान्वयन
- lib / Transform - बाईटेकोड पर किए गए पास
सिद्धांत रूप में, नामों से अर्थ स्पष्ट हैं, और यह सबूत भ्रामक नहीं है।
एलएलवीएम एपीआई
LLVM कर्नेल एक बहुत शक्तिशाली प्रणाली है। सभी सामान्य चीजें सामान्य डेवलपर्स से छिपी हुई हैं, जिससे आपको संकलक की गुणवत्ता में सुधार करने पर ध्यान केंद्रित करने का अवसर मिलता है। विस्तृत प्रलेखन, बहुत सारी सामग्री आपको जल्दी से पता करने की अनुमति देती है। वर्गों, विधियों, वंशानुक्रम योजना के नाम - सब कुछ सबसे छोटे विस्तार से सोचा गया है। मैं अनुकूलन संबंधी एपीआई के बारे में थोड़ी बात करूंगा। ऊब नहीं होने के लिए, यह जानना उचित है कि बुनियादी ब्लॉक और नियंत्रण प्रवाह ग्राफ (उर्फ सीएफजी, नियंत्रण प्रवाह ग्राफ) के रूप में ऐसे शब्द क्या हैं।
आधार इकाई और
नियंत्रण प्रवाह ग्राफ से पर्याप्त जानकारी।
सामान्य तौर पर, यह अनुकूलन के बजाय पास के बारे में बात करने के लिए प्रथागत है। एक पास एक विशेष अनुकूलन या उसके हिस्से का कार्यान्वयन है। एलएलवीएम में एक पासमैन है, जो एफआईएफओ सिद्धांत के अनुसार काम करता है। पहले, पास विवरण जोड़े जाते हैं, और फिर प्रबंधक उन्हें एक-एक करके निष्पादित करेगा। इसके अलावा, यदि मार्ग को कुछ सीएफजी विश्लेषण से डेटा की आवश्यकता होती है, तो प्रबंधक पहले इस विश्लेषण का संचालन करेगा यदि उसका डेटा पुराना हो। किसी फ़ंक्शन (FunctionPass से विरासत में मिली) या बेस ब्लॉक (BasicBlockPass) के रूप में कार्य करता है। पहले मामले में, एल्गोरिथ्म के इनपुट के लिए एक फ़ंक्शन दिया जाएगा, दूसरे में, आधार इकाई। निर्देशों के अनुसार, बेस ब्लॉक पर क्रमशः आधार ब्लॉक पर मानक तरीके से फ़ंक्शन को पुनरावृत्त किया जा सकता है। आप अपनी इच्छानुसार सब कुछ पुनर्व्यवस्थित कर सकते हैं, नए तत्व जोड़ सकते हैं।
एलएलवीएम ऑप्ट प्लगइन्स का समर्थन करता है - जैसे कि साझा पास। एक कॉल के दौरान, पुस्तकालय को लोड किया जाता है और इसे ध्यान में रखा जाता है। इसके कारण, संकलक की क्षमताओं का विस्तार करने के लिए, अपनी खुद की कोड शाखा बनाने के लिए आवश्यक नहीं है और फिर इसे संपादित करें, आवश्यक हेडर फाइलें पर्याप्त हैं। जैसा कि आप जानते हैं, जीसीसी में यह हाल ही में सामने आया है।
जैसा कि वे कहते हैं, सौ बार सुनने की तुलना में एक बार देखना बेहतर है। अभ्यास के लिए आगे बढ़ते हैं।
अनुकूलन हैलो, विश्व!
हम अपने स्वयं के वास्तविक मशीन-स्वतंत्र अनुकूलन को लिखने के लिए अर्जित ज्ञान को लागू करते हैं। वह सब करेगी आधार इकाई में निर्देशों की औसत संख्या पर आंकड़े एकत्र करती है, और वास्तव में अनुकूलन के लिए कुछ भी नहीं करती है। चलो इसे ऑप्ट प्लग-इन के रूप में बनाते हैं, ताकि LLVM कोड को संपादित न किया जा सके। एलएलवीएम स्रोत पेड़ की जड़ में प्लगइन्स फ़ोल्डर बनाएँ, और इसमें फाइल BasicBlockStats.cpp के साथ निम्नलिखित विकल्प हैं:
#define DEBUG_TYPE "मूल-ब्लॉक-आँकड़े"
# जारी करें "llvm / Pass.h"
# जारी "llvm / Function.h"
# जारी करें "llvm / निर्देश.एच"
# जारी करें "llvm / BasicBlock.h"
#include "llvm / ADT / Statistic.h"
#include "llvm / Transforms / Scalar.h"
#include "llvm / Support / CFG.h"
नामस्थान llvm का उपयोग करना ;
// एकत्रित आँकड़ों की घोषणा।
// उनमें से प्रत्येक केवल एक अहस्ताक्षरित पूर्णांक हो सकता है।
// बेस ब्लॉक की कुल संख्या
सांख्यिकी ( NumberOfBasicBlocks, "मॉड्यूल में बुनियादी ब्लॉकों की संख्या" ) ;
// निर्देशों की कुल संख्या
सांख्यिकी ( NumberOfInstructions, "मॉड्यूल में निर्देशों की संख्या" ) ;
// प्रति बेस यूनिट निर्देशों की औसत संख्या
सांख्यिकी ( औसत अवरोधन InBasicBlock, "एक बुनियादी ब्लॉक में निर्देशों की औसत संख्या" ) ;
नाम स्थान
{
// पास (पास) की संरचना (कक्षा)
// आमतौर पर स्वीकृत शब्दावली में, अनुकूलन को पास कहा जाता है, क्योंकि
// मॉड्यूल पर कार्रवाई अपनी सामग्री के माध्यम से एक पूर्ण पास में की जाती है
बुनियादी BasicBlockStats : सार्वजनिक समारोह
{
// पास पहचान संख्या
स्थिर चार आईडी ;
BasicBlockStats ( ) : FunctionPass ( ID ) { }
// एक विधि को ओवरलोड करना जो पास प्रबंधक को बताता है कि वह दिए गए पास का उपयोग करता है और बदलता है
वर्चुअल शून्य getAnalysisUsage ( AnalysisUsage और AU ) कास्ट
{
// कुछ भी उपयोग न करें, कुछ भी न बदलें
ए.यू.। setPreservesAll ( ) ;
}
// एक ऐसी विधि को ओवरलोड करना जो एक मॉड्यूल फ़ंक्शन पर पास - संचालन का अर्थ वहन करती है
वर्चुअल बूल runOnFunction ( फ़ंक्शन & एफ ) ;
~ बेसिकब्लॉकस्टैट्स ( )
{
// विध्वंसक को बुलाते समय, वांछित संख्या पर विचार करें
औसतइंस्ट्रक्शनइन् बेसबिकब्लॉक = नंबरऑफइंस्ट्रक्शन / नंबरऑफबेसिकब्लॉक ;
}
} ;
// हमें खुद को पहचानने की आवश्यकता नहीं है
char BasicBlockStats :: ID = 0 ;
// पास मैनेजर में पैसेज रजिस्टर करें
RegisterPass < BasicBlockStats > X ( "बेसिक-ब्लॉक- स्टैटिस्टिक्स" , "बेसिक ब्लॉक स्टैटिस्टिक्स" , सच ) ;
} // अनाम नामस्थान का अंत
// मुख्य तर्क का कार्यान्वयन
बूल BasicBlockStats :: runOnFunction ( फ़ंक्शन और एफ )
{
// हम फ़ंक्शन के प्रत्येक बेस ब्लॉक से गुजरते हैं
के लिए ( समारोह :: iterator I = F. start ( ) , E = F. end ( ) ; I ! = E ; I ++ ;
{
NumberOfInstructions + = I - > आकार ( ) ;
}
// बेस ब्लॉक की संख्या जोड़ें
NumberOfBasicBlocks + = F. size ( ) ;
झूठे लौटना ;
}
मैंने इसे स्पष्ट करने के लिए यथासंभव टिप्पणियों के साथ कोड प्रदान करने का प्रयास किया। अब हमारे प्लग-इन को इकट्ठा करें:
g ++ -c -fPIC BasicBlockStats.cpp -o BasicBlockStats.o -I .. / / -I .. / build / शामिल करें -D__STDC_LIMIT_MACROS -D__STDC_CSTSTANT_MACROS
gcc -sared -s BasicBlockStats.o -o BasicBlockStats.so
यह जांचने का समय है कि क्या यह काम करता है? मेरा सुझाव है कि SQLite कोड का उपयोग करके एक कॉम्बैट टेस्ट आयोजित करना। डाउनलोड
sqlite-amalgamation-A_B_C_D.zip , इसे कहीं अनपैक करें। इसके अलावा, मुझे लगता है कि स्थापित LLVM और llvm-gcc निष्पादनों के साथ-साथ BasicBlockStats.so तक का रास्ता $ PATH में निर्दिष्ट है।
# बाइटकोड में SQLite लाइब्रेरी डालना
llvm-gcc -emit-llvm -c sqlite3.c -o sqlite3.bc
# हमारा अनुकूलन लॉन्च करें
# -स्टैट एकत्रित किए गए सभी आंकड़ों को दर्शाता है
ऑप्ट sqlite3.bc- लोड BasicBlockStats.so -basic - block- सांख्यिकी -stats -o sqlite3.bc
=== ----------------------------------------------- -------------------------- === <br/>
... आंकड़े एकत्रित किए गए ... <br/>
=== ----------------------------------------------- -------------------------- === <br/>
<br/>
8 बुनियादी-ब्लॉक-आँकड़े - एक बुनियादी ब्लॉक में निर्देशों की औसत संख्या <br/>
18871 बुनियादी-ब्लॉक-आँकड़े - मॉड्यूल में बुनियादी ब्लॉकों की संख्या <br/>
153836 बुनियादी-ब्लॉक-आँकड़े - मॉड्यूल में निर्देशों की संख्या
तो, SQLite कोड में आधार इकाई में निर्देशों का औसत मूल्य 8 है।
शायद अपना खुद का पास (पास) लिखने की सरलता पाठक को इस क्षेत्र में शोध के लिए प्रेरित करेगी, और मैं यहां समाप्त हो जाऊंगा। मुझे आपके सवालों के जवाब देने में खुशी होगी।