Generating IR code for functions

In this recipe you, will learn how to generate IR code for a function.

How to do it…

Do the following steps:

  1. The Codegen() function for the function call can be defined as follows:
    Value *FunctionCallAST::Codegen() {
      Function *CalleeF =
      Module_Ob->getFunction(Function_Callee);
      std::vector<Value*>ArgsV;
      for(unsigned i = 0, e = Function_Arguments.size();
      i != e; ++i) {
        ArgsV.push_back(Function_Arguments[i]->Codegen());
        if(ArgsV.back() == 0) return 0;
      }
      return Builder.CreateCall(CalleeF, ArgsV, "calltmp");
    }

    Once we have the function to call, we recursively call the Codegen() function for each argument that is to be passed in and create an LLVM call instruction.

  2. Now that the Codegen() function for a function call has been defined, it's time to define the Codegen() functions for declarations and function definitions.

    The Codegen() function for function declarations can be defined as follows:

    Function *FunctionDeclAST::Codegen() {
      std::vector<Type*>Integers(Arguments.size(), Type::getInt32Ty(getGlobalContext()));
      FunctionType *FT = FunctionType::get(Type::getInt32Ty(getGlobalContext()), Integers, false);
      Function *F = Function::Create(FT,  Function::ExternalLinkage, Func_Name, Module_Ob);
      
      if(F->getName() != Func_Name) {
        F->eraseFromParent();
        F = Module_Ob->getFunction(Func_Name);
        
        if(!F->empty()) return 0;
        
        if(F->arg_size() != Arguments.size()) return 0;
        
      }
      
      unsigned Idx = 0;
      for(Function::arg_iterator Arg_It = F->arg_begin(); Idx != Arguments.size(); ++Arg_It, ++Idx) {
        Arg_It->setName(Arguments[Idx]);
        Named_Values[Arguments[Idx]] = Arg_It;
      }
      
      return F;
    }

    The Codegen() function for function definition can be defined as follows:

    Function *FunctionDefnAST::Codegen() {
      Named_Values.clear();
      
      Function *TheFunction = Func_Decl->Codegen();
      if(TheFunction == 0) return 0;
      
      BasicBlock *BB = BasicBlock::Create(getGlobalContext(),"entry", TheFunction);
      Builder.SetInsertPoint(BB);
      
      if(Value *RetVal = Body->Codegen()) {
        Builder.CreateRet(RetVal);
        verifyFunction(*TheFunction);
        return TheFunction;
      }
      
      TheFunction->eraseFromParent();
      return 0;
    }
  3. That's it! LLVMIR is now ready. These Codegen() functions can be called in the wrappers written to parse top-level expressions as follows:
    static void HandleDefn() {
      if (FunctionDefnAST *F = func_defn_parser()) {
        if(Function* LF = F->Codegen()) {
        }
      }
      else {
        next_token();
      }
    }
    static void HandleTopExpression() {
      if(FunctionDefnAST *F = top_level_parser()) {
        if(Function *LF = F->Codegen()) {
        }
      }
      else {
        next_token();
      }
    }

    So, after parsing successfully, the respective Codegen() functions are called to generate the LLVM IR. The dump() function is called to print the generated IR.

How it works…

The Codegen() functions use LLVM inbuilt function calls to generate IR. The header files to include for this purpose are llvm/IR/Verifier.h, llvm/IR/DerivedTypes.h, llvm/IR/IRBuilder.h, and llvm/IR/LLVMContext.h, llvm/IR/Module.h.

  1. While compiling, this code needs to be linked with LLVM libraries. For this purpose, the llvm-config tool can be used as follows:
    llvm-config  --cxxflags  --ldflags  --system-libs  --libs core.
  2. For this purpose, the toy program is recompiled with additional flags as follows:
    $ clang++ -O3 toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o toy
    
  3. When the toy compiler is now run on example code, it will generate LLVM IR as follows:
    $ ./toy example
    
    define i32 @foo (i32 %x, i32 %y) {
      entry:
      %multmp = muli32 %y, 16
      %addtmp = add i32 %x, %multmp
      reti32 %addtmp
    }

    Another example2 file has a function call.$ cat example2:

    foo(5, 6);

    Its LLVM IR will be dumped as follows:

    $ ./toy example2
    define i32 @1 () {
      entry:
      %calltmp = call i32@foo(i32 5, i32 6)
      reti32 %calltmp
    }

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.216.75