Adding debug support to tinylang – Advanced IR Generation-1


We encapsulate the generation of debug metadata in the new CGDebugInfo class. Additionally, we place the declaration in the tinylang/CodeGen/CGDebugInfo.h header file and the definition in the tinylang/CodeGen/CGDebugInfo.cpp file.
The CGDebugInfo class has five important members. We need a reference to the code generator for the module, CGM, because we need to convert types from AST representation into LLVM types. Of course, we also need an instance of the llvm::DIBuilder class called Dbuilder, as we did in the previous sections. A pointer to the instance of the compile unit is also needed; we store it in the CU member.
To avoid having to create the debug metadata for types again, we must also add a map to cache this information. The member is called TypeCache. Finally, we need a way to manage the scope information, for which we must create a stack based on the llvm::SmallVector<> class called ScopeStack. Thus, we have the following:

CGModule &CGM;
llvm::DIBuilder DBuilder;
llvm::DICompileUnit *CU;
llvm::DenseMap
TypeCache;
llvm::SmallVector ScopeStack;

The following methods of the CGDebugInfo class make use of these members:

  1. First, we need to create the compile unit, which we do in the constructor. We also create the file containing the compile unit here. Later, we can refer to the file through the CU member. The code for the constructor is as follows:

CGDebugInfo::CGDebugInfo(CGModule &CGM)
: CGM(CGM), DBuilder(*CGM.getModule()) {
llvm::SmallString<128> Path(
CGM.getASTCtx().getFilename());
llvm::sys::fs::make_absolute(Path);
llvm::DIFile *File = DBuilder.createFile(
llvm::sys::path::filename(Path),
llvm::sys::path::parent_path(Path));
bool IsOptimized = false;
llvm::StringRef CUFlags;
unsigned ObjCRunTimeVersion = 0;
llvm::StringRef SplitName;
llvm::DICompileUnit::DebugEmissionKind EmissionKind =
llvm::DICompileUnit::DebugEmissionKind::FullDebug;
CU = DBuilder.createCompileUnit(
llvm::dwarf::DW_LANG_Modula2, File, “tinylang”,
IsOptimized, CUFlags, ObjCRunTimeVersion,
SplitName, EmissionKind);
}

  1. Often, we need to provide a line number. The line number can be derived from the source manager location, which is available in most AST nodes. The source manager can convert this into a line number:

unsigned CGDebugInfo::getLineNumber(SMLoc Loc) {
return CGM.getASTCtx().getSourceMgr().FindLineNumber(
Loc);
}

  1. The information about a scope is held on a stack. We need methods to open and close a scope and retrieve the current scope. The compilation unit is the global scope, which we add automatically:

llvm::DIScope *CGDebugInfo::getScope() {
if (ScopeStack.empty())
openScope(CU->getFile());
return ScopeStack.back();
}
void CGDebugInfo::openScope(llvm::DIScope *Scope) {
ScopeStack.push_back(Scope);
}
void CGDebugInfo::closeScope() {
ScopeStack.pop_back();
}

  1. Next, we must create a method for each category of type we need to transform. The getPervasiveType() method creates the debug metadata for basic types. Note the use of the encoding parameter, which declares the INTEGER type as a signed type and the BOOLEAN type encoded as a Boolean:

llvm::DIType *
CGDebugInfo::getPervasiveType(TypeDeclaration *Ty) {
if (Ty->getName() == “INTEGER”) {
return DBuilder.createBasicType(
Ty->getName(), 64, llvm::dwarf::DW_ATE_signed);
}
if (Ty->getName() == “BOOLEAN”) {
return DBuilder.createBasicType(
Ty->getName(), 1, llvm::dwarf::DW_ATE_boolean);
}
llvm::report_fatal_error(
“Unsupported pervasive type”);
}

Leave a Reply

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