LLVM平臺,短短幾年間,改變了眾多編程語言的走向,也催生了一大批具有特色的編程語言的出現,不愧為編譯器架構的王者,也榮獲2012年ACM軟件系統獎 —— 題記
版權聲明:本文為 西風逍遙游 原創文章,轉載請注明出處 西風世界 [http://blog.csdn.net/xfxyy_sxfancy](http://blog.csdn.net/xfxyy_sxfancy)
# 函數的調用及基本運算符
之前我們提到了函數的定義,那么,定義好的函數如何調用才行呢?今天我們就來了解一下,函數的調用。
### 函數調用的宏形式
我們去讀之前對函數調用的語法樹翻譯形式:
~~~
printf("%d\n", y);
~~~
會被翻譯為:
~~~
Node
String call
String printf
String %d\n
ID y
~~~
這個宏的名字是call,是個不定參數的:
~~~
(call 函數名 參數表... )
~~~
于是我們就需要掃描參數表,獲取全部調用參數。
### 調用宏的基本形式
調用宏其實很簡單,就是不斷循環判斷有多少參數即可。
~~~
static Value* call_macro(CodeGenContext* context, Node* node) {
// 參數一 函數名
// 其余參數 要傳入的參數
for (Node* p = node->getNext(); p != NULL; p = p->getNext()) {
// 循環獲取參數
}
}
~~~
另外我們查閱一下LLVM的文檔,找到其中CallInst這個指令,LLVM的指令都派生自Instruction,可以發現構建的方法很簡單:
~~~
static CallInst * Create (Value *Func, ArrayRef< Value * > Args, const Twine &NameStr, BasicBlock *InsertAtEnd)
~~~
但是我們發現,Value中要傳輸的是一個Function對象,如何獲取呢?當然還是從符號表中獲取,我們下次會講符號表的實現,這次也和上節一樣,將接口先寫出來。
~~~
// 參數一 函數名
Value* func = context->getFunction(node);
if (func == NULL) {
errs() << "找不到函數的定義:";
errs() << node->getStr().c_str() << "\n";
exit(1);
}
~~~
函數調用在生成時,如果這個函數還沒有被掃描到,那么在生成時會報函數定義找不到的問題,這就是我們為什么要用多遍掃描。只有充分的多次掃描語法樹,才能獲取每個函數后面的函數定義。雖然像C語言那樣強制聲明也可以,但我個人不大喜歡這種風格。
至于參數的獲取,就十分簡單的,但有一點要注意,參數是遞歸生成的,例如:
~~~
printf("%d", add(3, 5));
~~~
這時,我們在獲取參數時,就會發現,其中一個參數是表達式,那么我們就要先對其進行處理:
~~~
// 其余參數 要傳入的參數
std::vector<Value*> args;
for (Node* p = node->getNext(); p != NULL; p = p->getNext()) {
Value* v = p->codeGen(context); // 遞歸地生成參數
if (v != NULL)
args.push_back(v);
}
~~~
Node類下面有實現codeGen方法,其作用就是重新調用了完整的對當前節點的代碼生成,方便遞歸調用:
~~~
Value* Node::codeGen(CodeGenContext* context) {
return context->MacroMake(this); // MacroMake是核心的代碼生成接口
}
~~~
于是我們遞歸地生成了這些代碼,就可以產生一條Call語句,那么別忘記將其返回給上一層:
~~~
static Value* call_macro(CodeGenContext* context, Node* node) {
// 參數一 函數名
Value* func = context->getFunction(node);
if (func == NULL) {
errs() << "找不到函數的定義:";
errs() << node->getStr().c_str() << "\n";
exit(1);
}
// 其余參數 要傳入的參數
std::vector<Value*> args;
for (Node* p = node->getNext(); p != NULL; p = p->getNext()) {
Value* v = p->codeGen(context);
if (v != NULL)
args.push_back(v);
}
CallInst *call = CallInst::Create(func, args, "", context->getNowBlock());
return call;
}
~~~
### 簡單運算符計算
對于計算機,加減乘除這些基本運算,就是幾個指令而已,但對于編譯器,卻也要分好幾種情況討論,因為,全部的運算符有這么多:
~~~
// Standard binary operators...
FIRST_BINARY_INST( 8)
HANDLE_BINARY_INST( 8, Add , BinaryOperator)
HANDLE_BINARY_INST( 9, FAdd , BinaryOperator)
HANDLE_BINARY_INST(10, Sub , BinaryOperator)
HANDLE_BINARY_INST(11, FSub , BinaryOperator)
HANDLE_BINARY_INST(12, Mul , BinaryOperator)
HANDLE_BINARY_INST(13, FMul , BinaryOperator)
HANDLE_BINARY_INST(14, UDiv , BinaryOperator)
HANDLE_BINARY_INST(15, SDiv , BinaryOperator)
HANDLE_BINARY_INST(16, FDiv , BinaryOperator)
HANDLE_BINARY_INST(17, URem , BinaryOperator)
HANDLE_BINARY_INST(18, SRem , BinaryOperator)
HANDLE_BINARY_INST(19, FRem , BinaryOperator)
// Logical operators (integer operands)
HANDLE_BINARY_INST(20, Shl , BinaryOperator) // Shift left (logical)
HANDLE_BINARY_INST(21, LShr , BinaryOperator) // Shift right (logical)
HANDLE_BINARY_INST(22, AShr , BinaryOperator) // Shift right (arithmetic)
HANDLE_BINARY_INST(23, And , BinaryOperator)
HANDLE_BINARY_INST(24, Or , BinaryOperator)
HANDLE_BINARY_INST(25, Xor , BinaryOperator)
~~~
這些定義很難找,在文檔中并沒有真正寫出來,而是在頭文件的`llvm/IR/Instruction.def`里面,這是宏定義的專屬部分。
這些還僅僅是數值運算,還不算比較運算的部分呢。
當然,這和計算機體系結構有關,浮點數的運算和整數肯定是不一樣的,而我們知道,右移位也分算數右移和邏輯右移。所以必然,會有大量不同的運算符。
創建指令則很簡單:
~~~
static BinaryOperator * Create (BinaryOps Op, Value *S1, Value *S2, const Twine &Name, BasicBlock *InsertAtEnd)
~~~
兩個運算數,可以是常量,也可以是變量load出值后,還可以是表達式返回值,只要兩個Value調用getType,符合運算規則,就可以。
注意,浮點數不能直接和整數運算,必須先將整形轉為浮點才可以。
于是以下是簡單的運算符操作,我只寫了整數的運算操作:
~~~
static Value* opt2_macro(CodeGenContext* context, Node* node) {
std::string opt = node->getStr();
Node* op1 = (node = node->getNext());
if (node == NULL) return NULL;
Node* op2 = (node = node->getNext());
if (node == NULL) return NULL;
Instruction::BinaryOps instr;
if (opt == "+") { instr = Instruction::Add; goto binOper; }
if (opt == "-") { instr = Instruction::Sub; goto binOper; }
if (opt == "*") { instr = Instruction::Mul; goto binOper; }
if (opt == "/") { instr = Instruction::SDiv; goto binOper; }
// 未知運算符
return NULL;
binOper:
return BinaryOperator::Create(instr, op1->codeGen(context),
op2->codeGen(context), "", context->getNowBlock());
~~~
### 附:文檔參考及源代碼
[CallInst類參考](http://llvm.org/doxygen/classllvm_1_1CallInst.html)
[BinaryOperator類參考](http://llvm.org/doxygen/classllvm_1_1BinaryOperator.html)
[github源碼-函數調用及基本運算符](https://github.com/sunxfancy/RedApple/blob/master/src/Macro/Functions.cpp)