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.