I am working on a custom front-end language using the LLVM c++ api and am adding a "class" definition construct to the language. The lexer and parser are written in C++ and LLVM c++ api is being used to generate IR code and compile to different machine backends. I can already define functions and variables, call functions and evaluate basic arithmetic with the custom front-end language.
In order to do add a "class" definition to the language, I am following the suggestion on https://mapping-high-level-constructs-to-llvm-ir.readthedocs.io/en/latest/object-oriented-constructs/classes.html
This suggests that we create a "structtype" to contain all the data members of the class and separately define a bunch of functions for the methods of the class (if I understand the link correctly).
Thus in my compiler code, I defined an AST node called "ClassAST" which contains the names and types for data members of the class and a collection of function definitions for the methods for the "class" being defined in the front-end language. The c++ code snippet for the AST node is as follows:
class ClassAST {
public:
std::vector<std::unique_ptr<FunctionAST>> function_members;
std::vector<std::unique_ptr<ExprAST>> data_members;
std::unique_ptr<PrototypeAST> Proto;
ClassAST(std::unique_ptr<PrototypeAST> Proto,std::vector<std::unique_ptr<FunctionAST>> function_members,
std::vector<std::unique_ptr<ExprAST>> data_members)
: Proto(std::move(Proto)),function_members(std::move(function_members)), data_members(std::move(data_members)) {}
llvm::Type *codegen(IRgen *irgen);
}
In the snippet, FunctionAST, ExprAST, PrototypeAST are other AST node types I have defined in order to represent functions, expressions and function prototypes respectively and are used for IR code generation of the same. "IRgen" is a class I defined to contain the llvm::Context, llvm::Module, llvm::IRBuilder instances for my compiler.
Now, to generate the IR code for the ClassAST node I define the "codegen", function as
llvm::Type *ClassAST::codegen(IRgen *irgen){
// create a struct type with all the data_members
llvm::StructType *class_StructType = llvm::StructType::create(irgen->TheContext);
class_StructType->setName(Proto->Name);
std::vector<llvm::Type *> DataTypes;
for(int i=0;i<data_members.size();i++){
DataTypes.push_back(llvm::Type::getDoubleTy(irgen->TheContext)); // assume all data types are doubles for now
}
class_StructType->setBody(DataTypes);
// add the type to the symbol table (How to do this?)
// .. ????? ..
// codegen the function members
for (int i=0;i<function_members.size();i++){
auto RetVal = function_members[i]->codegen(irgen);
if(!RetVal){
// Error reading body, remove function.
LogErrorV(("Error generating code for "+Proto->Name+"."+function_members[i]->Proto->Name).c_str());
}
}
return class_StructType;
}
The above codegen(..) function successfully creates the class_StructType to contain the appropriate data_member types and generates the IR code for the defined 'function_members' and adds the function definitions to the llvm::Module instance in 'irgen'.
However, I have no idea how to add the class_StructType type to the llvm::Module instance, so that later code can retrieve the type and create instances of the class_StructType.
Could someone shed some light on how a new StructType is added to a llvm:Module instance? (I am using the latest llvm 12.0 api, but even older api 11.0 should be fine).
Looking at the source code of Module::getTypeByName
, it looks up the name in the LLVMContext
instance. This means you don't have to add your type to the module and name lookup should work just fine, even through a Module
instance.