📜 ⬆️ ⬇️

Creating a programming language using LLVM. Part 8: Compile to Object Code

Table of contents:
Part 1: Introduction and Lexical Analysis
Part 2: Implementing Parser and AST
Part 3: LLVM IR Code Generation
Part 4: Adding JIT and Optimizer Support
Part 5: Language Expansion: Control Flow
Part 6: Language Extension: User Defined Operators
Part 7: Language Expansion: Variable Variables
Part 8: Compile to Object Code
Part 9: Add Debugging Information
Part 10: Conclusion and other goodies LLVM



8.1. Introduction


Welcome to chapter 8 of the guide “Creating a programming language using LLVM”. This chapter describes how to compile programs in our language into object files.

8.2. Target selection


LLVM has native support for cross-compiling. You can compile into the architecture of your current machine, or compile for another architecture. In this tutorial, we will aim the compiler at the current machine.
')
To determine the architecture that will serve the purpose, we use a string called the "target three". It has the form <arch> <sub> - <vendor> - <sys> - <abi> (see the cross-compilation documentation).

As an example, let's see what clang considers our current triple target:

$ clang --version | grep Target Target: x86_64-unknown-linux-gnu 

Running this command on your machine may show something else, because you can use a different architecture or operating system.

Fortunately, we don’t need to hardcode the target three so that it points to the current car. LLVM provides the sys :: getDefaultTargetTriple function, which returns the target three for the current machine.

 auto TargetTriple = sys::getDefaultTargetTriple(); 

LLVM does not require us to use all the functions of the target platform (target). For example, if we use only JIT, we do not need an assembler printer. Similarly, if we target only a specific architecture, we can only use the functionality for this architecture.

In this example, we initialize all the targets for generating the object code.

 InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); 

Now we can use the top three targets to get the Target object:

 std::string Error; auto Target = TargetRegistry::lookupTarget(TargetTriple, Error); //      ,      //   ,     // TargetRegistry      . if (!Target) { errs() << Error; return 1; } 

8.3. Target Machine object


We also need an object of class TargetMachine. This class provides a complete description of the machine we are targeting. If we want to have specific capabilities on the target machine (for example, SSE), or a specific CPU (for example, Intel Sandylake), we must indicate this now.

To see those features and CPUs that LLVM knows about, we can use llc. For example, let's look at x86:

 $ llvm-as < /dev/null | llc -march=x86 -mattr=help 

Available CPUs for this target:

amdfam10
athlon
athlon-4
...

Available options for this target:

16bit-mode - 16-bit mode (i8086).
32bit-mode - 32-bit mode (80386).
3dnow - Allow 3DNow instructions!
3dnowa - Allow 3DNow instructions! Athlon.
...

For our example, we will use a regular CPU without additional features, options, or relocation model.

 auto CPU = "generic"; auto Features = ""; TargetOptions opt; auto RM = Optional<Reloc::Model>(); auto TargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM); 

8.4. Module Object Configuration


Now we are ready to configure the module to determine the target and data model. This is not strictly necessary, but the frontend manual recommends doing so. Better optimization is possible if the target and data model are known.

 TheModule->setDataLayout(TargetMachine->createDataLayout()); TheModule->setTargetTriple(TargetTriple); 

8.5. Object Code Generation


We are ready to generate an object code! Determine where we want to write the file:

 auto Filename = "output.o"; std::error_code EC; raw_fd_ostream dest(Filename, EC, sys::fs::F_None); if (EC) { errs() << "Could not open file: " << EC.message(); return 1; } 

Finally, we define a pass that generates the object code, and run it:

 legacy::PassManager pass; auto FileType = TargetMachine::CGFT_ObjectFile; if (TargetMachine->addPassesToEmitFile(pass, dest, FileType)) { errs() << "TargetMachine can't emit a file of this type"; return 1; } pass.run(*TheModule); dest.flush(); 

8.6. Putting it all together


Does the program work? Let's try. We need to compile our code, but note that the llvm-config arguments are different than in previous chapters.

 $ clang++ -g -O3 toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -o toy 

Let's run, and define a simple averaging function. Press Ctrl-D when it is finished.

 $ ./toy ready> def average(xy) (x + y) * 0.5; ^D 

File output.o recorded
We have an object file! To check it, we will write a simple program and link it to this file. Here is the source code:

 #include <iostream> extern "C" { double average(double, double); } int main() { std::cout << "average of 3.0 and 4.0: " << average(3.0, 4.0) << std::endl; } 

We link our program with output.o and verify that the result is what we expect:

 $ clang++ main.cpp output.o -o main $ ./main average of 3.0 and 4.0: 3.5 

8.7. Full code listing


Full code
 #include "llvm/ADT/APFloat.h" #include "llvm/ADT/Optional.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/Instructions.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include <algorithm> #include <cassert> #include <cctype> #include <cstdio> #include <cstdlib> #include <map> #include <memory> #include <string> #include <system_error> #include <utility> #include <vector> using namespace llvm; using namespace llvm::sys; //===----------------------------------------------------------------------===// //   //===----------------------------------------------------------------------===// //     [0-255]    ,    //  enum Token { tok_eof = -1, //  tok_def = -2, tok_extern = -3, //   tok_identifier = -4, tok_number = -5, //  tok_if = -6, tok_then = -7, tok_else = -8, tok_for = -9, tok_in = -10, //  tok_binary = -11, tok_unary = -12, //   tok_var = -13 }; static std::string IdentifierStr; //   tok_identifier static double NumVal; //   tok_number /// gettok -       static int gettok() { static int LastChar = ' '; //   while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { : [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; if (IdentifierStr == "if") return tok_if; if (IdentifierStr == "then") return tok_then; if (IdentifierStr == "else") return tok_else; if (IdentifierStr == "for") return tok_for; if (IdentifierStr == "in") return tok_in; if (IdentifierStr == "binary") return tok_binary; if (IdentifierStr == "unary") return tok_unary; if (IdentifierStr == "var") return tok_var; return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // : [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), nullptr); return tok_number; } if (LastChar == '#') { //     do LastChar = getchar(); while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); if (LastChar != EOF) return gettok(); } //   .   EOF. if (LastChar == EOF) return tok_eof; // ,     ascii-. int ThisChar = LastChar; LastChar = getchar(); return ThisChar; } //===----------------------------------------------------------------------===// //    ( ) //===----------------------------------------------------------------------===// namespace { /// ExprAST -    . class ExprAST { public: virtual ~ExprAST() = default; virtual Value *codegen() = 0; }; /// NumberExprAST -      "1.0". class NumberExprAST : public ExprAST { double Val; public: NumberExprAST(double Val) : Val(Val) {} Value *codegen() override; }; /// VariableExprAST -   , , "a". class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &Name) : Name(Name) {} Value *codegen() override; const std::string &getName() const { return Name; } }; /// UnaryExprAST -      class UnaryExprAST : public ExprAST { char Opcode; std::unique_ptr<ExprAST> Operand; public: UnaryExprAST(char Opcode, std::unique_ptr<ExprAST> Operand) : Opcode(Opcode), Operand(std::move(Operand)) {} Value *codegen() override; }; /// BinaryExprAST -      class BinaryExprAST : public ExprAST { char Op; std::unique_ptr<ExprAST> LHS, RHS; public: BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS, std::unique_ptr<ExprAST> RHS) : Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {} Value *codegen() override; }; /// CallExprAST -      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(std::move(Args)) {} Value *codegen() override; }; /// IfExprAST -    if/then/else. class IfExprAST : public ExprAST { std::unique_ptr<ExprAST> Cond, Then, Else; public: IfExprAST(std::unique_ptr<ExprAST> Cond, std::unique_ptr<ExprAST> Then, std::unique_ptr<ExprAST> Else) : Cond(std::move(Cond)), Then(std::move(Then)), Else(std::move(Else)) {} Value *codegen() override; }; /// ForExprAST -    for/in. class ForExprAST : public ExprAST { std::string VarName; std::unique_ptr<ExprAST> Start, End, Step, Body; public: ForExprAST(const std::string &VarName, std::unique_ptr<ExprAST> Start, std::unique_ptr<ExprAST> End, std::unique_ptr<ExprAST> Step, std::unique_ptr<ExprAST> Body) : VarName(VarName), Start(std::move(Start)), End(std::move(End)), Step(std::move(Step)), Body(std::move(Body)) {} Value *codegen() override; }; /// VarExprAST -    var/in class VarExprAST : public ExprAST { std::vector<std::pair<std::string, std::unique_ptr<ExprAST>>> VarNames; std::unique_ptr<ExprAST> Body; public: VarExprAST( std::vector<std::pair<std::string, std::unique_ptr<ExprAST>>> VarNames, std::unique_ptr<ExprAST> Body) : VarNames(std::move(VarNames)), Body(std::move(Body)) {} Value *codegen() override; }; /// PrototypeAST -    "" , ///    ,    (, ,  /// ,  ),   . class PrototypeAST { std::string Name; std::vector<std::string> Args; bool IsOperator; unsigned Precedence; // Precedence if a binary op. public: PrototypeAST(const std::string &Name, std::vector<std::string> Args, bool IsOperator = false, unsigned Prec = 0) : Name(Name), Args(std::move(Args)), IsOperator(IsOperator), Precedence(Prec) {} Function *codegen(); const std::string &getName() const { return Name; } bool isUnaryOp() const { return IsOperator && Args.size() == 1; } bool isBinaryOp() const { return IsOperator && Args.size() == 2; } char getOperatorName() const { assert(isUnaryOp() || isBinaryOp()); return Name[Name.size() - 1]; } unsigned getBinaryPrecedence() const { return Precedence; } }; /// FunctionAST -      class FunctionAST { std::unique_ptr<PrototypeAST> Proto; std::unique_ptr<ExprAST> Body; public: FunctionAST(std::unique_ptr<PrototypeAST> Proto, std::unique_ptr<ExprAST> Body) : Proto(std::move(Proto)), Body(std::move(Body)) {} Function *codegen(); }; } //     //===----------------------------------------------------------------------===// //  //===----------------------------------------------------------------------===// /// CurTok/getNextToken -    . CurTok -  /// ,    . getNextToken     ///     CurTok  . static int CurTok; static int getNextToken() { return CurTok = gettok(); } /// BinopPrecedence -     , ///   static std::map<char, int> BinopPrecedence; /// GetTokPrecedence -       . static int GetTokPrecedence() { if (!isascii(CurTok)) return -1; // ,     int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; return TokPrec; } /// Error* -     . 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; } static std::unique_ptr<ExprAST> ParseExpression(); /// numberexpr ::= number static std::unique_ptr<ExprAST> ParseNumberExpr() { auto Result = llvm::make_unique<NumberExprAST>(NumVal); getNextToken(); //   return std::move(Result); } /// parenexpr ::= '(' expression ')' static std::unique_ptr<ExprAST> ParseParenExpr() { getNextToken(); //  (. auto V = ParseExpression(); if (!V) return nullptr; if (CurTok != ')') return LogError("expected ')'"); getNextToken(); //  ). return V; } /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' static std::unique_ptr<ExprAST> ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); // //  . if (CurTok != '(') //     return llvm::make_unique<VariableExprAST>(IdName); //  getNextToken(); //  ( std::vector<std::unique_ptr<ExprAST>> Args; if (CurTok != ')') { while (true) { if (auto Arg = ParseExpression()) Args.push_back(std::move(Arg)); else return nullptr; if (CurTok == ')') break; if (CurTok != ',') return LogError("Expected ')' or ',' in argument list"); getNextToken(); } } //  ')'. getNextToken(); return llvm::make_unique<CallExprAST>(IdName, std::move(Args)); } /// ifexpr ::= 'if' expression 'then' expression 'else' expression static std::unique_ptr<ExprAST> ParseIfExpr() { getNextToken(); //  if. // . auto Cond = ParseExpression(); if (!Cond) return nullptr; if (CurTok != tok_then) return LogError("expected then"); getNextToken(); //  then auto Then = ParseExpression(); if (!Then) return nullptr; if (CurTok != tok_else) return LogError("expected else"); getNextToken(); auto Else = ParseExpression(); if (!Else) return nullptr; return llvm::make_unique<IfExprAST>(std::move(Cond), std::move(Then), std::move(Else)); } /// forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression static std::unique_ptr<ExprAST> ParseForExpr() { getNextToken(); //  for. if (CurTok != tok_identifier) return LogError("expected identifier after for"); std::string IdName = IdentifierStr; getNextToken(); //  . if (CurTok != '=') return LogError("expected '=' after for"); getNextToken(); //  '='. auto Start = ParseExpression(); if (!Start) return nullptr; if (CurTok != ',') return LogError("expected ',' after for start value"); getNextToken(); auto End = ParseExpression(); if (!End) return nullptr; //   . std::unique_ptr<ExprAST> Step; if (CurTok == ',') { getNextToken(); Step = ParseExpression(); if (!Step) return nullptr; } if (CurTok != tok_in) return LogError("expected 'in' after for"); getNextToken(); //  'in'. auto Body = ParseExpression(); if (!Body) return nullptr; return llvm::make_unique<ForExprAST>(IdName, std::move(Start), std::move(End), std::move(Step), std::move(Body)); } /// varexpr ::= 'var' identifier ('=' expression)? // (',' identifier ('=' expression)?)* 'in' expression static std::unique_ptr<ExprAST> ParseVarExpr() { getNextToken(); //  var. std::vector<std::pair<std::string, std::unique_ptr<ExprAST>>> VarNames; //      if (CurTok != tok_identifier) return LogError("expected identifier after var"); while (true) { std::string Name = IdentifierStr; getNextToken(); //  . //    std::unique_ptr<ExprAST> Init = nullptr; if (CurTok == '=') { getNextToken(); //  '='. Init = ParseExpression(); if (!Init) return nullptr; } VarNames.push_back(std::make_pair(Name, std::move(Init))); //   ,   . if (CurTok != ',') break; getNextToken(); //  ','. if (CurTok != tok_identifier) return LogError("expected identifier list after var"); } //        'in'. if (CurTok != tok_in) return LogError("expected 'in' keyword after 'var'"); getNextToken(); //  'in'. auto Body = ParseExpression(); if (!Body) return nullptr; return llvm::make_unique<VarExprAST>(std::move(VarNames), std::move(Body)); } /// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr /// ::= ifexpr /// ::= forexpr /// ::= varexpr 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(); case tok_if: return ParseIfExpr(); case tok_for: return ParseForExpr(); case tok_var: return ParseVarExpr(); } } /// unary /// ::= primary /// ::= '!' unary static std::unique_ptr<ExprAST> ParseUnary() { //      ,     . if (!isascii(CurTok) || CurTok == '(' || CurTok == ',') return ParsePrimary(); //    ,   int Opc = CurTok; getNextToken(); if (auto Operand = ParseUnary()) return llvm::make_unique<UnaryExprAST>(Opc, std::move(Operand)); return nullptr; } /// binoprhs /// ::= ('+' unary)* static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec, std::unique_ptr<ExprAST> LHS) { //    ,    while (true) { int TokPrec = GetTokPrecedence(); //    ,    //  ,   if (TokPrec < ExprPrec) return LHS; //   ,     int BinOp = CurTok; getNextToken(); //    //       auto RHS = ParseUnary(); if (!RHS) return nullptr; //  BinOp    RHS,    RHS,  //    RHS   LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) { RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS)); if (!RHS) return nullptr; } //  LHS/RHS. LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS)); } } /// expression /// ::= unary binoprhs /// static std::unique_ptr<ExprAST> ParseExpression() { auto LHS = ParseUnary(); if (!LHS) return nullptr; return ParseBinOpRHS(0, std::move(LHS)); } /// prototype /// ::= id '(' id* ')' /// ::= binary LETTER number? (id, id) /// ::= unary LETTER (id) static std::unique_ptr<PrototypeAST> ParsePrototype() { std::string FnName; unsigned Kind = 0; // 0 = , 1 = , 2 = . unsigned BinaryPrecedence = 30; switch (CurTok) { default: return LogErrorP("Expected function name in prototype"); case tok_identifier: FnName = IdentifierStr; Kind = 0; getNextToken(); break; case tok_unary: getNextToken(); if (!isascii(CurTok)) return LogErrorP("Expected unary operator"); FnName = "unary"; FnName += (char)CurTok; Kind = 1; getNextToken(); break; case tok_binary: getNextToken(); if (!isascii(CurTok)) return LogErrorP("Expected binary operator"); FnName = "binary"; FnName += (char)CurTok; Kind = 2; getNextToken(); //  ,    if (CurTok == tok_number) { if (NumVal < 1 || NumVal > 100) return LogErrorP("Invalid precedence: must be 1..100"); BinaryPrecedence = (unsigned)NumVal; getNextToken(); } break; } if (CurTok != '(') return LogErrorP("Expected '(' in prototype"); std::vector<std::string> ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return LogErrorP("Expected ')' in prototype"); // . getNextToken(); //  ')'. // ,     if (Kind && ArgNames.size() != Kind) return LogErrorP("Invalid number of operands for operator"); return llvm::make_unique<PrototypeAST>(FnName, ArgNames, Kind != 0, BinaryPrecedence); } /// definition ::= 'def' prototype expression static std::unique_ptr<FunctionAST> ParseDefinition() { getNextToken(); // //  def. auto Proto = ParsePrototype(); if (!Proto) return nullptr; if (auto E = ParseExpression()) return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E)); return nullptr; } /// toplevelexpr ::= expression static std::unique_ptr<FunctionAST> ParseTopLevelExpr() { if (auto E = ParseExpression()) { //    auto Proto = llvm::make_unique<PrototypeAST>("__anon_expr", std::vector<std::string>()); return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E)); } return nullptr; } /// external ::= 'extern' prototype static std::unique_ptr<PrototypeAST> ParseExtern() { getNextToken(); //  extern. return ParsePrototype(); } //===----------------------------------------------------------------------===// //   //===----------------------------------------------------------------------===// static LLVMContext TheContext; static IRBuilder<> Builder(TheContext); static std::unique_ptr<Module> TheModule; static std::map<std::string, AllocaInst *> NamedValues; static std::map<std::string, std::unique_ptr<PrototypeAST>> FunctionProtos; Value *LogErrorV(const char *Str) { LogError(Str); return nullptr; } Function *getFunction(std::string Name) { //  ,       . if (auto *F = TheModule->getFunction(Name)) return F; //  ,         // . auto FI = FunctionProtos.find(Name); if (FI != FunctionProtos.end()) return FI->second->codegen(); //    ,  null. return nullptr; } /// CreateEntryBlockAlloca -   alloca    /// .     . static AllocaInst *CreateEntryBlockAlloca(Function *TheFunction, const std::string &VarName) { IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); return TmpB.CreateAlloca(Type::getDoubleTy(TheContext), nullptr, VarName); } Value *NumberExprAST::codegen() { return ConstantFP::get(TheContext, APFloat(Val)); } Value *VariableExprAST::codegen() { // ,       Value *V = NamedValues[Name]; if (!V) return LogErrorV("Unknown variable name"); //   return Builder.CreateLoad(V, Name.c_str()); } Value *UnaryExprAST::codegen() { Value *OperandV = Operand->codegen(); if (!OperandV) return nullptr; Function *F = getFunction(std::string("unary") + Opcode); if (!F) return LogErrorV("Unknown unary operator"); return Builder.CreateCall(F, OperandV, "unop"); } Value *BinaryExprAST::codegen() { //    '=', ..     LHS   if (Op == '=') { //  ,  LHS   // ,     RTTI, .. LLVM   //  .    LLVM  RTTI     // dynamic_cast    . VariableExprAST *LHSE = static_cast<VariableExprAST *>(LHS.get()); if (!LHSE) return LogErrorV("destination of '=' must be a variable"); //   RHS. Value *Val = RHS->codegen(); if (!Val) return nullptr; //   Value *Variable = NamedValues[LHSE->getName()]; if (!Variable) return LogErrorV("Unknown variable name"); Builder.CreateStore(Val, Variable); return Val; } 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.CreateFSub(L, R, "subtmp"); case '*': return Builder.CreateFMul(L, R, "multmp"); case '<': L = Builder.CreateFCmpULT(L, R, "cmptmp"); //  bool 0/1  double 0.0 or 1.0 return Builder.CreateUIToFP(L, Type::getDoubleTy(TheContext), "booltmp"); default: break; } //      ,     .  //    . Function *F = getFunction(std::string("binary") + Op); assert(F && "binary operator not found!"); Value *Ops[] = {L, R}; return Builder.CreateCall(F, Ops, "binop"); } Value *CallExprAST::codegen() { //       Function *CalleeF = getFunction(Callee); if (!CalleeF) return LogErrorV("Unknown function referenced"); // ,    . 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()) return nullptr; } return Builder.CreateCall(CalleeF, ArgsV, "calltmp"); } Value *IfExprAST::codegen() { Value *CondV = Cond->codegen(); if (!CondV) return nullptr; //          0.0. CondV = Builder.CreateFCmpONE( CondV, ConstantFP::get(TheContext, APFloat(0.0)), "ifcond"); Function *TheFunction = Builder.GetInsertBlock()->getParent(); //    then  else.   'then'  //   BasicBlock *ThenBB = BasicBlock::Create(TheContext, "then", TheFunction); BasicBlock *ElseBB = BasicBlock::Create(TheContext, "else"); BasicBlock *MergeBB = BasicBlock::Create(TheContext, "ifcont"); Builder.CreateCondBr(CondV, ThenBB, ElseBB); //  . Builder.SetInsertPoint(ThenBB); Value *ThenV = Then->codegen(); if (!ThenV) return nullptr; Builder.CreateBr(MergeBB); //    'Then'    ,  ThenBB  PHI. ThenBB = Builder.GetInsertBlock(); //   "else" TheFunction->getBasicBlockList().push_back(ElseBB); Builder.SetInsertPoint(ElseBB); Value *ElseV = Else->codegen(); if (!ElseV) return nullptr; Builder.CreateBr(MergeBB); //    'Else'    ,  ElseBB  PHI. ElseBB = Builder.GetInsertBlock(); //    TheFunction->getBasicBlockList().push_back(MergeBB); Builder.SetInsertPoint(MergeBB); PHINode *PN = Builder.CreatePHI(Type::getDoubleTy(TheContext), 2, "iftmp"); PN->addIncoming(ThenV, ThenBB); PN->addIncoming(ElseV, ElseBB); return PN; } //  for-loop : // var = alloca double // ... // start = startexpr // store start -> var // goto loop // loop: // ... // bodyexpr // ... // loopend: // step = stepexpr // endcond = endexpr // // curvar = load var // nextvar = curvar + step // store nextvar -> var // br endcond, loop, endloop // outloop: Value *ForExprAST::codegen() { Function *TheFunction = Builder.GetInsertBlock()->getParent(); //   alloca      AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); //    ,  'variable'    Value *StartVal = Start->codegen(); if (!StartVal) return nullptr; //    alloca. Builder.CreateStore(StartVal, Alloca); //       ,     // . BasicBlock *LoopBB = BasicBlock::Create(TheContext, "loop", TheFunction); //        LoopBB. Builder.CreateBr(LoopBB); //    LoopBB. Builder.SetInsertPoint(LoopBB); //  ,    PHI-.   //   ,    ,   . AllocaInst *OldVal = NamedValues[VarName]; NamedValues[VarName] = Alloca; //   . ,    ,   //  BB. ,    ,   ,   //  . if (!Body->codegen()) return nullptr; //    Value *StepVal = nullptr; if (Step) { StepVal = Step->codegen(); if (!StepVal) return nullptr; } else { //   ,  1.0. StepVal = ConstantFP::get(TheContext, APFloat(1.0)); } //    Value *EndCond = End->codegen(); if (!EndCond) return nullptr; // , ,   alloca.   //        Value *CurVar = Builder.CreateLoad(Alloca, VarName.c_str()); Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar"); Builder.CreateStore(NextVar, Alloca); //          0.0. EndCond = Builder.CreateFCmpONE( EndCond, ConstantFP::get(TheContext, APFloat(0.0)), "loopcond"); //   " "    BasicBlock *AfterBB = BasicBlock::Create(TheContext, "afterloop", TheFunction); //      LoopEndBB. Builder.CreateCondBr(EndCond, LoopBB, AfterBB); //       AfterBB. Builder.SetInsertPoint(AfterBB); //    . if (OldVal) NamedValues[VarName] = OldVal; else NamedValues.erase(VarName); //    0.0. return Constant::getNullValue(Type::getDoubleTy(TheContext)); } Value *VarExprAST::codegen() { std::vector<AllocaInst *> OldBindings; Function *TheFunction = Builder.GetInsertBlock()->getParent(); //       for (unsigned i = 0, e = VarNames.size(); i != e; ++i) { const std::string &VarName = VarNames[i].first; ExprAST *Init = VarNames[i].second.get(); //     ,   //      ,   // l  : // var a = 1 in // var a = a in ... #    'a'. Value *InitVal; if (Init) { InitVal = Init->codegen(); if (!InitVal) return nullptr; } else { //    ,  0.0. InitVal = ConstantFP::get(TheContext, APFloat(0.0)); } AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); Builder.CreateStore(InitVal, Alloca); //    OldBindings.push_back(NamedValues[VarName]); //   NamedValues[VarName] = Alloca; } //       Value *BodyVal = Body->codegen(); if (!BodyVal) return nullptr; //    for (unsigned i = 0, e = VarNames.size(); i != e; ++i) NamedValues[VarNames[i].first] = OldBindings[i]; //     return BodyVal; } Function *PrototypeAST::codegen() { //   : 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()); //    unsigned Idx = 0; for (auto &Arg : F->args()) Arg.setName(Args[Idx++]); return F; } Function *FunctionAST::codegen() { //      FunctionProtos,   //    . auto &P = *Proto; FunctionProtos[Proto->getName()] = std::move(Proto); Function *TheFunction = getFunction(P.getName()); if (!TheFunction) return nullptr; //   ,   if (P.isBinaryOp()) BinopPrecedence[P.getOperatorName()] = P.getBinaryPrecedence(); //        BasicBlock *BB = BasicBlock::Create(TheContext, "entry", TheFunction); Builder.SetInsertPoint(BB); //      NamedValues. NamedValues.clear(); for (auto &Arg : TheFunction->args()) { //   alloca   AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Arg.getName()); //     alloca. Builder.CreateStore(&Arg, Alloca); //      NamedValues[Arg.getName()] = Alloca; } if (Value *RetVal = Body->codegen()) { //  . Builder.CreateRet(RetVal); //    verifyFunction(*TheFunction); return TheFunction; } //    ,   TheFunction->eraseFromParent(); if (P.isBinaryOp()) BinopPrecedence.erase(P.getOperatorName()); return nullptr; } //===----------------------------------------------------------------------===// //     JIT //===----------------------------------------------------------------------===// static void InitializeModuleAndPassManager() { //    TheModule = llvm::make_unique<Module>("my cool jit", 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 { //       getNextToken(); } } static void HandleExtern() { if (auto ProtoAST = ParseExtern()) { if (auto *FnIR = ProtoAST->codegen()) { fprintf(stderr, "Read extern: "); FnIR->print(errs()); fprintf(stderr, "\n"); FunctionProtos[ProtoAST->getName()] = std::move(ProtoAST); } } else { //       getNextToken(); } } static void HandleTopLevelExpression() { // Evaluate a top-level expression into an anonymous function. if (auto FnAST = ParseTopLevelExpr()) { FnAST->codegen(); } else { //       getNextToken(); } } /// top ::= definition | external | expression | ';' static void MainLoop() { while (true) { switch (CurTok) { case tok_eof: return; case ';': //        getNextToken(); break; case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } } //===----------------------------------------------------------------------===// // "" ,        //===----------------------------------------------------------------------===// #ifdef LLVM_ON_WIN32 #define DLLEXPORT __declspec(dllexport) #else #define DLLEXPORT #endif /// putchard - putchar,  double,  0. extern "C" DLLEXPORT double putchard(double X) { fputc((char)X, stderr); return 0; } /// printd - printf,  double   "%f\n",  0. extern "C" DLLEXPORT double printd(double X) { fprintf(stderr, "%f\n", X); return 0; } //===----------------------------------------------------------------------===// //  main //===----------------------------------------------------------------------===// int main() { //     // 1 -    BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; //  . //    fprintf(stderr, "ready> "); getNextToken(); InitializeModuleAndPassManager(); //    MainLoop(); //   InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); auto TargetTriple = sys::getDefaultTargetTriple(); TheModule->setTargetTriple(TargetTriple); std::string Error; auto Target = TargetRegistry::lookupTarget(TargetTriple, Error); //    ,        //   ,     // TargetRegistry      if (!Target) { errs() << Error; return 1; } auto CPU = "generic"; auto Features = ""; TargetOptions opt; auto RM = Optional<Reloc::Model>(); auto TheTargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM); TheModule->setDataLayout(TheTargetMachine->createDataLayout()); auto Filename = "output.o"; std::error_code EC; raw_fd_ostream dest(Filename, EC, sys::fs::F_None); if (EC) { errs() << "Could not open file: " << EC.message(); return 1; } legacy::PassManager pass; auto FileType = TargetMachine::CGFT_ObjectFile; if (TheTargetMachine->addPassesToEmitFile(pass, dest, FileType)) { errs() << "TheTargetMachine can't emit a file of this type"; return 1; } pass.run(*TheModule); dest.flush(); outs() << "Wrote " << Filename << "\n"; return 0; } 

Source: https://habr.com/ru/post/336876/


All Articles