Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save gopi487krishna/7a434d4fa68cab380ed490c47342136d to your computer and use it in GitHub Desktop.
Save gopi487krishna/7a434d4fa68cab380ed490c47342136d to your computer and use it in GitHub Desktop.
[Adding Attributes to LLVM] #llvm

Introduction

This post describes how to add a custom attribute to LLVM and Clang. Why would you want to do such a thing?

  • You have semantic information of which the front-end is aware, but the back-end discards in the Intermediate Representation (IR), and an existing attribute can't be used to retain this information. Adding the attribute using the front-end analysis preserves the information into the back-end generated IR.
  • You've considered using the GCC/LLVM annotate attribute to hold arbitrary strings, but you also need to add a parameter (or more!) to that annotation.

The Clang Internals Manual discusses how to do this, but not with the detail you might like to see. Its description is high-level, and only lists one file that needs to be modified. Tracking down all of the other files that must be changed is left as a frustrating exercise to the reader.

Conventions used in this gist

We will be creating a new function attribute appropriately named newattribute. These steps may be different if you want to make an attribute type other than a Function attribute. This is beyond the scope of this post.

In this post, we'll use the following variables as shorthand for some of the paths specified in Getting Started with the LLVM System.

Variable Description
$(LLVM_SRC) The directory containing the LLVM sources. It will be llvm-project/llvm under both the monorepo and nested repo.
$(CLANG_SRC) The directory containing the Clang sources. It will be llvm-project/clang under the monorepo, and llvm-project/llvm/tools/clang under the nested repo.

General How-to Tips

  • Read the Clang Internals Manual for technical details of adding an attribute.
  • Look at code for an existing attribute that functions similarly and imitate it. Use grep -R <attribute> over the $(LLVM_SRC) and $(CLANG_SRC) directories to find every file in which the existing attribute has a presence. Careful: there's a lot of them!

Changes

Broadly:

  1. Build the entire LLVM project.
  2. Complete changes to the Clang Front End.
  3. Complete changes to the LLVM Back End.
  4. Rebuild the entire LLVM project using your build system (e.g. make all, cmake --build ., etc.)
  • $(CLANG_SRC)/include/clang/Basic/Attr.td Add the new front-end attribute definition.
  • $(CLANG_SRC)/lib/AST/ASTContext.cpp Add your function attribute to ASTContext::DeclMustBeEmitted(). This helps ensure that your attribute is not discarded in the translation from the front end's AST to the back end's IR.
  • $(CLANG_SRC)/lib/CodeGen/CodeGenModule.cpp Add your attribute to the AttributeBuilder so that it gets emitted to the IR.
  • $(CLANG_SRC)/lib/Sema/SemaDeclAttr.cpp This file implements llvm::Decl processing for attributes. There are two changes.
    • Note that each attribute has a static handler function (e.g. static void handleSectionAttr(...)) and optionally other helper functions (e.g. SectionAttr *Sema::mergeSectionAttr(...)) where the attributes may be processed before they are handled. We only need to write a handler function for our new attribute, and do so after all other handlers.
    • Add a case statement to the function which applies your attribute to the specified declaration.
  • $(LLVM_SRC)/lib/Bitcode/Reader/BitcodeReader.cpp There are two changes here:
    • Get the attribute kind mask. This was previously done in $(LLVM_SRC)/include/llvm/Bitcode/LLVMBitCodes.h
    • Provide a case statement for Bitcode decoding.
  • $(LLVM_SRC)/lib/Bitcode/Writer/BitcodeWriter.cpp
  • $(LLVM_SRC)/lib/IR/Attributes.cpp This file implements attributes, including AttrBuilder and AttributeList, both of which we use in this tutorial. Add a case to return the string for our new attribute, which is used for emitting human-readable IR.
  • $(LLVM_SRC)/include/llvm/Bitcode/LLVMBitCodes.h
  • $(LLVM_SRC)/include/llvm/IR/Attributes.td
  • $(LLVM_SRC)/lib/IR/Verifier.cpp
//===- ASTContext.cpp - Context to hold long-lived AST nodes --------------===//
/* snip */
bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (const auto *VD = dyn_cast<VarDecl>(D)) {
/* snip */
//ADDED add your else-if case here for the new attribute
else if (FD->hasAttr<NewAttributeAttr>())
return true;
/* snip */
}
}
/* snip */
//==--- Attr.td - attribute definitions -----------------------------------===//
/* snip */
//
// Attributes begin here
//
/* snip */
// ADDED: NewAttribute definition
def NewAttribute : InheritableAttr {
let Spellings = [GNU<"newattribute">];
let Subjects = SubjectList<[Function]>;
let Documentation = [Undocumented];
}
//===- Attributes.cpp - Implement AttributesList --------------------------===//
/* snip */
std::string Attribute::getAsString(bool InAttrGrp) const {
/* snip */
if (hasAttribute(Attribute::Cold))
return "cold";
//ADDED: case for new attribute
if (hasAttribute(Attribute::NewAttribute))
return "newattribute";
/* snip */
}
/* snip */
//===- BitcodeReader.cpp - Internal BitcodeReader implementation ----------===//
/* snip */
//===----------------------------------------------------------------------===//
// Functions for parsing blocks from the bitcode file
//===----------------------------------------------------------------------===//
static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
switch (Val) {
/* snip */
case Attribute::ShadowCallStack: return 1ULL << 59;
case Attribute::SpeculativeLoadHardening: return 1ULL << 60;
//ADDED: case for NewAttribute
//Note that you must not reuse any attribute masks,
//so it helps to put it at the very end of the case statements.
case Attribute::NewAttribute: return 1ULL << 61;
/* snip */
}
llvm_unreachable("Unsupported attribute type");
}
/* snip */
// Returns Attribute::None on unrecognized codes.
static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
switch (Code) {
case bitc::ATTR_KIND_NEW_ATTRIBUTE
```
//===--- CodeGenModule.cpp - Emit LLVM Code from ASTs for a Module --------===//
/* snip */
void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
llvm::Function *F) {
llvm::AttrBuilder B;
/* snip */
//Added give your attribute to the Attribute Builder
if (D-> hasAttr<NewAttributeAttr>())
B.addAttribute(llvm::Attribute::NewAttribute);
F->addAttributes(llvm::AttributeList::FunctionIndex, B);
/* snip */
}
/* snip */
//===--- SemaDeclAttr.cpp - Declaration Attribute Handling ----------------===//
/* snip */
//===----------------------------------------------------------------------===//
// Attribute Implementations
//===----------------------------------------------------------------------===//
/* snip */
static void handleNewAttributeAttr(Sema &S, Decl *D, const ParsedAttr &AL){
D->addAttr(::new (S.Context)
NewAttributeAttr(AL.getRange(), S.Context,AL.getAttributeSpellingListIndex()));
}
/* snip */
//===----------------------------------------------------------------------===//
// Top Level Sema Entry Points
//===----------------------------------------------------------------------===//
/// ProcessDeclAttribute - Apply the specific attribute to the specified decl if
/// the attribute applies to decls. If the attribute is a type attribute, just
/// silently ignore it if a GNU attribute.
static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
const ParsedAttr &AL,
bool IncludeCXX11Attributes) {
/* snip */
switch (AL.getKind()) {
//ADDED: case for handling new attribute.
//Add after all other attribute handling cases. There is no fall-through default case.
case ParsedAttr::AT_NewAttribute:
handleNewAttributeAttr(S,D,AL);
break;
}
}
/* snip */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment