LLVM Target Registration Minimal Setup

380 阅读2分钟

Assuming the target needed to register is called NT, i.e. New Target.

1. Create a target directory under llvm/lib/Target directory.

mkdir /llvm-project/llvm/lib/Target/NT

2. Let LLVM knows there is a new target directory created by modifying LLVMBuild.txt under llvm/lib/Target directory.

;===- ./lib/Target/LLVMBuild.txt -------------------------------*- Conf -*--===;
...
[common]
subdirectories =
 ...
 XCore
 NT
...

3. Modify llvm/cmake/config-ix.cmake file to let it canonicalize architecture name.

...
if (LLVM_NATIVE_ARCH MATCHES "i[2-6]86")
  set(LLVM_NATIVE_ARCH X86)
...
elseif (LLVM_NATIVE_ARCH MATCHES "NT")
  set(LLVM_NATIVE_ARCH NT)
...
endif ()
...

4. Modify llvm/CMakeLists.txt file to put our NT target into LLVM_ALL_TARGETS variable in order to get NT target built by default.

# List of all targets to be built by default:
set(LLVM_ALL_TARGETS
  ...
  NT
  ...)

5. Modify llvm/include/llvm/ADT/Triple.h and llvm/lib/Support/Triple.cpp file for Target Triple.

/*Triple.h*/
...
#undef mips
#undef NT
...
  enum ArchType {
    ...
    NT, 
    ...};
...

/*Triple.cpp*/
const char *Triple::getArchTypeName(ArchType Kind) {
  switch (Kind) {
  ...
  case NT:        return "NT";
  ...}
}
...
const char *Triple::getArchTypePrefix(ArchType Kind) {
  switch (Kind) {
  ...
  case NT:      return "NT";
  ...}
...
Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) {
  return StringSwitch<Triple::ArchType>(Name)
    ...
    .Case("NT", NT)
    ...
}
...
static Triple::ArchType parseArch(StringRef ArchName) {
  return StringSwitch<Triple::ArchType>(ArchName)
    ...
    .Cases("NT", Triple::NT)
    ...
}
...
static Triple::ObjectFormatType getDefaultFormat(const Triple &T) {
  ...
  case Triple::NT:
  ...
}
...
static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) {
  switch (Arch) {
  ...
  case llvm::Triple::NT:
  ...
    return 32;
  }
}
...
Triple Triple::get32BitArchVariant() const {
  ...
  switch (getArch()) {
  ...
  case Triple::NT:
  ...
    // Already 32-bit.
    break;
  }
  return T;
}

6. Under our target directory llvm/lib/Target/NT, make NT header,  TargetMachine, TargetInfo, and MCTargetDesc.

//===-- NT.h - Top-level interface for NT representation ----*- C++ -*-===//
#ifndef LLVM_LIB_TARGET_NT_NT_H
#define LLVM_LIB_TARGET_NT_NT_H
#include "MCTargetDesc/NTMCTargetDesc.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
  class NTTargetMachine;
  class FunctionPass;
} // end namespace llvm;
#endif

//===-- NTTargetMachine.h - Define TargetMachine for NT -----*- C++ -*-===//
#ifndef LLVM_LIB_TARGET_NT_NTTARGETMACHINE_H
#define LLVM_LIB_TARGET_NT_NTTARGETMACHINE_H
#endif

//===-- NTTargetMachine.cpp - Define TargetMachine for NT ---*- C++ -*-===//
#include "NTTargetMachine.h"
#include "NT.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
#define DEBUG_TYPE "NT"
extern "C" void LLVMInitializeNTTarget() {
// Register the Target
// Particularly, call RegisterTargetMachine<> for our NTTarget
}

//===-- NTTargetInfo.cpp - NT Target Implementation -------------------===//
#include "NT.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
Target llvm::TheNTTarget;
extern "C" void LLVMInitializeNTTargetInfo() {
  RegisterTarget<Triple::NT,
                 /*HasJIT=*/true> X(TheNTTarget, "NT", "NT", "NT");
}

//===-- NTMCTargetDesc.h - NT Target Descriptions -----------*- C++ -*-===//

#ifndef LLVM_LIB_TARGET_NT_MCTARGETDESC_NTMCTARGETDESC_H
#define LLVM_LIB_TARGET_NT_MCTARGETDESC_NTMCTARGETDESC_H
#include "llvm/Support/DataTypes.h"
namespace llvm {
class Target;
class Triple;
extern Target TheNTTarget;
} // End llvm namespace
#endif

//===-- NTMCTargetDesc.cpp - NT Target Descriptions -------------------===//

#include "NTMCTargetDesc.h"
#include "llvm/MC/MachineLocation.h"
#include "llvm/MC/MCELFStreamer.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
//@2 {
extern "C" void LLVMInitializeNTTargetMC() {}
//@2 }

7. Now, we need make CMakeLists.txt and LLVMBuild.txt to construct our minimal NT Target for LLVM system.

# llvm/lib/Target/NT/CMakeLists.txt
# NTCodeGen should match with LLVMBuild.txt NTCodeGen
add_llvm_target(NTCodeGen
  NTTargetMachine.cpp
  )
# Should match with "subdirectories =  MCTargetDesc TargetInfo" in LLVMBuild.txt
add_subdirectory(TargetInfo)
add_subdirectory(MCTargetDesc)


# MCTargetDesc/CMakeLists.txt
add_llvm_library(LLVMNTDesc
  NTMCTargetDesc.cpp)


# TargetInfo/CMakeLists.txt
add_llvm_library(LLVMNTInfo
  NTTargetInfo.cpp)


;===- ./lib/Target/NT/LLVMBuild.txt --------------------------*- Conf -*--===;
[common]
subdirectories = 
  MCTargetDesc TargetInfo
[component_0]
type = TargetGroup
name = NT
parent = Target
[component_1]
type = Library
name = NTCodeGen
parent = NT
required_libraries =
                     CodeGen Core MC 
                     NTDesc 
                     NTInfo 
                     SelectionDAG 
                     Support 
                     Target
add_to_library_groups = NT

;===- ./lib/Target/NT/MCTargetDesc/LLVMBuild.txt -------------*- Conf -*--===;
[component_0]
type = Library
name = NTDesc
parent = NT
required_libraries = MC 
                     NTInfo 
                     Support
add_to_library_groups = NT

;===- ./lib/Target/NT/TargetInfo/LLVMBuild.txt ---------------*- Conf -*--===;
[component_0]
type = Library
name = NTInfo
parent = NT
required_libraries = Support
add_to_library_groups = NT

7. Now the basic setup is finished, let us to verify if it works.

$ pwd
/llvm-project
$ mkdir build && cd build
$ cmake \
    -DCMAKE_BUILD_TYPE=Debug \
    -G Ninja \
    -DLLVM_TARGETS_TO_BUILD="NT" \
    ../llvm
$ cmake --build .
$ ./bin/llc --version
LLVM (http://llvm.org/):
  LLVM version 11.1.0
  DEBUG build with assertions.
  Default target: arm64-apple-darwin20.3.0
  Host CPU: cyclone
  Registered Targets:
    NT - NT

Now you can see the llc is registered our NT target. However it cannot compile any LLVM IR, because it is an empty target that cannot do anything.

$ ./bin/llc -march=NT main.ll
Assertion failed: (Target && "Could not allocate target machine!"), function operator(), file /Users/k/llvm-project/llvm/tools/llc/llc.cpp, line 472.

When use it to compile LLVM IR, it complains that there is not target machine. Because our target is empty, it just a plain llvm::Target, and there is no target machine it knows how to create, i.e. the function LLVMInitializeNTTarget() is empty. Moreover, there is no NTTargetMachine class at all.

However, it is minimal setup that register a new target.