From 6a1bc70bd7fe15cf9c9d8a4fce2ff7a6ac6d7fdc Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Fri, 10 May 2013 22:56:52 +0200 Subject: [PATCH] Added -float-abi and auto-detection logic for ARM. Even though this argument design conflates two separate concepts (ABI and hardware/software implementation), I chose to go with it since users are liekly know it from GCC and the combination of softloat operations with hardfloat ABI makes no sense. I didn't implement it for old LLVM versions, as ARM EABI exception handling requires LLVM 3.3+ anyway, without which LDC would be useless anyway. --- driver/cl_options.cpp | 10 +++ driver/cl_options.h | 2 + driver/main.cpp | 5 +- driver/target.cpp | 169 ++++++++++++++++++++++++++++++++++++++++-- driver/target.h | 10 +++ 5 files changed, 189 insertions(+), 7 deletions(-) diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index c88a2633..ed0e36e6 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -310,6 +310,16 @@ cl::opt mCodeModel("code-model", clEnumValN(llvm::CodeModel::Large, "large", "Large code model"), clEnumValEnd)); +cl::opt mFloatABI("float-abi", + cl::desc("ABI/operations to use for floating-point types:"), + cl::init(FloatABI::Default), + cl::values( + clEnumValN(FloatABI::Default, "default", "Target default floating-point ABI"), + clEnumValN(FloatABI::Soft, "soft", "Software floating-point ABI and operations"), + clEnumValN(FloatABI::SoftFP, "softfp", "Soft-float ABI, but hardware floating-point instructions"), + clEnumValN(FloatABI::Hard, "hard", "Hardware floating-point ABI and instructions"), + clEnumValEnd)); + static cl::opt asserts("asserts", cl::desc("(*) Enable assertions"), cl::value_desc("bool"), diff --git a/driver/cl_options.h b/driver/cl_options.h index d1320894..605a6f27 100644 --- a/driver/cl_options.h +++ b/driver/cl_options.h @@ -16,6 +16,7 @@ #define LDC_DRIVER_CL_OPTIONS_H #include "mars.h" +#include "driver/target.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/CommandLine.h" #include @@ -60,6 +61,7 @@ namespace opts { extern cl::opt mTargetTriple; extern cl::opt mRelocModel; extern cl::opt mCodeModel; + extern cl::opt mFloatABI; extern cl::opt singleObj; extern cl::opt linkonceTemplates; diff --git a/driver/main.cpp b/driver/main.cpp index 3ed1d247..3dec2d7c 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -459,8 +459,9 @@ int main(int argc, char** argv) if (global.errors) fatal(); - gTargetMachine = createTargetMachine(mTargetTriple, mArch, mCPU, mAttrs, bitness, - mRelocModel, mCodeModel, codeGenOptLevel(), global.params.symdebug); + gTargetMachine = createTargetMachine(mTargetTriple, mArch, mCPU, mAttrs, + bitness, mFloatABI, mRelocModel, mCodeModel, codeGenOptLevel(), + global.params.symdebug); global.params.targetTriple = llvm::Triple(gTargetMachine->getTargetTriple()); #if LDC_LLVM_VER >= 302 diff --git a/driver/target.cpp b/driver/target.cpp index f01d9089..f9581c02 100644 --- a/driver/target.cpp +++ b/driver/target.cpp @@ -7,13 +7,15 @@ // //===----------------------------------------------------------------------===// // -// Note: The target CPU detection logic has been adapted from Clang -// (lib/Driver/Tools.cpp). +// Note: The target CPU and FP ABI detection logic has been adapted from Clang +// (Tools.cpp and ToolChain.cpp in lib/Driver, the latter seems to have the +// more up-to-date version). // //===----------------------------------------------------------------------===// #include "driver/target.h" #include "gen/llvmcompat.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Support/Host.h" @@ -23,7 +25,6 @@ #include "llvm/Target/TargetOptions.h" #include "mars.h" - static std::string getX86TargetCPU(std::string arch, const llvm::Triple &triple) { @@ -74,12 +75,130 @@ static std::string getX86TargetCPU(std::string arch, } +static const char *getLLVMArchSuffixForARM(llvm::StringRef CPU) +{ + return llvm::StringSwitch(CPU) + .Cases("arm7tdmi", "arm7tdmi-s", "arm710t", "v4t") + .Cases("arm720t", "arm9", "arm9tdmi", "v4t") + .Cases("arm920", "arm920t", "arm922t", "v4t") + .Cases("arm940t", "ep9312","v4t") + .Cases("arm10tdmi", "arm1020t", "v5") + .Cases("arm9e", "arm926ej-s", "arm946e-s", "v5e") + .Cases("arm966e-s", "arm968e-s", "arm10e", "v5e") + .Cases("arm1020e", "arm1022e", "xscale", "iwmmxt", "v5e") + .Cases("arm1136j-s", "arm1136jf-s", "arm1176jz-s", "v6") + .Cases("arm1176jzf-s", "mpcorenovfp", "mpcore", "v6") + .Cases("arm1156t2-s", "arm1156t2f-s", "v6t2") + .Cases("cortex-a5", "cortex-a7", "cortex-a8", "v7") + .Cases("cortex-a9", "cortex-a15", "v7") + .Case("cortex-r5", "v7r") + .Case("cortex-m0", "v6m") + .Case("cortex-m3", "v7m") + .Case("cortex-m4", "v7em") + .Case("cortex-a9-mp", "v7f") + .Case("swift", "v7s") + .Default(""); +} + +static std::string getARMTargetCPU(std::string arch, const llvm::Triple &triple) +{ + // FIXME: Warn on inconsistent use of -mcpu and -march. + + llvm::StringRef MArch; + if (!arch.empty()) { + // Otherwise, if we have -march= choose the base CPU for that arch. + MArch = arch; + } else { + // Otherwise, use the Arch from the triple. + MArch = triple.getArchName(); + } + + // Handle -march=native. + std::string NativeMArch; + if (MArch == "native") { + std::string CPU = llvm::sys::getHostCPUName(); + if (CPU != "generic") { + // Translate the native cpu into the architecture. The switch below will + // then chose the minimum cpu for that arch. + NativeMArch = std::string("arm") + getLLVMArchSuffixForARM(CPU); + MArch = NativeMArch; + } + } + + return llvm::StringSwitch(MArch) + .Cases("armv2", "armv2a","arm2") + .Case("armv3", "arm6") + .Case("armv3m", "arm7m") + .Cases("armv4", "armv4t", "arm7tdmi") + .Cases("armv5", "armv5t", "arm10tdmi") + .Cases("armv5e", "armv5te", "arm1026ejs") + .Case("armv5tej", "arm926ej-s") + .Cases("armv6", "armv6k", "arm1136jf-s") + .Case("armv6j", "arm1136j-s") + .Cases("armv6z", "armv6zk", "arm1176jzf-s") + .Case("armv6t2", "arm1156t2-s") + .Cases("armv6m", "armv6-m", "cortex-m0") + .Cases("armv7", "armv7a", "armv7-a", "cortex-a8") + .Cases("armv7l", "armv7-l", "cortex-a8") + .Cases("armv7f", "armv7-f", "cortex-a9-mp") + .Cases("armv7s", "armv7-s", "swift") + .Cases("armv7r", "armv7-r", "cortex-r4") + .Cases("armv7m", "armv7-m", "cortex-m3") + .Cases("armv7em", "armv7e-m", "cortex-m4") + .Case("ep9312", "ep9312") + .Case("iwmmxt", "iwmmxt") + .Case("xscale", "xscale") + // If all else failed, return the most base CPU LLVM supports. + .Default("arm7tdmi"); +} + +static FloatABI::Type getARMFloatABI(const llvm::Triple &triple, + const char* llvmArchSuffix) +{ + switch (triple.getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: { + // Darwin defaults to "softfp" for v6 and v7. + if (llvm::StringRef(llvmArchSuffix).startswith("v6") || + llvm::StringRef(llvmArchSuffix).startswith("v7")) + return FloatABI::SoftFP; + return FloatABI::Soft; + } + + case llvm::Triple::FreeBSD: + // FreeBSD defaults to soft float + return FloatABI::Soft; + + default: + switch(triple.getEnvironment()) { + case llvm::Triple::GNUEABIHF: + return FloatABI::Hard; + case llvm::Triple::GNUEABI: + return FloatABI::SoftFP; + case llvm::Triple::EABI: + // EABI is always AAPCS, and if it was not marked 'hard', it's softfp + return FloatABI::SoftFP; + case llvm::Triple::Android: { + if (llvm::StringRef(llvmArchSuffix).startswith("v7")) + return FloatABI::SoftFP; + return FloatABI::Soft; + } + default: + // Assume "soft", but warn the user we are guessing. + return FloatABI::SoftFP; + } + } +} + + llvm::TargetMachine* createTargetMachine( std::string targetTriple, std::string arch, std::string cpu, std::vector attrs, ExplicitBitness::Type bitness, + FloatABI::Type floatABI, llvm::Reloc::Model relocModel, llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel, @@ -159,10 +278,16 @@ llvm::TargetMachine* createTargetMachine( // to default to "generic"). if (cpu.empty()) { - if (triple.getArch() == llvm::Triple::x86_64 || - triple.getArch() == llvm::Triple::x86) + switch (triple.getArch()) { + default: break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: cpu = getX86TargetCPU(arch, triple); + break; + case llvm::Triple::arm: + cpu = getARMTargetCPU(arch, triple); + break; } } @@ -176,8 +301,25 @@ llvm::TargetMachine* createTargetMachine( FeaturesStr = Features.getString(); } + if (floatABI == FloatABI::Default) + { + switch (triple.getArch()) + { + default: // X86, ... + floatABI = FloatABI::Hard; + break; + case llvm::Triple::arm: + floatABI = getARMFloatABI(triple, getLLVMArchSuffixForARM(cpu)); + break; + case llvm::Triple::thumb: + floatABI = FloatABI::Soft; + break; + } + } + #if LDC_LLVM_VER == 300 llvm::NoFramePointerElim = genDebugInfo; + // FIXME: Handle floating-point ABI. return theTarget->createTargetMachine(triple.str(), cpu, FeaturesStr, relocModel, codeModel); @@ -185,6 +327,23 @@ llvm::TargetMachine* createTargetMachine( llvm::TargetOptions targetOptions; targetOptions.NoFramePointerElim = genDebugInfo; + switch (floatABI) + { + default: llvm_unreachable("Floating point ABI type unknown."); + case FloatABI::Soft: + targetOptions.UseSoftFloat = true; + targetOptions.FloatABIType = llvm::FloatABI::Soft; + break; + case FloatABI::SoftFP: + targetOptions.UseSoftFloat = false; + targetOptions.FloatABIType = llvm::FloatABI::Soft; + break; + case FloatABI::Hard: + targetOptions.UseSoftFloat = false; + targetOptions.FloatABIType = llvm::FloatABI::Hard; + break; + } + return theTarget->createTargetMachine( triple.str(), cpu, diff --git a/driver/target.h b/driver/target.h index 4eb4c635..e14b5ed4 100644 --- a/driver/target.h +++ b/driver/target.h @@ -30,6 +30,15 @@ namespace ExplicitBitness { }; } +namespace FloatABI { + enum Type { + Default, + Soft, + SoftFP, + Hard + }; +} + namespace llvm { class TargetMachine; } /** @@ -44,6 +53,7 @@ llvm::TargetMachine* createTargetMachine( std::string cpu, std::vector attrs, ExplicitBitness::Type bitness, + FloatABI::Type floatABI, llvm::Reloc::Model relocModel, llvm::CodeModel::Model codeModel, llvm::CodeGenOpt::Level codeGenOptLevel,