Adding TBAA metadata to tinylang – Advanced IR Generation-2
- To return the metadata for a tinylang type, we need to create the type hierarchy. Due to the type system of tinylang being very restricted, we can use a simple approach. Each scalar type is mapped to a unique type attached to the root node, and we map all pointers to a single type. Structured types then refer to these nodes. If we cannot map a type, then we return nullptr:
llvm::MDNode *CGTBAA::getTypeInfo(TypeDeclaration *Ty) {
if (llvm::MDNode *N = MetadataCache[Ty])
return N;
if (auto *Pervasive =
llvm::dyn_cast(Ty)) {
StringRef Name = Pervasive->getName();
return createScalarTypeNode(Pervasive, Name, getRoot());
}
if (auto *Pointer =
llvm::dyn_cast(Ty)) {
StringRef Name = “any pointer”;
return createScalarTypeNode(Pointer, Name, getRoot());
}
if (auto *Array =
llvm::dyn_cast(Ty)) {
StringRef Name = Array->getType()->getName();
return createScalarTypeNode(Array, Name, getRoot());
}
if (auto *Record =
llvm::dyn_cast(Ty)) {
llvm::SmallVector, 4> Fields;
auto *Rec =
llvm::cast(CGM.convertType(Record));
const llvm::StructLayout *Layout =
CGM.getModule()->getDataLayout().getStructLayout(Rec);
unsigned Idx = 0;
for (const auto &F : Record->getFields()) {
uint64_t Offset = Layout->getElementOffset(Idx);
Fields.emplace_back(getTypeInfo(F.getType()), Offset);
++Idx;
}
StringRef Name = CGM.mangleName(Record);
return createStructTypeNode(Record, Name, Fields);
}
return nullptr;
}
- The general method to get the metadata is getAccessTagInfo(). To get the TBAA access tag information, a call to the getTypeInfo() function must be added. This function expects TypeDeclaration as its parameter, which is retrieved from the instructions we want to produce metadata for:
llvm::MDNode *CGTBAA::getAccessTagInfo(TypeDeclaration *Ty) {
return getTypeInfo(Ty);
}
Finally, to enable the generation of TBAA metadata, we simply need to attach the metadata to all of the load and store instructions that we generate within tinylang.
For example, in CGProcedure::writeVariable(), a store to a global variable uses a store instruction:
Builder.CreateStore(Val, CGM.getGlobal(D));
To decorate this particular instruction, we need to replace this line with the following lines, where decorateInst() adds the TBAA metadata to this store instruction:
auto *Inst = Builder.CreateStore(Val, CGM.getGlobal(D));
// NOTE: V is of the VariableDeclaration class, and
// the getType() method in this class retrieves the
// TypeDeclaration that is needed for decorateInst().
CGM.decorateInst(Inst, V->getType());
With these changes in place, we have finished generating the TBAA metadata.
We can now compile a sample tinylang file into an LLVM intermediate representation to see our newly implemented TBAA metadata. For instance, consider the following file, Person.mod:
MODULE Person;
TYPE
Person = RECORD
Height: INTEGER;
Age: INTEGER
END;
PROCEDURE Set(VAR p: Person);
BEGIN
p.Age := 18;
END Set;
END Person.
The tinylang compiler that is built in the build directory of this chapter can be used to generate the intermediate representation for this file:
$ tools/driver/tinylang -emit-llvm ../examples/Person.mod
In the newly generated Person.ll file, we can see that the store instruction is decorated with the TBAA metadata that we have generated within this chapter, where the metadata reflects the fields of the record type that was originally declared:
; ModuleID = ‘../examples/Person.mod’
source_filename = “../examples/Person.mod”
target datalayout = “e-m:o-i64:64-i128:128-n32:64-S128”
target triple = “arm64-apple-darwin22.6.0”
define void @_t6Person3Set(ptr nocapture dereferenceable(16) %p) {
entry:
%0 = getelementptr inbounds ptr, ptr %p, i32 0, i32 1
store i64 18, ptr %0, align 8, !tbaa !0
ret void
}
!0 = !{!”_t6Person6Person”, !1, i64 0, !1, i64 8}
!1 = !{!”INTEGER”, !2, i64 0}
!2 = !{!”Simple tinylang TBAA”}
Now that we have learned how to generate TBAA metadata, we will explore a very similar topic in the next section: generating debug metadata.