Difference between revisions of "NaplesPU Clang Documentation"

From NaplesPU Documentation
Jump to: navigation, search
(Defining Target Features)
(tools/clang/lib/Basic/Targets.cpp)
Line 77: Line 77:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== tools/clang/lib/Basic/Targets.cpp ==
+
== Mapping Builtins on LLVM Intrinsics ==
<syntaxhighlight lang="cpp" line='line'>
+
The next step in adding frontend support for the custom target is to create a mapping between the defined builtins and the corresponding LLVM Intrinsics, that are used to build the intermediate representation.
class NuPlusTargetInfo : public TargetInfo {
+
 
  static const char *const GCCRegNames[];
+
LLVM provides a simple way to define the intrinsics by using the TableGen language. Let's take a look on the ''Intrinsics'' class definition showed below.
  static const Builtin::Info BuiltinInfo[];
+
 
public:
+
<syntaxhighlight>
  NuPlusTargetInfo(const llvm::Triple &Triple) : TargetInfo(Triple) {
+
class Intrinsic<list<LLVMType> ret_types,
    BigEndian = false;
+
                list<LLVMType> param_types = [],
    TLSSupported = false; //thread-local storage
+
                list<IntrinsicProperty> intr_properties = [],
    IntWidth = IntAlign = 32;
+
                string name = "" > : SDPatternOperator {
    LongWidth = LongLongWidth = LongAlign = LongLongAlign = 64;
+
...
    DoubleAlign = 64;
+
}
    LongDoubleWidth = LongDoubleAlign = 64;
+
</syntaxhighlight>
    PointerWidth = PointerAlign = 32;
+
 
    SizeType = UnsignedInt;
+
As shown, the ''Intrinsic'' class has several arguments:
    PtrDiffType = SignedInt;
+
* ''ret_types'' is the field containing the list of return types of the defined function. It is a list of ''LLVMType'' objects, each of them is described in Intrinsics.td. However, it is possible to define a custom type by deriving it from the defined ones. For example:
    MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32;
+
 
    resetDataLayout("e-m:e-p:32:32-i64:64:64-i32:32:32-f32:32:32-f64:64:64");
+
<code>def my_dummy_type : LLVMType<v16i32> </code>
  }
+
 
 +
The ''LLVMType'' object has a single argument that is a ''ValueType'' object. It is defined in ValueTypes.td. For example the following is the definition of ''v16i32'':
 +
 
 +
<syntaxhighlight>
 +
//ValueType <Size, Value>
 +
def v16i32 : ValueType<512, 42>;
 +
</syntaxhighlight>
 +
 
 +
* ''param_types'' is a list of parameter types, each one must be a ''LLVMType'' object.
 +
* ''properties'' is an array of ''IntrinsicProperty'', used to describe the behaviour of the intrinsic. For example ''IntrNoMem'' is the property to tell that the intrinsic does not access to memory or have side effects.
 +
* ''name'' is the string name of the intrinsic, prefixes included.
  
  bool setCPU(const std::string &Name) override {
+
Each intrinsic must be defined as an object of the just cited class, and each definition should be of the following format:
    return Name == "nuplus";
 
  }
 
  
  virtual void getTargetDefines(const LangOptions &Opts,
+
<code>def int_[name] : Intrinsic <...>    </code>
                                MacroBuilder &Builder) const override {
 
    Builder.defineMacro("__NUPLUS__");
 
  }
 
  
  ArrayRef<Builtin::Info> getTargetBuiltins() const override {
+
where ''name'' is the intrinsic name excluded of the prefixes. For example, the following one is a correct definition:
     return llvm::makeArrayRef(BuiltinInfo,
+
<syntaxhighlight>
                          clang::NuPlus::LastTSBuiltin - Builtin::FirstTSBuiltin);
+
     def int_nuplus_write_control_reg : Intrinsic<[],
  }
+
        [llvm_i32_ty, llvm_i32_ty], [],
 +
        "llvm.nuplus.__builtin_nuplus_write_control_reg">
 +
</syntaxhighlight>
  
  virtual ArrayRef<const char*> getGCCRegNames() const override;
+
In order to enable the frontend to generate the IR for our custom platform, it is needed to define a function to map builtins on the intrinsics. This function has to be defined in \texttt{CodeGenFunction} header and then implemented by switching among builtins and returning the associate \texttt{Intrinsic} object.
  
   ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
+
\begin{figure}[h]
    return None;
+
\begin{lstlisting}
 +
llvm::Value *CodeGenFunction::EmitMyTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E) {
 +
  ...
 +
   SmallVector<Value*, 2> Ops;
 +
  for (unsigned i = 0; i < E->getNumArgs(); i++){
 +
        Ops.push_back(EmitScalarExpr(E->getArg(i)));
 
   }
 
   }
 +
  llvm::Function *F;
 +
  ...
 +
  case MyTarget::BI__builtin_mytarget_ctzv8i64:
 +
    F = CGM.getIntrinsic(Intrinsic::mytarget_ctzv8i64);
 +
        break;
 +
..
 +
  return Builder.CreateCall(F, Ops, "");
 +
}
 +
\end{lstlisting}
 +
\caption{Chunk of \texttt{EmitMyTargetBuiltinExpr} in \texttt{CGBuiltin.cpp}}
 +
\label{code:emit_ir_builtin}
 +
\end{figure}
 +
   
 +
When \textit{clang} tries to convert a builtin in an intrinsic, it calls a method, that is \texttt{EmitTargetArchBuiltinExpr}, that switches among the different target to get the proper function to call. This method has got a static implementation in \texttt{CGBuiltin.cpp} and looks like as the follow code in figure \ref{code:EmitTargetArchBuiltinExpr}.
  
  virtual bool validateAsmConstraint(const char *&Name,
+
\begin{figure}[h]
                                    TargetInfo::ConstraintInfo &info) const override;
+
\begin{lstlisting}
   virtual const char *getClobbers() const override {
+
static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF, unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch) {
     return "";
+
   switch (Arch) {
  }
+
  ...
 +
  case llvm::Triple::mytarget:
 +
     return CGF->EmitMyTargetBuiltinExpr(BuiltinID, E);
 +
\end{lstlisting}
 +
\caption{Chunk of \texttt{EmitTargetArchBuiltinExpr} in \texttt{CGBuiltin.cpp}}
 +
\label{code:EmitTargetArchBuiltinExpr}
 +
\end{figure}
  
  virtual BuiltinVaListKind getBuiltinVaListKind() const override {
+
\subsection{Mapping Builtins on LLVM Intrinsics}
    return TargetInfo::VoidPtrBuiltinVaList;
+
The next step in adding frontend support for the custom target is to create a mapping between the defined builtins and the corresponding LLVM Intrinsics, that are used to build the intermediate representation.
  }
 
};
 
</syntaxhighlight>
 
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 | NuPlusTargetMachine.cpp/.h]] files.
 
  
<syntaxhighlight lang="cpp" line='line'>
+
LLVM provides a simple way to define the intrinsics by using the TableGen language. Let's take a look on the \texttt{Intrinsics} class definition showed in the figure \ref{code:intrinsics}.
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"
 
};
 
</syntaxhighlight>
 
This is the list of all registers in the NuPlus register file.
 
  
<syntaxhighlight lang="cpp" line='line'>
+
\begin{figure}[!htbp]
ArrayRef<const char *> NuPlusTargetInfo::getGCCRegNames() const {
+
\begin{lstlisting}
  return llvm::makeArrayRef(GCCRegNames);
+
class Intrinsic<list<LLVMType> ret_types,
 +
                list<LLVMType> param_types = [],
 +
                list<IntrinsicProperty> intr_properties = [],
 +
                string name = "" > : SDPatternOperator {
 +
...
 
}
 
}
</syntaxhighlight>
+
\end{lstlisting}
 +
\caption{\texttt{Intrinsic} class definition from \texttt{Intrinsics.td}}
 +
\label{code:intrinsics}
 +
\end{figure}
 +
 
 +
As shown, the \texttt{Intrinsic} class has got several arguments that are described below:
 +
\begin{itemize}
 +
    \item \texttt{ret\_types} is the field containing the list of return types of the defined function. It is a list of \texttt{LLVMType} objects, each of them is described in \texttt{Intrinsics.td}. However, it is possible to define a custom type by deriving it from the defined ones. For example:
 +
    \begin{verbatim}
 +
def my_dummy_type : LLVMType<v16i32>
 +
    \end{verbatim}
 +
    Note that the \texttt{LLVMType} object has one argument that is an \texttt{ValueType} object. It is defined in \texttt{ValueTypes.td}. For example the following is the definition of \texttt{v16i32}:
 +
    \begin{verbatim}
 +
//ValueType <Size, Value>
 +
def v16i32 : ValueType<512, 42>;
 +
    \end{verbatim}
 +
    \item \texttt{param\_types} is a list of parameter types, each one must be an \texttt{LLVMType} object.
 +
    \item \texttt{properties} is an array of \texttt{IntrinsicProperty}, used to describe the behaviour of the intrinsic. For example \texttt{IntrNoMem} is the property to tell that the intrinsic does not access to memory or have side effects.
 +
    \item \texttt{name} is the string name of the intrinsic, prefixes included.
 +
\end{itemize}
  
<syntaxhighlight lang="cpp" line='line'>
+
Each intrinsic must be defined as an object of the just cited class, and each definition should be of the following format:
bool NuPlusTargetInfo::
+
\begin{verbatim}
validateAsmConstraint(const char *&Name,
+
def int_[name] : Intrinsic <...>   
                      TargetInfo::ConstraintInfo &Info) const {
+
\end{verbatim}
  switch (*Name) {
 
  default:
 
    return false;
 
  
  case 's':
+
where \texttt{name} is the intrinsic name excluded of the prefixes. For example, the following one is a correct definition:
  case 'v':
+
\begin{verbatim}
    Info.setAllowsRegister();
+
    def int_mytarget_write_control_reg : Intrinsic<[],
    return true;
+
        [llvm_i32_ty, llvm_i32_ty], [],
 +
        "llvm.mytarget.__builtin_nuplus_write_control_reg">
 +
\end{verbatim}
  
  case 'I': // Unsigned 8-bit constant
+
In order to enable the frontend to generate the IR for our custom platform, it is needed to define a function to map builtins on the intrinsics. This function has to be defined in \texttt{CodeGenFunction} header and then implemented by switching among builtins and returning the associate \texttt{Intrinsic} object.
  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
+
\begin{figure}[h]
  case 'R': // Likewise, plus an index
+
\begin{lstlisting}
   case 'S': // Memory with base and signed 20-bit displacement
+
llvm::Value *CodeGenFunction::EmitMyTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E) {
   case 'T': // Likewise, plus an index
+
   ...
    Info.setAllowsMemory();
+
   SmallVector<Value*, 2> Ops;
    return true;
+
  for (unsigned i = 0; i < E->getNumArgs(); i++){
 +
        Ops.push_back(EmitScalarExpr(E->getArg(i)));
 
   }
 
   }
 +
  llvm::Function *F;
 +
  ...
 +
  case MyTarget::BI__builtin_mytarget_ctzv8i64:
 +
    F = CGM.getIntrinsic(Intrinsic::mytarget_ctzv8i64);
 +
        break;
 +
..
 +
  return Builder.CreateCall(F, Ops, "");
 
}
 
}
</syntaxhighlight>
+
\end{lstlisting}
This is related to the constraints in case of inline assembler implementation methods. We derived our method from SystemZTargetInfo::validateAsmConstraint.
+
\caption{Chunk of \texttt{EmitMyTargetBuiltinExpr} in \texttt{CGBuiltin.cpp}}
 +
\label{code:emit_ir_builtin}
 +
\end{figure}
 +
   
 +
When \textit{clang} tries to convert a builtin in an intrinsic, it calls a method, that is \texttt{EmitTargetArchBuiltinExpr}, that switches among the different target to get the proper function to call. This method has got a static implementation in \texttt{CGBuiltin.cpp} and looks like as the follow code in figure \ref{code:EmitTargetArchBuiltinExpr}.
  
<syntaxhighlight lang="cpp" line='line'>
+
\begin{figure}[h]
const Builtin::Info NuPlusTargetInfo::BuiltinInfo[] = {
+
\begin{lstlisting}
#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES },
+
static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF, unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch) {
#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\
+
   switch (Arch) {
                                              ALL_LANGUAGES },
 
#include "clang/Basic/BuiltinsNuPlus.def"
 
};
 
</syntaxhighlight>
 
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.
 
 
 
<syntaxhighlight lang="cpp" line='line'>
 
static TargetInfo *AllocateTarget(const llvm::Triple &Triple, const TargetOptions &Opts) {
 
   switch (Triple.getArch()) {
 
 
   ...
 
   ...
   case llvm::Triple::nuplus:
+
   case llvm::Triple::mytarget:
     return new NuPlusTargetInfo(Triple);
+
     return CGF->EmitMyTargetBuiltinExpr(BuiltinID, E);
</syntaxhighlight>
+
\end{lstlisting}
We add the nu+ case for the above switch statements.
+
\caption{Chunk of \texttt{EmitTargetArchBuiltinExpr} in \texttt{CGBuiltin.cpp}}
 +
\label{code:EmitTargetArchBuiltinExpr}
 +
\end{figure}
  
 
== tools/clang/lib/CodeGen/CGBuiltin.cpp ==
 
== tools/clang/lib/CodeGen/CGBuiltin.cpp ==

Revision as of 16:38, 29 March 2019

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

Builtins Definition

When implementing support for a new target, a frontend implementation must be able to recognise target-specific instructions, also called builtins. Clang provides a simple mechanism to add to frontend the capability to identify builtins, described in the file Builtins.def. It looks like a database for builtin functions, in which each entry is of the following form:

BUILTIN(<name of the builtin>, <types>, <function attributes>)

Referring to the above schema, the first value is the function name, the second one is the concatenation of types for the result value and for each argument, the latter is the concatenation of codes, each one is a function attribute. For example, the following is a builtin definition:

BUILTIN(__builtin_nuplus_flush, vi, n)

In this case, the function __builtin_nuplus_flush has got only one argument of int type and it is a void return function. n attribute means that it is a nothrow function. To get a full list of attributes and types you may check the just cited Builtins.def

At this point builtins are defined but can not be recognised by frontend tools. To realise this behaviour it is necessary to collect all builtin definitions in a .def file and then include it in the TargetBuiltins header file as shown below:

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

Defining Target Features

In order to provide target informations, the LLVM team provides a class named TargetInfo. It contains information fields, each of them is a potentially supported feature. To be able to use this class, it is required to inherit it by creating a specialized TargetInfo class, defining the target features and register it as new supported target.

The following code sample is extracted by NuPlus.h in clang/lib/Basic/Targets.

class NuPlusTargetInfo : public TargetInfo {
  static const char *const GCCRegNames[];
  static const Builtin::Info BuiltinInfo[];
 public:
  NuPlusTargetInfo(const llvm::Triple &Triple) : TargetInfo(Triple) {
    BigEndian = false;
    IntWidth = IntAlign = 32;
    ...
    resetDataLayout("e-m:e-p:32:32-i32:32:32-f32:32:32");
  }

Referring to the code sample above, the DataLayout field describes the target in terms of its features. Each of them is separated by a - character. By looking at the example above, the first feature defines the endianness as little. The other ones are related to size and alignment of pointers, integers and floats respectively. They should be read as follows:

<feature_code>:size:alignment

More about the data layout parsing can be found in the DataLayout documentation.

The TargetInfo class implementation provides two static variables, GCCRegNames and BuiltinInfo, of array type. The first contains the list of the registers' names supported by the target platform, while the latter is the definition of the target builtin format.

Another implemented method is validateAsmConstraint. This function is used to validate the constraint used for inputs, outputs and clobbers for inline assembly code declarations. Recall that the inline assembly code is the way to embed asm code into an HLL source file.

To be correctly recognised, NuPlus has to be added in the llvm::Triple namespace, by defining it in Triple.h:

enum ArchType {
    UnknownArch,
    arm,            // ARM (little endian): arm, armv.*, xscale
    ...
    nuplus,       // ADDING nu+ TARGET

Once the custom target is declared, it can be allocated by frontend. To enable this operation, it is necessary to define a link between the clang function AllocateTarget and the TargetInfo object constructor in clang/lib/Basic/Targets.cpp:

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

Mapping Builtins on LLVM Intrinsics

The next step in adding frontend support for the custom target is to create a mapping between the defined builtins and the corresponding LLVM Intrinsics, that are used to build the intermediate representation.

LLVM provides a simple way to define the intrinsics by using the TableGen language. Let's take a look on the Intrinsics class definition showed below.

class Intrinsic<list<LLVMType> ret_types,
                list<LLVMType> param_types = [],
                list<IntrinsicProperty> intr_properties = [],
                string name = "" > : SDPatternOperator {
...
}

As shown, the Intrinsic class has several arguments:

  • ret_types is the field containing the list of return types of the defined function. It is a list of LLVMType objects, each of them is described in Intrinsics.td. However, it is possible to define a custom type by deriving it from the defined ones. For example:

def my_dummy_type : LLVMType<v16i32>

The LLVMType object has a single argument that is a ValueType object. It is defined in ValueTypes.td. For example the following is the definition of v16i32:

//ValueType <Size, Value>
def v16i32 : ValueType<512, 42>;
  • param_types is a list of parameter types, each one must be a LLVMType object.
  • properties is an array of IntrinsicProperty, used to describe the behaviour of the intrinsic. For example IntrNoMem is the property to tell that the intrinsic does not access to memory or have side effects.
  • name is the string name of the intrinsic, prefixes included.

Each intrinsic must be defined as an object of the just cited class, and each definition should be of the following format:

def int_[name] : Intrinsic <...>

where name is the intrinsic name excluded of the prefixes. For example, the following one is a correct definition:

    def int_nuplus_write_control_reg : Intrinsic<[], 
        [llvm_i32_ty, llvm_i32_ty], [],
        "llvm.nuplus.__builtin_nuplus_write_control_reg">

In order to enable the frontend to generate the IR for our custom platform, it is needed to define a function to map builtins on the intrinsics. This function has to be defined in \texttt{CodeGenFunction} header and then implemented by switching among builtins and returning the associate \texttt{Intrinsic} object.

\begin{figure}[h] \begin{lstlisting} llvm::Value *CodeGenFunction::EmitMyTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E) {

 ...
 SmallVector<Value*, 2> Ops;
 for (unsigned i = 0; i < E->getNumArgs(); i++){
       Ops.push_back(EmitScalarExpr(E->getArg(i)));
 }
 llvm::Function *F;
 ...
 case MyTarget::BI__builtin_mytarget_ctzv8i64:
   F = CGM.getIntrinsic(Intrinsic::mytarget_ctzv8i64);
       break;

..

 return Builder.CreateCall(F, Ops, "");

} \end{lstlisting} \caption{Chunk of \texttt{EmitMyTargetBuiltinExpr} in \texttt{CGBuiltin.cpp}} \label{code:emit_ir_builtin} \end{figure}

When \textit{clang} tries to convert a builtin in an intrinsic, it calls a method, that is \texttt{EmitTargetArchBuiltinExpr}, that switches among the different target to get the proper function to call. This method has got a static implementation in \texttt{CGBuiltin.cpp} and looks like as the follow code in figure \ref{code:EmitTargetArchBuiltinExpr}.

\begin{figure}[h] \begin{lstlisting} static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF, unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch) {

 switch (Arch) {
 ...
 case llvm::Triple::mytarget:
   return CGF->EmitMyTargetBuiltinExpr(BuiltinID, E);

\end{lstlisting} \caption{Chunk of \texttt{EmitTargetArchBuiltinExpr} in \texttt{CGBuiltin.cpp}} \label{code:EmitTargetArchBuiltinExpr} \end{figure}

\subsection{Mapping Builtins on LLVM Intrinsics} The next step in adding frontend support for the custom target is to create a mapping between the defined builtins and the corresponding LLVM Intrinsics, that are used to build the intermediate representation.

LLVM provides a simple way to define the intrinsics by using the TableGen language. Let's take a look on the \texttt{Intrinsics} class definition showed in the figure \ref{code:intrinsics}.

\begin{figure}[!htbp] \begin{lstlisting} class Intrinsic<list<LLVMType> ret_types,

               list<LLVMType> param_types = [],
               list<IntrinsicProperty> intr_properties = [],
               string name = "" > : SDPatternOperator {

... } \end{lstlisting} \caption{\texttt{Intrinsic} class definition from \texttt{Intrinsics.td}} \label{code:intrinsics} \end{figure}

As shown, the \texttt{Intrinsic} class has got several arguments that are described below: \begin{itemize}

   \item \texttt{ret\_types} is the field containing the list of return types of the defined function. It is a list of \texttt{LLVMType} objects, each of them is described in \texttt{Intrinsics.td}. However, it is possible to define a custom type by deriving it from the defined ones. For example:
   \begin{verbatim}

def my_dummy_type : LLVMType<v16i32>

   \end{verbatim}
   Note that the \texttt{LLVMType} object has one argument that is an \texttt{ValueType} object. It is defined in \texttt{ValueTypes.td}. For example the following is the definition of \texttt{v16i32}:
   \begin{verbatim}

//ValueType <Size, Value> def v16i32 : ValueType<512, 42>;

   \end{verbatim}
   \item \texttt{param\_types} is a list of parameter types, each one must be an \texttt{LLVMType} object. 
   \item \texttt{properties} is an array of \texttt{IntrinsicProperty}, used to describe the behaviour of the intrinsic. For example \texttt{IntrNoMem} is the property to tell that the intrinsic does not access to memory or have side effects.
   \item \texttt{name} is the string name of the intrinsic, prefixes included.

\end{itemize}

Each intrinsic must be defined as an object of the just cited class, and each definition should be of the following format: \begin{verbatim} def int_[name] : Intrinsic <...> \end{verbatim}

where \texttt{name} is the intrinsic name excluded of the prefixes. For example, the following one is a correct definition: \begin{verbatim}

   def int_mytarget_write_control_reg : Intrinsic<[], 
       [llvm_i32_ty, llvm_i32_ty], [],
       "llvm.mytarget.__builtin_nuplus_write_control_reg">

\end{verbatim}

In order to enable the frontend to generate the IR for our custom platform, it is needed to define a function to map builtins on the intrinsics. This function has to be defined in \texttt{CodeGenFunction} header and then implemented by switching among builtins and returning the associate \texttt{Intrinsic} object.

\begin{figure}[h] \begin{lstlisting} llvm::Value *CodeGenFunction::EmitMyTargetBuiltinExpr(unsigned BuiltinID, const CallExpr *E) {

 ...
 SmallVector<Value*, 2> Ops;
 for (unsigned i = 0; i < E->getNumArgs(); i++){
       Ops.push_back(EmitScalarExpr(E->getArg(i)));
 }
 llvm::Function *F;
 ...
 case MyTarget::BI__builtin_mytarget_ctzv8i64:
   F = CGM.getIntrinsic(Intrinsic::mytarget_ctzv8i64);
       break;

..

 return Builder.CreateCall(F, Ops, "");

} \end{lstlisting} \caption{Chunk of \texttt{EmitMyTargetBuiltinExpr} in \texttt{CGBuiltin.cpp}} \label{code:emit_ir_builtin} \end{figure}

When \textit{clang} tries to convert a builtin in an intrinsic, it calls a method, that is \texttt{EmitTargetArchBuiltinExpr}, that switches among the different target to get the proper function to call. This method has got a static implementation in \texttt{CGBuiltin.cpp} and looks like as the follow code in figure \ref{code:EmitTargetArchBuiltinExpr}.

\begin{figure}[h] \begin{lstlisting} static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF, unsigned BuiltinID, const CallExpr *E, llvm::Triple::ArchType Arch) {

 switch (Arch) {
 ...
 case llvm::Triple::mytarget:
   return CGF->EmitMyTargetBuiltinExpr(BuiltinID, E);

\end{lstlisting} \caption{Chunk of \texttt{EmitTargetArchBuiltinExpr} in \texttt{CGBuiltin.cpp}} \label{code:EmitTargetArchBuiltinExpr} \end{figure}

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;
  };
}