Chapter 3. Kaleidoscope: Code generation to LLVM IR
简介
本章将向您展示如何将第二章构建的抽象语法树转换为 LLVM IR。这将为您介绍 LLVM 的一些工作原理,并演示其使用的简易性。构建词法分析器和解析器比生成 LLVM IR 代码要复杂得多。请注意:本章及以后的代码需要 LLVM 3.7 或更高版本。LLVM 3.6 及之前的版本不兼容。另外,请注意您需要使用与您的 LLVM 发行版匹配的本教程版本:如果您正在使用官方 LLVM 发行版,请使用与您的发行版或 llvm.org 发布页面上提供的文档版本相匹配的教程版本。
本文仍然沿用主页的教程。
3.1 Static single assignment form
静态单赋值SSA是LLVM中的核心存在,含义为 Every var can only be assigned once.
SSA的主要用处在于它同时简化和改进了各种编译器优化的结果,通过简化变量的属性。例如,考虑下面的代码片段:
y := 1
y := 2
x := y
可以看出第一个赋值是不必要的,并且第三行中使用的 y 的值来自于第二次赋值。程序必须执行到达定义分析来确定这一点。但如果程序处于 SSA 形式,这两个都是immediate的:
y1 := 1
y2 := 2
x1 := y2
使用 SSA(静态单赋值)技术启用或强化的编译器优化算法包括:
常量传播 - 将运行时的计算转换为编译时,例如将指令 a=3*4+5; 视为 a=17;
值范围传播 - 预先计算计算可能的范围,允许提前创建分支预测
稀疏条件常量传播 - 对一些值进行范围检查,允许测试预测最可能的分支
死代码消除 - 删除对结果没有影响的代码
全局值编号 - 替换产生相同结果的重复计算
部分冗余消除 - 删除先前在程序某些分支中执行的重复计算
强度降低 - 用较便宜但等效的操作替换昂贵的操作,例如用左移(乘法)或右移(除法)替换整数乘法或除法的指数2
寄存器分配 - 优化有限数量的机器寄存器如何用于计算
3.2 CodeGen
为了生成 LLVM IR,我们需要一些简单的设置来开始。首先,我们在每个 AST 类中定义虚拟代码生成(codegen)方法:
/// ExprAST - Base class for all expression nodes.
class ExprAST {
public:
virtual ~ExprAST() = default;
//基类中使用 = 0 表示纯虚函数。纯虚函数是在基类中声明但没有提供实现的虚函数。派生类必须提供实现以使基类变得可实例化
virtual Value *codegen() = 0;
};
// NumberExprAST - for numeric literals like "1.0"
class NumberExprAST: public ExprAST{
double Val;
public:
NumberExprAST(double Val) : Val(Val){}
Value *codegen() override;
};
codegen() 方法用于为该AST节点及其所有依赖项发出IR,并且它们都返回一个LLVM Value对象。“Value”是LLVM中用于表示“静态单赋值(SSA)寄存器”或“SSA值”的类。SSA值最显著的特点是,它们的值是随着相关指令的执行而计算的,并且直到(如果)指令重新执行,它们才会获得新值。换句话说,没有办法“改变”SSA值。有关更多信息,请阅读关于Static Single Assignment的资料-一旦理解了这些概念,就会觉得它们非常自然。
请注意,与其向ExprAST类层次结构添加虚拟方法,使用访问者模式或其他模型也可能是有意义的。再次强调,本教程不会详细讨论良好的软件工程实践:对于我们的目的,添加虚拟方法是最简单的。
static unique_ptr<LLVMContext> TheContext;
static unique_ptr<IRBuilder<>> Builder(TheContext);
static unique_ptr<Module> TheModule;
static map<string, Value *> NamedValues;
Value *LogErrorV(const char *Str){
LogError(Str);
return nullptr;
}
静态变量将在代码生成过程中使用。TheContext 是一个不透明对象,拥有许多核心的 LLVM 数据结构,例如类型和常量值表。我们不需要详细了解它,我们只需要一个单一实例来传递给需要它的 API。
Builder 对象是一个辅助对象,可以轻松生成 LLVM 指令。IRBuilder 类模板的实例跟踪当前插入指令的位置,并具有创建新指令的方法。
TheModule 是一个包含函数和全局变量的 LLVM 结构。在许多方面,它是 LLVM IR 用来包含代码的顶级结构。它将拥有我们生成的所有 IR 的内存,这就是为什么 codegen() 方法返回一个原始的 Value*,而不是一个 unique_ptr。
NamedValues 映射跟踪当前作用域中定义的值以及它们的 LLVM 表示形式。(换句话说,它是代码的符号表)。在这个 Kaleidoscope 的形式中,唯一可以引用的是函数参数。因此,在为函数体生成代码时,函数参数将在这个映射中。
有了这些基础,我们可以开始讨论如何为每个表达式生成代码。请注意,这假设 Builder 已经设置好,可以将代码生成到某个地方。暂时我们假设已经完成了这个设置,并且只是使用它来发出代码。
3.2.1 Expression Code Generation
// NumberExprAST - for numeric literals like "1.0"
class NumberExprAST: public ExprAST{
double Val;
public:
NumberExprAST(double Val) : Val(Val){}
Value *codegen() override;
};
Value *NumberExprAST::codegen(){
return ConstantFP::get(*TheContext, APFloat(Val));
}
class VariableExprAST: public ExprAST{
std::string Name;
public:
VariableExprAST(const std::string &Name): Name(Name){}
Value *codegen() override;
};
Value *VariableExprAST::codegen(){
// look this variable up in the function
Value *V = NamedValues[Name];
if (!V)
LogErrorV("Unknown variable name");
return V;
}
class BinaryExprAST: public ExprAST{ // 2+3
char Op; // + - * \ < >
std::unique_ptr<ExprAST> LHS, RHS;
// smart pointer: c++ doesn't have garbage collector
public:
BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS, std::unique_ptr<ExprAST> RHS):
Op(Op), LHS(move(LHS)), RHS(move(RHS)){}
Value *codegen() override;
};
Value *BinaryExprAST::codegen(){
Value *L = LHS->codegen();
Value *R = RHS->codegen();
if (!L || !R)
return nullptr;
switch(Op){
case '+':
return Builder->CreateFAdd(L, R, "addtmp");
case '-':
return Builder->CreateFAdd(L, R, "subtmp");
case '*':
return Builder->CreateFMul(L, R, "multmp");
case '<':
L = Builder->CreateFCmpULT(L, R, "cmptmp");
return Builder->CreateUIToFP(L, Type::getDoubleTy(TheContext), "booltmp");
default:
return LogErrorV("invalid binary operator");
}
}
class CallExprAST: public ExprAST{
std::string Callee;
std::vector<std::unique_ptr<ExprAST>> Args;
public:
CallExprAST(const std::string &Callee, std::vector<std::unique_ptr<ExprAST>> Args):
Callee(Callee), Args(move(Args)){}
Value *codegen() override;
};
Value *CallExprAST::codegen(){
// look up the name in the global module table
Function *CalleeF = TheModule->getFunction(Callee);
if (!CalleeF)
return LogErrorV("Unknown function referenced");
// if argument mismatch error
if (CalleeF->arg_size() != Args.size())
return LogErrorV("Incorrect # arguments passed");
std::vector<Value *> ArgsV;
for (unsigned i=0, e=Args.size();i!=e;++i){
ArgsV.push_back(Args[i]->codegen());
if (!ArgsV.back()) // the last element
return nullptr;
}
return Builder->CreateCall(CalleeF, ArgsV, "calltmp");
}
3.2.2 Function Code Generation
Function *PrototypeAST::codegen(){
// make the function type: double(double, double) etc.
std::vector<Type*> Doubles(Args.size(),Type::getDoubleTy(*TheContext));
FunctionType *FT = FunctionType::get(Type::getDoubleTy(*TheContext), Doubles, false);
Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule.get());
//Set names for all arguments
unsigned Idx = 0;
for (auto &Arg: F->args())
Arg.setName(Args[Idx++]);
return F;
}
这段代码凝练而功能强大。首先要注意的是,这个函数返回一个“Function*”,而不是一个“Value*”。因为“prototype”实际上描述的是一个函数的外部接口(而不是表达式计算得到的值),因此返回与其对应的LLVM函数的指针是合理的。
调用FunctionType::get创建了应用于给定原型的FunctionType。由于Kaleidoscope中的所有函数参数都是double类型,因此第一行创建了一个由“N”个LLVM double类型组成的向量。然后使用FunctionType::get方法创建了一个函数类型,该类型以“N”个double类型的参数作为参数,返回一个double类型的结果,并且不是vararg(false参数指示此情况)。请注意,LLVM中的类型与常量一样是唯一的,因此您不需要“new”一个类型,而是“get”它。
上面的最后一行实际上创建了与Prototype相对应的IR函数。这指示了要使用的类型、链接和名称,以及要插入到哪个模块中。“external linkage”意味着该函数可以在当前模块之外定义,和/或者可被模块外的函数调用。传入的名称是用户指定的名称:由于指定了“TheModule”,因此此名称将在“TheModule”的符号表中注册。
Function *FunctionAST::codegen(){
//first, check for an existing function from a previous 'extern' declaration
Function *TheFunction = TheModule->getFunction(Proto->getName());
if (!TheFunction)
TheFunction = Proto->codegen();
if (!TheFunction)
return nullptr;
if (!TheFunction->empty())
return (Function*)LogErrorV("Function cannot be redefined");
// create a new bb to start insertion into
BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", TheFunction);
Builder->SetInsertPoint(BB);
// record the function arguments in the NamedValues map
NamedValues.clear();
for (auto &Arg: TheFunction->args())
NamedValues[std::string(Arg.getName())] = &Arg;
Value *RetVal = Body->codegen();
if (RetVal){
//finish off the function
Builder->CreateRet(RetVal);
//velidate the generated code, checking for consistency
verifyFunction(*TheFunction);
return TheFunction;
}
//error reading body ,remove funciton
TheFunction->eraseFromParent();
return nullptr;
}
对于函数定义,我们首先搜索TheModule的符号表,以查找是否已经使用“extern”语句创建了此函数的现有版本。如果Module::getFunction返回null,则表示不存在先前的版本,因此我们将从Prototype开始生成一个。在任何情况下,我们都希望在开始之前断言函数是空的(即尚未具有body)。
现在我们来到了设置Builder的地方。第一行创建了一个新的基本块(命名为“entry”),它被插入到TheFunction中。第二行告诉构建器新指令应该插入到新基本块的末尾。LLVM中的基本块是定义控制流图的函数的重要部分。由于我们没有任何控制流,所以在这一点上,我们的函数只包含一个块。我们将在第五章中解决这个问题 😃.
接下来,我们将函数参数添加到NamedValues映射中(首先清除它),以便它们对VariableExprAST节点可访问。
一旦插入点设置好并且NamedValues映射填充好,我们就调用函数的根表达式的codegen()方法。如果没有错误发生,这将生成代码来计算表达式并将其放入入口块,并返回计算得到的值。假设没有错误,我们然后创建一个LLVM ret指令,这样函数就完成了。构建函数后,我们调用verifyFunction,这是LLVM提供的函数。该函数对生成的代码执行各种一致性检查,以确定我们的编译器是否正确执行了所有操作。使用这个函数很重要:它可以捕获到很多错误。一旦函数完成并验证通过,我们就返回它。
3.2.3 top-level parsing and jit driver
static void InitializeModule(){
//Open a new context and module
TheContext = std::make_unique<LLVMContext>();
TheModule = std::make_unique<Module>("my cool jit", *TheContext);
//create a new builder for the module
Builder = std::make_unique<IRBuilder<>>(*TheContext);
}
static void HandleDefinition(){
if(auto FnAST = ParseDefinition()){
if (auto *FnIR = FnAST->codegen()){
fprintf(stderr, "Read function definition: ");
FnIR->print(errs());
fprintf(stderr, "\n");
}
}
else //skip token for error recovery
getNextToken();
}
static void HandleExtern(){
if (auto ProtoAST = ParseExtern()){
if (auto *FnIR = ProtoAST->codegen()){
fprintf(stderr, "Read extern: ");
FnIR->print(errs());
fprintf(stderr, "\n");
}
}
else //skip token for error recovery
getNextToken();
}
static void HandleTopLevelExpression(){
// evaluate a top-level expression into an anonymous function
if (auto FnAST = ParseTopLevelExpr()){
if (auto *FnIR = FnAST->codegen()){
fprintf(stderr, "Read top-level expression: \n");
FnIR->print(errs());
fprintf(stderr, "\n");
// remove token for error recovery
FnIR->eraseFromParent();
}
}
else
getNextToken();
}
// the driver
static void MainLoop(){
while(true){
fprintf(stderr, "read> ");
switch(CurTok){
case tok_eof:
return;
case ';': // ignore top-level semicolons
getNextToken();
break;
case tok_def:
HandleDefinition();
break;
case tok_extern:
HandleExtern();
break;
default:
HandleTopLevelExpression();
break;
}
}
}
Debug & Run
测试函数:
int main(){
fprintf(stderr, "ready> ");
InitializeModule();
getNextToken();
MainLoop();
return 0;
}
下载官网llvm10的预编译包,基于此进行扩展。
编译命令:
export PATH=/home//llvm-10.0/bin:$PATH
clang++ -g -O3 my-lang.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o my-lang.bin
运行结果:
gdb tips
GDB GUI 是一系列工具的统称,它们提供了图形界面来辅助使用 GDB(GNU Debugger),从而使得调试过程更加直观和方便。这些工具通过图形化的方式展示源代码、变量、断点、线程等调试信息,使得开发者能够更加高效地进行代码调试。
vscode gdb
安装:sudo apt-get install lldb
使用:lldb ./my-lang.bin
or gdb ./my-lang.bin
使用vscode 进行可视化gdb要更加方便快捷,配置步骤见另一篇博客。
full code
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
using namespace llvm;
enum Token{
tok_eof=-1,
tok_def=-2,
tok_extern=-3,
tok_identifier=-4,
tok_number=-5
};
static std::unique_ptr<LLVMContext> TheContext;
static std::unique_ptr<IRBuilder<>> Builder;
static std::unique_ptr<Module> TheModule;
static std::map<std::string, Value *> NamedValues;
// 在编程语言的词法分析器(lexer)中,标识符通常是指用来标识变量、函数、类、模块等命名实体的字符序列。
static std::string IdentifierStr=""; // filled in if tok_identifier
static double NumVal; // filled in if tok_number
// gettok
static int gettok(){
static int LastChar = ' ';
// skip space
while(isspace(LastChar)){
LastChar = getchar();
}
// get identifier
if (isalpha(LastChar)){ // identifier: [a-zA-z][a-zA-Z0-9]*
IdentifierStr = LastChar;
while (isalnum(LastChar=getchar()))
IdentifierStr += LastChar;
if (IdentifierStr == "def")
return tok_def;
if (IdentifierStr == "extern")
return tok_extern;
return tok_identifier;
}
// get number
if (isdigit(LastChar) || LastChar == '.'){ // Number: [0-9.]+
std::string NumStr;
do{
NumStr += LastChar;
LastChar = getchar();
}while (isdigit(LastChar) || LastChar == '.');
// 这行代码的功能是将一个字符串转换为双精度浮点数
// .c_str() 方法将 NumStr 转换为以空字符结尾的 C 风格字符串(即以 const char* 的形式返回字符串的指针)
// strtod() 是一个 C 标准库函数,用于将字符串转换为双精度浮点数。两个参数:第一个参数是要转换的字符串的指针,第二个参数是一个指向字符指针的指针,用于存储第一个无法转换的字符的地址。在这里第二个参数为 0,表示不需要获取无法转换的字符的地址。
NumVal = strtod(NumStr.c_str(), 0);
return tok_number;
}
// fill comments
if(LastChar == '#'){
// comments until end of line
do
LastChar = getchar();
while(LastChar !=EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
return gettok();
}
if (LastChar == EOF)
return tok_eof;
// otherwise, just return the character as its ascii value
int ThisChar = LastChar;
LastChar = getchar();
return ThisChar;
}
// 2.2. The Abstract Syntax Tree (AST)
// ExprAST- base class
class ExprAST{
public:
virtual ~ExprAST() = default;
//基类中使用 = 0 表示纯虚函数。纯虚函数是在基类中声明但没有提供实现的虚函数。派生类必须提供实现以使基类变得可实例化
virtual Value *codegen() = 0;
};
// NumberExprAST - for numeric literals like "1.0"
class NumberExprAST: public ExprAST{
double Val;
public:
NumberExprAST(double Val) : Val(Val){}
Value *codegen() override;
};
class VariableExprAST: public ExprAST{
std::string Name;
public:
VariableExprAST(const std::string &Name): Name(Name){}
Value *codegen() override;
};
class BinaryExprAST: public ExprAST{ // 2+3
char Op; // + - * \ < >
std::unique_ptr<ExprAST> LHS, RHS;
// smart pointer: c++ doesn't have garbage collector
public:
BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS, std::unique_ptr<ExprAST> RHS):
Op(Op), LHS(move(LHS)), RHS(move(RHS)){}
Value *codegen() override;
};
class CallExprAST: public ExprAST{
std::string Callee;
std::vector<std::unique_ptr<ExprAST>> Args;
public:
CallExprAST(const std::string &Callee, std::vector<std::unique_ptr<ExprAST>> Args):
Callee(Callee), Args(move(Args)){}
Value *codegen() override;
};
//Function Code Generation
class PrototypeAST{ // func(string xxx,)
std::string Name;
std::vector<std::string> Args;
public:
PrototypeAST(const std::string&Name, std::vector<std::string> Args):
Name(Name), Args(move(Args)){}
//getName() 后面加上 const 关键字的原因是为了表明这个成员函数是一个常量成员函数。在C++中,常量成员函数是指那些在函数体内不会修改类的成员变量的成员函数
const std::string &getName() const{
return Name;
}
Function *codegen();
};
class FunctionAST{ // def func(string xxx,){}
std::unique_ptr<PrototypeAST> Proto;
std::unique_ptr<ExprAST> Body;
public:
FunctionAST(std::unique_ptr<PrototypeAST> Proto, std::unique_ptr<ExprAST> Body):
Proto(move(Proto)), Body(move(Body)){}
Function *codegen();
};
// Basic Expression Parsing
// LogError
std::unique_ptr<ExprAST> LogError(const char *Str){
fprintf(stderr, "Error: %s\n", Str);
return nullptr;
}
std::unique_ptr<PrototypeAST> LogErrorP(const char *Str){
LogError(Str);
return nullptr;
}
// CurTok/getNextToken - 提供一个简单的token缓冲区。CurTok是解析器当前正在查看的令牌。
// getNextToken从词法分析器中读取另一个令牌,并使用其结果更新CurTok。
static int CurTok;
static int getNextToken(){
return CurTok = gettok();
}
static std::unique_ptr<ExprAST> ParseExpression();
// 这个例子吃掉了与产生式相对应的所有令牌,并返回词法分析器缓冲区中的下一个令牌
// 这是递归下降解析器的一种相当标准的做法。
// numberexpr ::=number
static std::unique_ptr<ExprAST> ParseNumberExpr(){
auto Result = std::make_unique<NumberExprAST>(NumVal);
getNextToken(); // consume the number
return move(Result);
}
// 括号运算符的定义如下:
// parenexpr ::= '(' expression')'
static std::unique_ptr<ExprAST> ParseParenExpr(){
getNextToken(); // eat (
auto V = ParseExpression();
if (!V)
return nullptr;
if (CurTok != ')')
return LogError("expected ')");
getNextToken(); // eat ')'
return V;
}
// 用于处理变量引用和函数调用
// IdentifierExpr
// :: identifier
// :: identifier '(' expression* ')'
static std::unique_ptr<ExprAST> ParseIdentifierExpr() {
std::string IdName = IdentifierStr;
getNextToken(); // eat identifier
if (CurTok == '('){ // call
getNextToken(); // eat (
std::vector<std::unique_ptr<ExprAST>> Args;
while(true){
auto Arg = ParseExpression();
if (Arg)
Args.push_back(move(Arg));
else
return nullptr;
if (CurTok == ')'){
getNextToken(); // eat )
break;
}else if (CurTok == ','){
getNextToken(); // eat ,
continue;
}else{
return LogError("Expected ')' or ',' in argument list");
}
}
return std::make_unique<CallExprAST>(IdName, move(Args));
}else{ // simple variable ref
return std::make_unique<VariableExprAST>(IdName);
}
}
// 已经把所有简单表达式解析逻辑放到了一起,可以定义一个辅助函数将它们封装到一个入口点中
// primary
static std::unique_ptr<ExprAST> ParsePrimary(){
switch(CurTok){
default:
return LogError("unknown token when expecting an expression");
case tok_identifier:
return ParseIdentifierExpr();
case tok_number:
return ParseNumberExpr();
case '(':
return ParseParenExpr();
}
}
// Binary Expression Parsing
// 2+2 I +( 2*3 )
static int GetTokPrecedence(){
switch(CurTok){
case '<':
case '>':
return 10;
case '+':
case '-':
return 20;
case '*':
case '/':
return 40;
default:
return -1;
}
}
// binoprhs
// :: = ( + primary ) *
static std::unique_ptr<ExprAST> ParseBinOpRHS(
int ExprPrec, std::unique_ptr<ExprAST> LHS){
while(true){
int TokPrec = GetTokPrecedence(); // binop prec
if (TokPrec < ExprPrec){
return LHS;
}else {
int BinOp = CurTok;
getNextToken(); // eat binop
auto RHS = ParsePrimary();
if (RHS){
int NextPrec = GetTokPrecedence();
if(TokPrec < NextPrec){ // + VS *
// ExprPrec is (TokPrec + RHS_Prec)
RHS = ParseBinOpRHS(TokPrec+1, move(RHS));
if (!RHS)
return nullptr;
}
LHS = std::make_unique<BinaryExprAST>(BinOp, move(LHS), move(RHS));
} else {
return nullptr;
}
}
}
}
// expression
static std::unique_ptr<ExprAST> ParseExpression(){
auto LHS = ParsePrimary();
if (LHS)
return ParseBinOpRHS(0, move(LHS));
else
return nullptr;
}
// parse the rest
//prototype
// foobar(n m)
static std::unique_ptr<PrototypeAST> ParsePrototype(){
if (CurTok != tok_identifier)
return LogErrorP("Expected function name in prototype");
std::string FnName = IdentifierStr;
getNextToken();
if (CurTok != '(')
return LogErrorP("Expected '(' in prototype");
// read args
std::vector<std::string> ArgNames;
while (getNextToken() == tok_identifier)
ArgNames.push_back(IdentifierStr);
if (CurTok != ')')
return LogErrorP("Expected ')' in prototype");
getNextToken();
return std::make_unique<PrototypeAST>(FnName, move(ArgNames));
}
// parse definition, body
static std::unique_ptr<FunctionAST> ParseDefinition(){
getNextToken();
auto Proto = ParsePrototype();
if (!Proto)
return nullptr;
if (auto E = ParseExpression())
return std::make_unique<FunctionAST>(move(Proto), move(E));
return nullptr;
}
// extern prototype
static std::unique_ptr<PrototypeAST> ParseExtern(){
getNextToken(); // eat extern
return ParsePrototype();
}
//topevelexpr expression
static std::unique_ptr<FunctionAST> ParseTopLevelExpr(){
if (auto E = ParseExpression()){
// make an anonymous proto
auto Proto = std::make_unique<PrototypeAST>("__anon_expr", std::vector<std::string>());
return std::make_unique<FunctionAST>(move(Proto), move(E));
}
return nullptr;
}
//===----------------------------------------------------------------------===//
// Code Generation
//===----------------------------------------------------------------------===//
Value *LogErrorV(const char *Str){
LogError(Str);
return nullptr;
}
Value *NumberExprAST::codegen(){
return ConstantFP::get(*TheContext, APFloat(Val));
}
Value *VariableExprAST::codegen(){
// look this variable up in the function
Value *V = NamedValues[Name];
std::cout<<"VariableExprAST::codegen - Name: "<<Name<<std::endl;
if (!V)
LogErrorV("Unknown variable name");
return V;
}
Value *BinaryExprAST::codegen(){
Value *L = LHS->codegen();
Value *R = RHS->codegen();
if (!L || !R)
return nullptr;
switch(Op){
case '+':
return Builder->CreateFAdd(L, R, "addtmp");
case '-':
return Builder->CreateFAdd(L, R, "subtmp");
case '*':
return Builder->CreateFMul(L, R, "multmp");
case '<':
L = Builder->CreateFCmpULT(L, R, "cmptmp");
return Builder->CreateUIToFP(L, Type::getDoubleTy(*TheContext), "booltmp");
default:
return LogErrorV("invalid binary operator");
}
}
Value *CallExprAST::codegen(){
// look up the name in the global module table
Function *CalleeF = TheModule->getFunction(Callee);
if (!CalleeF)
return LogErrorV("Unknown function referenced");
// if argument mismatch error
if (CalleeF->arg_size() != Args.size())
return LogErrorV("Incorrect # arguments passed");
std::vector<Value *> ArgsV;
for (unsigned i=0, e=Args.size();i!=e;++i){
ArgsV.push_back(Args[i]->codegen());
if (!ArgsV.back()) // the last element
return nullptr;
}
return Builder->CreateCall(CalleeF, ArgsV, "calltmp");
}
Function *PrototypeAST::codegen(){
// make the function type: double(double, double) etc.
std::vector<Type *> Doubles(Args.size(),Type::getDoubleTy(*TheContext));
FunctionType *FT = FunctionType::get(Type::getDoubleTy(*TheContext), Doubles, false);
Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule.get());
//Set names for all arguments
unsigned Idx = 0;
for (auto &Arg: F->args())
Arg.setName(Args[Idx++]);
return F;
}
Function *FunctionAST::codegen(){
//first, check for an existing function from a previous 'extern' declaration
Function *TheFunction = TheModule->getFunction(Proto->getName());
if (!TheFunction)
TheFunction = Proto->codegen();
if (!TheFunction)
return nullptr;
if (!TheFunction->empty())
return (Function*)LogErrorV("Function cannot be redefined");
// create a new bb to start insertion into
BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", TheFunction);
Builder->SetInsertPoint(BB);
// record the function arguments in the NamedValues map
NamedValues.clear();
for (auto &Arg: TheFunction->args())
NamedValues[std::string(Arg.getName())] = &Arg;
Value *RetVal = Body->codegen();
if (RetVal){
//finish off the function
Builder->CreateRet(RetVal);
//velidate the generated code, checking for consistency
verifyFunction(*TheFunction);
return TheFunction;
}
//error reading body ,remove funciton
TheFunction->eraseFromParent();
return nullptr;
}
// top-level parsing and jit driver
static void InitializeModule(){
//Open a new context and module
TheContext = std::make_unique<LLVMContext>();
TheModule = std::make_unique<Module>("my cool jit", *TheContext);
//create a new builder for the module
Builder = std::make_unique<IRBuilder<>>(*TheContext);
}
static void HandleDefinition(){
if(auto FnAST = ParseDefinition()){
if (auto *FnIR = FnAST->codegen()){
fprintf(stderr, "Read function definition: ");
FnIR->print(errs());
fprintf(stderr, "\n");
}
}
else //skip token for error recovery
getNextToken();
}
static void HandleExtern(){
if (auto ProtoAST = ParseExtern()){
if (auto *FnIR = ProtoAST->codegen()){
fprintf(stderr, "Read extern: ");
FnIR->print(errs());
fprintf(stderr, "\n");
}
}
else //skip token for error recovery
getNextToken();
}
static void HandleTopLevelExpression(){
// evaluate a top-level expression into an anonymous function
if (auto FnAST = ParseTopLevelExpr()){
if (auto *FnIR = FnAST->codegen()){
fprintf(stderr, "Read top-level expression: ");
FnIR->print(errs());
fprintf(stderr, "\n");
// remove token for error recovery
FnIR->eraseFromParent();
}
}
else
getNextToken();
}
// the driver
static void MainLoop(){
while(true){
fprintf(stderr, "read> ");
switch(CurTok){
case tok_eof:
return;
case ';': // ignore top-level semicolons
getNextToken();
break;
case tok_def:
HandleDefinition();
break;
case tok_extern:
HandleExtern();
break;
default:
HandleTopLevelExpression();
break;
}
}
}
//Expression Code Generation
// int main(){
// while(true){
// int tok = gettok();
// cout << "got token" << tok <<endl;
// }
// }
int main(){
fprintf(stderr, "ready> ");
InitializeModule();
getNextToken();
MainLoop();
// Print out all of the generated code.
TheModule->print(errs(), nullptr);
return 0;
}