Ieads Definitive Guide to LLVM Backends (I)


Published:
Tags:llvm C++ VM

When writing my first llvm backend at my last job I quickly found out that there is only very material assisting the creation of a custom backend. The posts and tutorials only cover the basics and stop as soon as it starts to become really interesting. This series of posts serves to fill the void left by others and to teach how to customice and tailor different aspects of a backend to your needs.

Setting up the environment

Before we can get startet with writing our very own llvm backend we need to setup a couple of things first. As we are writing a llvm backend the first thing we need is llvm itself. It can be obtained from github (llvm-project). You should checkout a reasonably new stable release. Next you want to run cmake from a separate build directory. I prefer then Ninja build system as parallel builds can easily be restricted when you are tight on RAM and it automatically runs jobs in parallel. For LLVM adding -DLLVM_PARALLEL_LINK_JOBS=2 can help if you run into the oom killer.

git clone -b llvmorg-17.0.6 https://github.com/llvm/llvm-project.git
mkdir llvm-build
cd llvm-build
cmake -DLLVM_ENABLE_PROJECTS="lld;clang" \
      -DLLVM_TARGETS_TO_BUILD="ARM;AArch64;X86" \
      -DLLVM_ENABLE_ASSERTIONS=On \
      -DCMAKE_BUILD_TYPE=Release \
      -G Ninja \
      ../llvm-project/llvm
ninja

After making sure llvm can be build without any modifications create a new directory with the name of your backend in llvm/lib/Target. I called mine LEG, so I created the direcotry llvm/lib/Target/LEG. Next a couple of files need to be created or modified. Please substitute your backend name for LEG in filenames, pathes, and file content.

diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 75f0c960b..4848ae0d9 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -320,6 +320,7 @@ enum {
   EM_VE = 251,            // NEC SX-Aurora VE
   EM_CSKY = 252,          // C-SKY 32-bit processor
   EM_LOONGARCH = 258,     // LoongArch
+  EM_LEG = 512,           // Ieads LEG architecture
 };
 
 // Object file classes.
@@ -945,6 +946,10 @@ enum {
 #include "ELFRelocs/Xtensa.def"
 };
 
+enum {
+#include "ELFRelocs/LEG.def"
+};
+
 #undef ELF_RELOC
 
 // Section header.

diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/LEG.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/LEG.def
new file mode 100644
index 000000000..370237ead
--- /dev/null
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/LEG.def
@@ -0,0 +1,5 @@
+#ifndef ELF_RELOC
+#error "ELF_RELOC must be defined"
+#endif
+
+ELF_RELOC(R_LEG_NONE, 0)
\ No newline at end of file
diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h
index 8baf6f4c5..72d72e5cf 100644
--- a/llvm/include/llvm/Object/ELFObjectFile.h
+++ b/llvm/include/llvm/Object/ELFObjectFile.h
@@ -1247,6 +1247,8 @@ StringRef ELFObjectFile<ELFT>::getFileFormatName() const {
       return "elf64-ve";
     case ELF::EM_LOONGARCH:
       return "elf64-loongarch";
+    case ELF::EM_LEG:
+      return "elf64-leg";
     default:
       return "elf64-unknown";
     }
@@ -1276,6 +1278,8 @@ template <class ELFT> Triple::ArchType ELFObjectFile<ELFT>::getArch() const {
     return Triple::hexagon;
   case ELF::EM_LANAI:
     return Triple::lanai;
+  case ELF::EM_LEG:
+    return Triple::leg;
   case ELF::EM_MIPS:
     switch (EF.getHeader().e_ident[ELF::EI_CLASS]) {
     case ELF::ELFCLASS32:
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 59513fa2f..c077d8e8c 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -90,6 +90,7 @@ public:
     nvptx64,        // NVPTX: 64-bit
     le32,           // le32: generic little-endian 32-bit CPU (PNaCl)
     le64,           // le64: generic little-endian 64-bit CPU (PNaCl)
+    leg,            // Iead's LEG architecture
     amdil,          // AMDIL
     amdil64,        // AMDIL with 64-bit pointers
     hsail,          // AMD HSAIL
diff --git a/llvm/lib/BinaryFormat/ELF.cpp b/llvm/lib/BinaryFormat/ELF.cpp
index dc8f3051a..729cac435 100644
--- a/llvm/lib/BinaryFormat/ELF.cpp
+++ b/llvm/lib/BinaryFormat/ELF.cpp
@@ -198,6 +198,7 @@ uint16_t ELF::convertArchNameToEMachine(StringRef Arch) {
       .Case("ve", EM_VE)
       .Case("csky", EM_CSKY)
       .Case("loongarch", EM_LOONGARCH)
+      .Case("leg", EM_LEG)
       .Default(EM_NONE);
 }
 
@@ -564,6 +565,8 @@ StringRef ELF::convertEMachineToArchName(uint16_t EMachine) {
     return "csky";
   case EM_LOONGARCH:
     return "loongarch";
+  case EM_LEG:
+    return "leg";
   default:
     return "None";
   }
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 81c9a0971..15583f559 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -180,6 +180,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine,
       break;
     }
     break;
+  case ELF::EM_LEG:
+    switch (Type) {
+#include "llvm/BinaryFormat/ELFRelocs/LEG.def"
+    default:
+      break;
+    }
+    break;
   default:
     break;
   }
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index a68035989..f53358628 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -45,6 +45,7 @@ StringRef Triple::getArchTypeName(ArchType Kind) {
   case lanai:          return "lanai";
   case le32:           return "le32";
   case le64:           return "le64";
+  case leg:            return "leg";
   case loongarch32:    return "loongarch32";
   case loongarch64:    return "loongarch64";
   case m68k:           return "m68k";