NaplesPU Tools

From NaplesPU Documentation
Revision as of 17:44, 12 September 2017 by Edo (talk | contribs)
Jump to: navigation, search

A new backend for llvm needs to be added and registered. After registration, llvm tools are able to lookup and use the new target at runtime. In the following, we will show which files are involved in the registration phase and which changes are required.

CMakeLists.txt

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
	set(CMAKE_INSTALL_PREFIX "/usr/local/llvm-nuplus/" CACHE PATH "NuPlusLLVM install prefix" FORCE)
endif()

This specifies the installation path that, in our case, is: "/usr/local/llvm-nuplus/".

set(LLVM_ALL_TARGETS
  AArch64
  AMDGPU
  ARM
  BPF
  Hexagon
  Mips
  MSP430
  NVPTX
  PowerPC
  Sparc
  SystemZ
  X86
  XCore
  NuPlus
  )

The target name should be added to the LLVM_ALL_TARGETS list.

set(LLVM_TARGETS_TO_BUILD "NuPlus" CACHE STRING "Semicolon-separated list of targets to build, or \"all\".")

In a standard version of llvm, the LLVM_TARGETS_TO_BUILD variable is set to "all" in order to compile llvm with all the provided target backends. In out custom compiler, we set this variable to "NuPlus". In this way the compiler is built just targeting the NuPlus architecture.

set(LLVM_DEFAULT_TARGET_TRIPLE "nuplus-none-none" CACHE STRING "Default target for which LLVM will generate code." )

Finally, we remove the possibility of targeting a different architecture then NuPlus.

cmake/config-ix.cmake

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

include/llvm/ADT/Triple.h

enum ArchType {
    UnknownArch,
    arm,            // ARM (little endian): arm, armv.*, xscale
    ...
    nuplus,         // NuPlus

We add NuPlus to the ArchType list.

include/llvm/IR/Intrinsics.td

include "llvm/IR/IntrinsicsNuPlus.td"

In order to use target-specific intrinsics, we include the path to IntrinsicsNuPlus.td, i.e. the file containing the NuPlus intrinsics.

include/llvm/Object/ELFObjectFile.h

StringRef ELFObjectFile<ELFT>::getFileFormatName() const {
...
  case ELF::EM_NUPLUS:
    return "ELF32-nuplus";
unsigned ELFObjectFile<ELFT>::getArch() const {
...
  case ELF::EM_NUPLUS:
    return Triple::nuplus;

include/llvm/Object/RelocVisitor.h

RelocToApply visitELF(uint32_t RelocType, RelocationRef R, uint64_t Value) {
  if (ObjToVisit.getBytesInAddress() == 8) {
    ...
  } else if (ObjToVisit.getBytesInAddress() == 4) {
    switch (ObjToVisit.getArch()) {
    ...
      case Triple::nuplus:
        switch (RelocType) {
        case llvm::ELF::R_NUPLUS_ABS32:
          return visitELF_NUPLUS_ABS32(R, Value);
        case llvm::ELF::R_NUPLUS_BRANCH:
          return visitELF_NUPLUS_BRANCH(R, Value);
        case llvm::ELF::R_NUPLUS_PCREL_LEA:
          return visitELF_NUPLUS_PCREL_LEA(R, Value);
        default:
          HasError = true;
          return RelocToApply();
        }

Add the different kind of supported relocations inside the function visitELF for 32-bit object file.

  //NuPlus ELF
  RelocToApply visitELF_NUPLUS_ABS32(RelocationRef R, uint64_t Value) {
    int64_t Addend = getELFAddend(R);
    int64_t Res =  Value + Addend;
    // Overflow check allows for both signed and unsigned interpretation.
    if (Res < INT32_MIN || Res > UINT32_MAX)
      HasError = true;
    return RelocToApply(static_cast<uint32_t>(Res), 4);
  }
  RelocToApply visitELF_NUPLUS_BRANCH(RelocationRef R, uint64_t Value) {
    int64_t Res =  Value + getELFAddend(R) - R.getOffset();
    return RelocToApply(static_cast<uint32_t>(Res), 4);
  }
  RelocToApply visitELF_NUPLUS_PCREL_LEA(RelocationRef R, uint64_t Value) {
    int64_t Res =  Value + getELFAddend(R) - R.getOffset();
    return RelocToApply(static_cast<uint32_t>(Res), 4);
  }

Implement one visitELF function for each case in the above switch statement.

include/llvm/Support/ELF.h

enum {
  EM_NONE          = 0, // No machine
  ...
  EM_NUPLUS        = 1000, // NuPlus

Add the NuPlus architecture to the list of registered ELF machine architectures. Note that the number is arbitrarily chosen, but different architectures must have different numbers.

// ELF relocation types for NuPlus
enum {
#include "ELFRelocs/NuPlus.def"
};

We also include the file supporting the NuPlus specific relocation types.

include/llvm/Support/ELFRelocs/NuPlus.def

#ifndef ELF_RELOC
#error "ELF_RELOC must be defined"
#endif

ELF_RELOC(R_NUPLUS_NONE,                   0x00)
ELF_RELOC(R_NUPLUS_ABS32,                  0x01)
ELF_RELOC(R_NUPLUS_BRANCH,                 0x02)
ELF_RELOC(R_NUPLUS_PCREL_MEM,              0x03)
ELF_RELOC(R_NUPLUS_PCREL_MEM_EXT,          0x04)
ELF_RELOC(R_NUPLUS_PCREL_LEA,              0x05)

The different kind of relocations of NuPlus are specified in this file.

lib/MC/MCObjectFileInfo.cpp

void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T) {
  ...
  case Triple::nuplus:
    FDECFIEncoding = dwarf::DW_EH_PE_sdata4; //4 bytes signed value
    break;
  ...
  case Triple::nuplus:
    PersonalityEncoding = dwarf::DW_EH_PE_absptr;
    LSDAEncoding = dwarf::DW_EH_PE_absptr;
    TTypeEncoding = dwarf::DW_EH_PE_absptr;

We included the NuPlus DWARF extensions to the initELFMCObjectFileInfo function. In particular, the FDECFIEncoding is used for the Frame Description Entry (FDE) and Call Frame Information (CFI) records of the .eh_frame section. The LSDAEncoding is used for the language-specific data area (LSDA). DW_EH_PE_sdata4 means a 4 bytes signed value, while in DW_EH_PE_absptr the value is a literal pointer whose size is determined by the architecture. For more information, check DWARF Extensions and Exception Frames

lib/Object/ELF.cpp

StringRef getELFRelocationTypeName(uint32_t Machine, uint32_t Type) {
  switch (Machine) {
  ...
  case ELF::EM_NUPLUS:
    switch (Type) {
      #include "llvm/Support/ELFRelocs/NuPlus.def"
    default:
      break;
    }
    break;

lib/ObjectYAML/ELFYAML.cpp

ScalarEnumerationTraits<ELFYAML::ELF_EM>::enumeration(IO &IO, ELFYAML::ELF_EM &Value) {
  ...
  ECase(EM_NUPLUS)

lib/Support/Triple.cpp

const char *Triple::getArchTypeName(ArchType Kind) {
  switch (Kind) {
  ...
  case nuplus:         return "nuplus";
const char *Triple::getArchTypePrefix(ArchType Kind) {
  switch (Kind) {
  ...
  case nuplus:      return "nuplus";
Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) {
  ...
  .Case("nuplus", nuplus)
static Triple::ArchType parseArch(StringRef ArchName) {
  ...
  .Case("nuplus", Triple::nuplus)
static Triple::ObjectFormatType getDefaultFormat(const Triple &T) {
  switch (T.getArch()) {
  ...
  case Triple::nuplus:
    return Triple::ELF;
static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) {
  switch (Arch) {
  ...
  case llvm::Triple::nuplus:
    return 32;

This must be set with the pointers width of the NuPlus architecture, i.e. 32 bits.

Triple Triple::get32BitArchVariant() const {
  Triple T(*this);
  switch (getArch()) {
  ...
  case Triple::nuplus:
    // Already 32-bit.
    break;

This method is used with families of architectures and hence is useless with NuPlus.

Triple Triple::get64BitArchVariant() const {
  Triple T(*this);
  switch (getArch()) {
  ...
  case Triple::nuplus:
    T.setArch(UnknownArch);
    break;

It is not possible to have a 64-bit variant of NuPlus and, hence, NuPlus is intentionally unsupported here.

Triple Triple::getBigEndianArchVariant() const {
  ...
  switch (getArch()) {
  ...
  case Triple::nuplus:
    T.setArch(UnknownArch);
    break;

It is not possible to have a big endian variant of NuPlus and, hence, NuPlus is intentionally unsupported here.

bool Triple::isLittleEndian() const {
  switch (getArch()) {
  ...
  case Triple::nuplus:
    return true;

NuPlus is little endian.