NaplesPU Clang Documentation

From NaplesPU Documentation
Revision as of 15:12, 29 March 2019 by Francesco (talk | contribs)
Jump to: navigation, search

This section shows how the Clang frontend is modified to match nu+ requirements concerning intrinsic recognition and target registration.

tools/clang/include/clang/Basic/BuiltinsNuPlus.def

This file defines the NuPlus-specific builtin function database. The format of this database is described inside tools/clang/include/clang/Basic/Builtins.def.

tools/clang/include/clang/Basic/TargetBuiltins.h

namespace NuPlus {
    enum {
      LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
      #define BUILTIN(ID, TYPE, ATTRS) BI##ID,
      #include "clang/Basic/BuiltinsNuPlus.def"
      LastTSBuiltin
    };
  }

We include the path to BuiltinsNuPlus.def.

tools/clang/lib/Basic/Targets.cpp

class NuPlusTargetInfo : public TargetInfo {
  static const char *const GCCRegNames[];
  static const Builtin::Info BuiltinInfo[];
public:
  NuPlusTargetInfo(const llvm::Triple &Triple) : TargetInfo(Triple) {
    BigEndian = false;
    TLSSupported = false; //thread-local storage
    IntWidth = IntAlign = 32;
    LongWidth = LongLongWidth = LongAlign = LongLongAlign = 64;
    DoubleAlign = 64;
    LongDoubleWidth = LongDoubleAlign = 64;
    PointerWidth = PointerAlign = 32;
    SizeType = UnsignedInt;
    PtrDiffType = SignedInt;
    MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32;
    resetDataLayout("e-m:e-p:32:32-i64:64:64-i32:32:32-f32:32:32-f64:64:64");
  }

  bool setCPU(const std::string &Name) override {
    return Name == "nuplus";
  }

  virtual void getTargetDefines(const LangOptions &Opts,
                                MacroBuilder &Builder) const override {
    Builder.defineMacro("__NUPLUS__");
  }

  ArrayRef<Builtin::Info> getTargetBuiltins() const override {
    return llvm::makeArrayRef(BuiltinInfo,
                          clang::NuPlus::LastTSBuiltin - Builtin::FirstTSBuiltin);
  }

  virtual ArrayRef<const char*> getGCCRegNames() const override;

  ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
    return None;
  }

  virtual bool validateAsmConstraint(const char *&Name,
                                     TargetInfo::ConstraintInfo &info) const override;
  virtual const char *getClobbers() const override {
    return "";
  }

  virtual BuiltinVaListKind getBuiltinVaListKind() const override {
    return TargetInfo::VoidPtrBuiltinVaList;
  }
};

This is required to implement the construction of a TargetInfo object. The NuPlusTargetInfo object is inherited from the TargetInfo object and the majority of its attributes are set by default in the TargetInfo constructor. Check the constructor in tools/clang/lib/Basic/TargetInfo.cpp. As regards NuPlus, the information that must be provided to the frontend includes:

  • endianess
  • thread-local storage (TLS)
  • allignment and width of several types
  • a DataLayout string used to describe the target. The string related to NuPlus is "e-m:e-p:32:32-i64:64:64-i32:32:32-f32:32:32-f64:64:64". The different features are separated by '-'. The first lower-case 'e' indicates little-endian. "m:e" indicates the ELF mangling mode. "p:32:32" indicates size and alignment of pointers. "i64:64:64", "i32:32:32", "f32:32:32" and "f64:64:64" indicate size and alignment of 32- and 64-bit integers and floats. For more information, check the DataLayout class implementation in lib/IR/DataLayout.cpp and the include/llvm/IR/DataLayout.h file.

Note that some of these aspects must be specified also inside the costructor of the NuPlusTargetMachine class located in the * NuPlusTargetMachine.cpp/.h files.

const char *const NuPlusTargetInfo::GCCRegNames[] = {
  "s0",  "s1",  "s2",  "s3",  "s4",  "s5",  "s6",  "s7",
  "s8",  "s9",  "s10", "s11", "s12", "s13", "s14", "s15",
  "s16",  "s17",  "s18",  "s19",  "s20",  "s21",  "s22",  "s23",
  "s24",  "s25",  "s26", "s27",  "s28",  "s29",  "s30", "s31", 
  "s32",  "s33",  "s34",  "s35",  "s36",  "s37",  "s38",  "s39",
  "s40",  "s41",  "s42",  "s43",  "s44",  "s45",  "s46",  "s47",
  "s48",  "s49",  "s50",  "s51",  "s52",  "s53",  "s54",  "s55",
  "s56",  "s57",  "TR",  "RM",  "FP", "SP", "RA", "PC",
  "v0",  "v1",  "v2",  "v3",  "v4",  "v5",  "v6",  "v7",
  "v8",  "v9",  "v10", "v11", "v12", "v13", "v14", "v15",
  "v16",  "v17",  "v18",  "v19",  "v20",  "v21",  "v22",  "v23",
  "v24",  "v25",  "v26", "v27", "v28", "v29", "v30", "v31",
  "v32",  "v33",  "v34",  "v35",  "v36",  "v37",  "v38",  "v39",
  "v40",  "v41",  "v42",  "v43",  "v44",  "v45",  "v46",  "v47",
  "v48",  "v49",  "v50",  "v51",  "v52",  "v53",  "v54",  "v55",
  "v56",  "v57",  "v58",  "v59",  "v60",  "v61",  "v62",  "v63"
};

This is the list of all registers in the NuPlus register file.

ArrayRef<const char *> NuPlusTargetInfo::getGCCRegNames() const {
  return llvm::makeArrayRef(GCCRegNames);
}
bool NuPlusTargetInfo::
validateAsmConstraint(const char *&Name,
                      TargetInfo::ConstraintInfo &Info) const {
  switch (*Name) {
  default:
    return false;

  case 's':
  case 'v':
    Info.setAllowsRegister();
    return true;

  case 'I': // Unsigned 8-bit constant
  case 'J': // Unsigned 12-bit constant
  case 'K': // Signed 16-bit constant
  case 'L': // Signed 20-bit displacement (on all targets we support)
  case 'M': // 0x7fffffff
    return true;

  case 'Q': // Memory with base and unsigned 12-bit displacement
  case 'R': // Likewise, plus an index
  case 'S': // Memory with base and signed 20-bit displacement
  case 'T': // Likewise, plus an index
    Info.setAllowsMemory();
    return true;
  }
}

This is related to the constraints in case of inline assembler implementation methods. We derived our method from SystemZTargetInfo::validateAsmConstraint.

const Builtin::Info NuPlusTargetInfo::BuiltinInfo[] = {
#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES },
#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\
                                              ALL_LANGUAGES },
#include "clang/Basic/BuiltinsNuPlus.def"
};

We specify the format of the builtins and we include the BuiltinsNuPlus.def file, i.e. the file that contains all the nu+ specific builtins.

static TargetInfo *AllocateTarget(const llvm::Triple &Triple, const TargetOptions &Opts) {
  switch (Triple.getArch()) {
  ...
  case llvm::Triple::nuplus:
    return new NuPlusTargetInfo(Triple);

We add the nu+ case for the above switch statements.

tools/clang/lib/CodeGen/CGBuiltin.cpp

static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF, unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch) {
  switch (Arch) {
  ...
  case llvm::Triple::nuplus:
    return CGF->EmitNuPlusBuiltinExpr(BuiltinID, E);
...
Value *CodeGenFunction::EmitNuPlusBuiltinExpr(unsigned BuiltinID, const CallExpr *E) {
  ...
}

The EmitNuPlusBuiltinExpr function maps each Builtin call to the corresponding LLVM intrinsic. This is a target dependent function and hence it should be filled with all the NuPlus Builtin calls.

tools/clang/lib/CodeGen/CodeGenFunction.h

llvm::Value *EmitNuPlusBuiltinExpr(unsigned BuiltinID, const CallExpr *E);

We add the function prototype.

tools/clang/lib/Driver/Driver.cpp

const ToolChain &Driver::getToolChain(const ArgList &Args, const llvm::Triple &Target) const {
  ...
  switch (Target.getArch()) {
  case llvm::Triple::nuplus:
    TC = new toolchains::NuPlusToolChain(*this, Target, Args);
    break;

This is required to retrieve the ToolChain for the NuPlus triple.

tools/clang/lib/Driver/ToolChains.cpp

NuPlusToolChain::NuPlusToolChain(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args) :	ToolChain(D, Triple, Args){
  // We expect 'as', 'ld', etc. to be adjacent to our install dir.
  getProgramPaths().push_back(getDriver().getInstalledDir());
  if (getDriver().getInstalledDir() != getDriver().Dir)
    getProgramPaths().push_back(getDriver().Dir);
}

NuPlusToolChain::~NuPlusToolChain()
{
}


bool NuPlusToolChain::IsIntegratedAssemblerDefault() const
{
  return true;
}

We use the default integrated assembler.

bool NuPlusToolChain::isPICDefault() const
{
  return false;
}

bool NuPlusToolChain::isPIEDefault() const
{
  return false;
}

bool NuPlusToolChain::isPICDefaultForced() const
{
  return false;
}

By default, we don't use the Position Independent Code (PIC) and the Position Independent Executable (PIE).

void NuPlusToolChain::addClangTargetOptions(const ArgList &DriverArgs,
                                  ArgStringList &CC1Args) const {
  CC1Args.push_back("-nostdsysteminc");
  if (DriverArgs.hasFlag(options::OPT_fuse_init_array,
                         options::OPT_fno_use_init_array,
                         true))
  {
    CC1Args.push_back("-fuse-init-array");
  }
}

This is used to add some options to Clang. In particular,

  • -nostdsysteminc Disable standard system #include directories
  • -fuse-init-array Use .init_array instead of .ctors. For more details, see ELF Special Sections
bool NuPlusToolChain::IsUnwindTablesDefault() const {
  return true;
}

This enables the generation of unwind tables (DWARF-based stack unwinding, .eh_frame section).

Tool *NuPlusToolChain::buildLinker() const {
  return new tools::NuPlus::Link(*this);
}

It builds the linker.

tools/clang/lib/Driver/ToolChains.h

class LLVM_LIBRARY_VISIBILITY NuPlusToolChain : public ToolChain {
public:
	NuPlusToolChain(const Driver &D, const llvm::Triple &Triple,
                      const llvm::opt::ArgList &Args);
  ~NuPlusToolChain();
  bool IsIntegratedAssemblerDefault() const override;
  bool isPICDefault() const override;
  bool isPIEDefault() const override;
  bool isPICDefaultForced() const override;
  void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
     tools::ArgStringList &CC1Args) const override;
  bool IsUnwindTablesDefault() const override;

protected:
  virtual Tool *buildLinker() const;
};

The NuPlusToolChain class declaration.

tools/clang/lib/Driver/Tools.cpp

static std::string getCPUName(const ArgList &Args, const llvm::Triple &T, bool FromAs = false) {
  ...
  switch (T.getArch()) {
  case llvm::Triple::nuplus:
    return "nuplus";

It returns the CPU name.

static void addExceptionArgs(const ArgList &Args, types::ID InputType,
                             const ToolChain &TC, bool KernelOrKext,
                             const ObjCRuntime &objcRuntime,
                             ArgStringList &CmdArgs) {
  ...
  if (types::isCXX(InputType)) {
    // Disable C++ EH by default on XCore, PS4 and NuPlus.
    bool CXXExceptionsEnabled =
        Triple.getArch() != llvm::Triple::xcore && 
	!Triple.isPS4CPU() &&
	Triple.getArch() != llvm::Triple::nuplus;

By default, the C++ EH (Exception Handling Model) is disabled.

static bool shouldUseFramePointerForTarget(const ArgList &Args, const llvm::Triple &Triple) {
  switch (Triple.getArch()) {
  ...
  case llvm::Triple::nuplus:
    return !areOptimizationsEnabled(Args);

We don't use a frame pointer if optimizing for the NuPlus target.

void NuPlus::Link::ConstructJob(Compilation &C, const JobAction &JA,
                                   const InputInfo &Output,
                                   const InputInfoList &Inputs,
                                   const ArgList &Args,
                                   const char *LinkingOutput) const {
  ArgStringList CmdArgs;

  if (Output.isFilename()) {
    CmdArgs.push_back("-o");
    CmdArgs.push_back(Output.getFilename());
  } else {
    assert(Output.isNothing() && "Invalid output.");
  }

  AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs);

  const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld.mcld"));
  C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
}

This is necessary for a proper usage of the linker.

tools/clang/lib/Driver/Tools.h

namespace NuPlus {
  class LLVM_LIBRARY_VISIBILITY Link : public Tool {
  public:
    Link(const ToolChain &TC) : Tool("NuPlus::Link", "NuPlus-ld", TC) {}

    virtual bool hasIntegratedCPP() const override { return false; }
    virtual bool isLinkJob() const override { return true; }
    virtual void ConstructJob(Compilation &C, const JobAction &JA,
                              const InputInfo &Output, const InputInfoList &Inputs,
                              const llvm::opt::ArgList &TCArgs,
                              const char *LinkingOutput) const override;
  };
}