<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                LLVM平臺,短短幾年間,改變了眾多編程語言的走向,也催生了一大批具有特色的編程語言的出現,不愧為編譯器架構的王者,也榮獲2012年ACM軟件系統獎 —— 題記 版權聲明:本文為 西風逍遙游 原創文章,轉載請注明出處 西風世界 [http://blog.csdn.net/xfxyy_sxfancy](http://blog.csdn.net/xfxyy_sxfancy) # 函數的翻譯方法 前面介紹了許多編譯器架構上面的特點,如何組織語法樹、如果多遍掃描語法樹。今天開始,我們就要設計本編譯器中最核心的部分了,如何設計一個編譯時宏,再利用LLVM按順序生成模塊。 ### 設計宏 我們的編譯器可以說是宏驅動的,因為我們掃描每個語法節點后,都會考察當前是不是一個合法的宏,例如我們來分析一下上一章的示例代碼: ~~~ void hello(int k, int g) { ...... } ~~~ 我暫時隱藏了函數體部分,讓大家先關注一下函數頭 ~~~ String function String void String hello Node Node String set String int String k Node String set String int String g Node ...... ~~~ 我們的語法樹的每一層相當于是鏈表組織的,通過next指針都可以找到下一個元素。 而語法樹的開頭部分,是一個“function”的宏名稱,這個部分就是提示我們用哪個宏函數來翻譯用的。 接下來的節點就是: 返回類型,函數名,參數表,函數體 例如參數表,里面的內容很多,但我們掃描時,它們是一個整體,進行識別。 所以我們的宏的形式實際上就是這樣: ~~~ (function 返回類型 函數名 (形參表) (函數體)) ~~~ 括號括起來的部分表示是一個列表,而不是一個元素。 ### 宏函數的編寫 我們之前已經定義了宏的函數形式,我們需要傳入的有我們自己的上下文類和當前要處理的Node節點,返回的是LLVM的Value類型(各個語句的抽象基類) ~~~ typedef Value* (*CodeGenFunction)(CodeGenContext*, Node*); ~~~ 于是我們將這個函數實現出來: ~~~ static Value* function_macro(CodeGenContext* context, Node* node) { // 第一個參數, 返回類型 // 第二個參數, 函數名 node = node->getNext(); // 第三個參數, 參數表 Node* args_node = node = node->getNext(); // 第四個參數, 代碼塊 node = node->getNext(); return F; } ~~~ 獲取一個字符串代表的類型,往往不是一件容易的事,尤其在存在結構體和類的情況下,這時,我們往往需要查一下符號表,檢查這個字符串是否為類型,以及是什么樣的類型,是基本類型、結構體,還是函數指針或者指向其他結構的指針等等。 獲取類型,往往是LLVM中非常重要的一步。 我們這里先寫一下查符號表的接口,不做實現,接下來的章節中,我們會介紹經典的棧式符號表的實現。 第二個參數是函數名,我們將其保存在臨時變量中待用: ~~~ static Value* function_type_macro(CodeGenContext* context, Node* node) { // 第一個參數, 返回類型 Type* ret_type = context->FindType(node); // 第二個參數, 函數名 node = node->getNext(); std::string function_name = node->getStr(); // 第三個參數, 參數表 Node* args_node = node = node->getNext(); // 第四個參數, 代碼塊 node = node->getNext(); return F; } ~~~ 接下來的參數表也許是很不好實現的一部分,因為其嵌套比較復雜,不過思路還好,就是不斷的去掃描節點,這樣我們就可以寫出如下的代碼: ~~~ // 第三個參數, 參數表 Node* args_node = node = node->getNext(); std::vector<Type*> type_vec; // 類型列表 std::vector<std::string> arg_name; // 參數名列表 if (args_node->getChild() != NULL) { for (Node* pC = args_node->getChild(); pC != NULL; pC = pC->getNext() ) { Node* pSec = pC->getChild()->getNext(); Type* t = context->FindType(pSec); type_vec.push_back(t); arg_name.push_back(pSec->getNext()->getStr()); } } ~~~ 其實有了前三個參數,我們就可以構建LLVM中的函數聲明了,這樣是不用寫函數體代碼的。 LLVM里很多對象都有這個特點,函數可以只聲明函數頭,解析完函數體后再將其填回去。結構體也一樣,可以先聲明符號,回頭再向里填入類型信息。這些特性都是方便生成聲明的實現,并且在多遍掃描的實現中也會顯得很靈活。 我們下面來聲明這個函數: ~~~ // 先合成一個函數 FunctionType *FT = FunctionType::get(ret_type, type_vec, /*not vararg*/false); Module* M = context->getModule(); Function *F = Function::Create(FT, Function::ExternalLinkage, function_name, M); ~~~ 這里,我們使用了函數類型,這也是派生自Type的其中一個類,函數類型也可以getPointerTo來獲取函數指針類型。 另外,如果構建函數時,添加了Function::ExternalLinkage參數,就相當于C語言的extern關鍵字,確定這個函數要導出符號。這樣,你寫的函數就能夠被外部鏈接,或者作為外部函數的聲明使用。 ### 函數的特殊問題 接下來我們要創建函數的代碼塊,但這部分代碼實際上和上面的不是在同一個函數中實現的,應該說,他們不是在一趟掃描中。 我們知道,如果要讓一個函數內的代碼塊能夠調用在任意位置聲明的函數,那么我們就必須對所有函數都先處理剛才講過的前三個參數,這樣函數的聲明就有了,在之后的正式掃描中,才有了如下的代碼塊生成部分: ~~~ // 第四個參數, 代碼塊 node = node->getNext(); BasicBlock* bb = context->createBlock(F); // 創建新的Block // 特殊處理參數表, 這個地方特別坑,你必須給每個函數的參數 // 手動AllocaInst開空間,再用StoreInst存一遍才行,否則一Load就報錯 // context->MacroMake(args_node->getChild()); if (args_node->getChild() != NULL) { context->MacroMake(args_node); int i = 0; for (auto arg = F->arg_begin(); i != arg_name.size(); ++arg, ++i) { arg->setName(arg_name[i]); Value* argumentValue = arg; ValueSymbolTable* st = bb->getValueSymbolTable(); Value* v = st->lookup(arg_name[i]); new StoreInst(argumentValue, v, false, bb); } } context->MacroMake(node); // 處理塊結尾 bb = context->getNowBlock(); if (bb->getTerminator() == NULL) ReturnInst::Create(*(context->getContext()), bb); return F; ~~~ 這個地方問題非常多,我先保留一個懸念,在接下來代碼塊和變量存儲與加載的講解中,我會再次提到這個部分的特殊處理。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看