The IR module is the brace around all elements we generate for a compilation unit. At the global level, we iterate through the declarations at the module level, create global variables, and call the code generation for procedures. A global variable in tinylang is mapped to an instance of the llvm::GobalValue class. This mapping is saved in Globals and made available to the code generation for procedures:
void CGModule::run(ModuleDeclaration *Mod) {
  for (auto *Decl : Mod->getDecls()) {
    if (auto *Var =
            llvm::dyn_cast<VariableDeclaration>(Decl)) {
      // Create global variables
      auto *V = new llvm::GlobalVariable(
          *M, convertType(Var->getType()),
          /*isConstant=*/false,
          llvm::GlobalValue::PrivateLinkage, nullptr,
          mangleName(Var));
      Globals[Var] = V;
    } else if (auto *Proc =
                   llvm::dyn_cast<ProcedureDeclaration>(
                       Decl)) {
      CGProcedure CGP(*this);
      CGP.run(Proc);
    }
  }
}

The module also holds the LLVMContext class and caches the most commonly used LLVM types. The latter ones need to be initialized, for example, for the 64-bit integer type:
Int64Ty = llvm::Type::getInt64Ty(getLLVMCtx());

The CodeGenerator class initializes the LLVM IR module and calls the code generation for the module. Most importantly, this class must know for which target architecture we’d like to generate code. This information is passed in the llvm::TargetMachine class, which is set up in the driver:
std::unique_ptr<llvm::Module>
CodeGenerator::run(ModuleDeclaration *Mod,
                   std::string FileName) {
  std::unique_ptr<llvm::Module> M =
      std::make_unique<llvm::Module>(FileName, Ctx);
  M->setTargetTriple(TM->getTargetTriple().getTriple());
  M->setDataLayout(TM->createDataLayout());
  CGModule CGM(M.get());
  CGM.run(Mod);
  return M;
}

For ease of use, we must also introduce a factory method for the code generator:
CodeGenerator *
CodeGenerator::create(llvm::LLVMContext &Ctx,
                      llvm::TargetMachine *TM) {
  return new CodeGenerator(Ctx, TM);
}

The CodeGenerator class provides a small interface to create IR code, which is ideal for use in the compiler driver. Before we integrate it, we need to implement the support for machine code generation.

Leave a Reply

Your email address will not be published. Required fields are marked *