NaplesPU Clang Documentation
This section shows how the Clang frontend is modified to match nu+ requirements concerning intrinsics recognition and target registration.
Contents
- 1 Builtins Definition
- 2 tools/clang/include/clang/Basic/TargetBuiltins.h
- 3 tools/clang/lib/Basic/Targets.cpp
- 4 tools/clang/lib/CodeGen/CGBuiltin.cpp
- 5 tools/clang/lib/CodeGen/CodeGenFunction.h
- 6 tools/clang/lib/Driver/Driver.cpp
- 7 tools/clang/lib/Driver/ToolChains.cpp
- 8 tools/clang/lib/Driver/ToolChains.h
- 9 tools/clang/lib/Driver/Tools.cpp
- 10 tools/clang/lib/Driver/Tools.h
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 }; }
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;
};
}