| //===--- Mips.h - Declare Mips target feature support -----------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file declares Mips TargetInfo objects. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_LIB_BASIC_TARGETS_MIPS_H |
| #define LLVM_CLANG_LIB_BASIC_TARGETS_MIPS_H |
| |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Basic/TargetOptions.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/Support/Compiler.h" |
| |
| namespace clang { |
| namespace targets { |
| |
| class LLVM_LIBRARY_VISIBILITY MipsTargetInfo : public TargetInfo { |
| void setDataLayout() { |
| StringRef Layout; |
| |
| if (ABI == "o32") |
| Layout = "m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"; |
| else if (ABI == "n32") |
| Layout = "m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128"; |
| else if (ABI == "n64") |
| Layout = "m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"; |
| else |
| llvm_unreachable("Invalid ABI"); |
| |
| if (BigEndian) |
| resetDataLayout(("E-" + Layout).str()); |
| else |
| resetDataLayout(("e-" + Layout).str()); |
| } |
| |
| static const Builtin::Info BuiltinInfo[]; |
| std::string CPU; |
| bool IsMips16; |
| bool IsMicromips; |
| bool IsNan2008; |
| bool IsAbs2008; |
| bool IsSingleFloat; |
| bool IsNoABICalls; |
| bool CanUseBSDABICalls; |
| enum MipsFloatABI { HardFloat, SoftFloat } FloatABI; |
| enum DspRevEnum { NoDSP, DSP1, DSP2 } DspRev; |
| bool HasMSA; |
| bool DisableMadd4; |
| bool UseIndirectJumpHazard; |
| |
| protected: |
| enum FPModeEnum { FPXX, FP32, FP64 } FPMode; |
| std::string ABI; |
| |
| public: |
| MipsTargetInfo(const llvm::Triple &Triple, const TargetOptions &) |
| : TargetInfo(Triple), IsMips16(false), IsMicromips(false), |
| IsNan2008(false), IsAbs2008(false), IsSingleFloat(false), |
| IsNoABICalls(false), CanUseBSDABICalls(false), FloatABI(HardFloat), |
| DspRev(NoDSP), HasMSA(false), DisableMadd4(false), |
| UseIndirectJumpHazard(false), FPMode(FPXX) { |
| TheCXXABI.set(TargetCXXABI::GenericMIPS); |
| |
| if (Triple.isMIPS32()) |
| setABI("o32"); |
| else if (Triple.getEnvironment() == llvm::Triple::GNUABIN32) |
| setABI("n32"); |
| else |
| setABI("n64"); |
| |
| CPU = ABI == "o32" ? "mips32r2" : "mips64r2"; |
| |
| CanUseBSDABICalls = Triple.isOSFreeBSD() || |
| Triple.isOSOpenBSD(); |
| } |
| |
| bool isIEEE754_2008Default() const { |
| return CPU == "mips32r6" || CPU == "mips64r6"; |
| } |
| |
| bool isFP64Default() const { |
| return CPU == "mips32r6" || ABI == "n32" || ABI == "n64" || ABI == "64"; |
| } |
| |
| bool isNan2008() const override { return IsNan2008; } |
| |
| bool processorSupportsGPR64() const; |
| |
| StringRef getABI() const override { return ABI; } |
| |
| bool setABI(const std::string &Name) override { |
| if (Name == "o32") { |
| setO32ABITypes(); |
| ABI = Name; |
| return true; |
| } |
| |
| if (Name == "n32") { |
| setN32ABITypes(); |
| ABI = Name; |
| return true; |
| } |
| if (Name == "n64") { |
| setN64ABITypes(); |
| ABI = Name; |
| return true; |
| } |
| return false; |
| } |
| |
| void setO32ABITypes() { |
| Int64Type = SignedLongLong; |
| IntMaxType = Int64Type; |
| LongDoubleFormat = &llvm::APFloat::IEEEdouble(); |
| LongDoubleWidth = LongDoubleAlign = 64; |
| LongWidth = LongAlign = 32; |
| MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32; |
| PointerWidth = PointerAlign = 32; |
| PtrDiffType = SignedInt; |
| SizeType = UnsignedInt; |
| SuitableAlign = 64; |
| } |
| |
| void setN32N64ABITypes() { |
| LongDoubleWidth = LongDoubleAlign = 128; |
| LongDoubleFormat = &llvm::APFloat::IEEEquad(); |
| if (getTriple().isOSFreeBSD()) { |
| LongDoubleWidth = LongDoubleAlign = 64; |
| LongDoubleFormat = &llvm::APFloat::IEEEdouble(); |
| } |
| MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; |
| SuitableAlign = 128; |
| } |
| |
| void setN64ABITypes() { |
| setN32N64ABITypes(); |
| if (getTriple().isOSOpenBSD()) { |
| Int64Type = SignedLongLong; |
| } else { |
| Int64Type = SignedLong; |
| } |
| IntMaxType = Int64Type; |
| LongWidth = LongAlign = 64; |
| PointerWidth = PointerAlign = 64; |
| PtrDiffType = SignedLong; |
| SizeType = UnsignedLong; |
| } |
| |
| void setN32ABITypes() { |
| setN32N64ABITypes(); |
| Int64Type = SignedLongLong; |
| IntMaxType = Int64Type; |
| LongWidth = LongAlign = 32; |
| PointerWidth = PointerAlign = 32; |
| PtrDiffType = SignedInt; |
| SizeType = UnsignedInt; |
| } |
| |
| bool isValidCPUName(StringRef Name) const override; |
| void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override; |
| |
| bool setCPU(const std::string &Name) override { |
| CPU = Name; |
| return isValidCPUName(Name); |
| } |
| |
| const std::string &getCPU() const { return CPU; } |
| bool |
| initFeatureMap(llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, |
| StringRef CPU, |
| const std::vector<std::string> &FeaturesVec) const override { |
| if (CPU.empty()) |
| CPU = getCPU(); |
| if (CPU == "octeon") |
| Features["mips64r2"] = Features["cnmips"] = true; |
| else |
| Features[CPU] = true; |
| return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); |
| } |
| |
| unsigned getISARev() const; |
| |
| void getTargetDefines(const LangOptions &Opts, |
| MacroBuilder &Builder) const override; |
| |
| ArrayRef<Builtin::Info> getTargetBuiltins() const override; |
| |
| bool hasFeature(StringRef Feature) const override; |
| |
| BuiltinVaListKind getBuiltinVaListKind() const override { |
| return TargetInfo::VoidPtrBuiltinVaList; |
| } |
| |
| ArrayRef<const char *> getGCCRegNames() const override { |
| static const char *const GCCRegNames[] = { |
| // CPU register names |
| // Must match second column of GCCRegAliases |
| "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", |
| "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", "$20", |
| "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "$30", |
| "$31", |
| // Floating point register names |
| "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", |
| "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", |
| "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", |
| "$f28", "$f29", "$f30", "$f31", |
| // Hi/lo and condition register names |
| "hi", "lo", "", "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", |
| "$fcc6", "$fcc7", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo", "$ac3hi", |
| "$ac3lo", |
| // MSA register names |
| "$w0", "$w1", "$w2", "$w3", "$w4", "$w5", "$w6", "$w7", "$w8", "$w9", |
| "$w10", "$w11", "$w12", "$w13", "$w14", "$w15", "$w16", "$w17", "$w18", |
| "$w19", "$w20", "$w21", "$w22", "$w23", "$w24", "$w25", "$w26", "$w27", |
| "$w28", "$w29", "$w30", "$w31", |
| // MSA control register names |
| "$msair", "$msacsr", "$msaaccess", "$msasave", "$msamodify", |
| "$msarequest", "$msamap", "$msaunmap" |
| }; |
| return llvm::makeArrayRef(GCCRegNames); |
| } |
| |
| bool validateAsmConstraint(const char *&Name, |
| TargetInfo::ConstraintInfo &Info) const override { |
| switch (*Name) { |
| default: |
| return false; |
| case 'r': // CPU registers. |
| case 'd': // Equivalent to "r" unless generating MIPS16 code. |
| case 'y': // Equivalent to "r", backward compatibility only. |
| case 'f': // floating-point registers. |
| case 'c': // $25 for indirect jumps |
| case 'l': // lo register |
| case 'x': // hilo register pair |
| Info.setAllowsRegister(); |
| return true; |
| case 'I': // Signed 16-bit constant |
| case 'J': // Integer 0 |
| case 'K': // Unsigned 16-bit constant |
| case 'L': // Signed 32-bit constant, lower 16-bit zeros (for lui) |
| case 'M': // Constants not loadable via lui, addiu, or ori |
| case 'N': // Constant -1 to -65535 |
| case 'O': // A signed 15-bit constant |
| case 'P': // A constant between 1 go 65535 |
| return true; |
| case 'R': // An address that can be used in a non-macro load or store |
| Info.setAllowsMemory(); |
| return true; |
| case 'Z': |
| if (Name[1] == 'C') { // An address usable by ll, and sc. |
| Info.setAllowsMemory(); |
| Name++; // Skip over 'Z'. |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| std::string convertConstraint(const char *&Constraint) const override { |
| std::string R; |
| switch (*Constraint) { |
| case 'Z': // Two-character constraint; add "^" hint for later parsing. |
| if (Constraint[1] == 'C') { |
| R = std::string("^") + std::string(Constraint, 2); |
| Constraint++; |
| return R; |
| } |
| break; |
| } |
| return TargetInfo::convertConstraint(Constraint); |
| } |
| |
| const char *getClobbers() const override { |
| // In GCC, $1 is not widely used in generated code (it's used only in a few |
| // specific situations), so there is no real need for users to add it to |
| // the clobbers list if they want to use it in their inline assembly code. |
| // |
| // In LLVM, $1 is treated as a normal GPR and is always allocatable during |
| // code generation, so using it in inline assembly without adding it to the |
| // clobbers list can cause conflicts between the inline assembly code and |
| // the surrounding generated code. |
| // |
| // Another problem is that LLVM is allowed to choose $1 for inline assembly |
| // operands, which will conflict with the ".set at" assembler option (which |
| // we use only for inline assembly, in order to maintain compatibility with |
| // GCC) and will also conflict with the user's usage of $1. |
| // |
| // The easiest way to avoid these conflicts and keep $1 as an allocatable |
| // register for generated code is to automatically clobber $1 for all inline |
| // assembly code. |
| // |
| // FIXME: We should automatically clobber $1 only for inline assembly code |
| // which actually uses it. This would allow LLVM to use $1 for inline |
| // assembly operands if the user's assembly code doesn't use it. |
| return "~{$1}"; |
| } |
| |
| bool handleTargetFeatures(std::vector<std::string> &Features, |
| DiagnosticsEngine &Diags) override { |
| IsMips16 = false; |
| IsMicromips = false; |
| IsNan2008 = isIEEE754_2008Default(); |
| IsAbs2008 = isIEEE754_2008Default(); |
| IsSingleFloat = false; |
| FloatABI = HardFloat; |
| DspRev = NoDSP; |
| FPMode = isFP64Default() ? FP64 : FPXX; |
| |
| for (const auto &Feature : Features) { |
| if (Feature == "+single-float") |
| IsSingleFloat = true; |
| else if (Feature == "+soft-float") |
| FloatABI = SoftFloat; |
| else if (Feature == "+mips16") |
| IsMips16 = true; |
| else if (Feature == "+micromips") |
| IsMicromips = true; |
| else if (Feature == "+dsp") |
| DspRev = std::max(DspRev, DSP1); |
| else if (Feature == "+dspr2") |
| DspRev = std::max(DspRev, DSP2); |
| else if (Feature == "+msa") |
| HasMSA = true; |
| else if (Feature == "+nomadd4") |
| DisableMadd4 = true; |
| else if (Feature == "+fp64") |
| FPMode = FP64; |
| else if (Feature == "-fp64") |
| FPMode = FP32; |
| else if (Feature == "+fpxx") |
| FPMode = FPXX; |
| else if (Feature == "+nan2008") |
| IsNan2008 = true; |
| else if (Feature == "-nan2008") |
| IsNan2008 = false; |
| else if (Feature == "+abs2008") |
| IsAbs2008 = true; |
| else if (Feature == "-abs2008") |
| IsAbs2008 = false; |
| else if (Feature == "+noabicalls") |
| IsNoABICalls = true; |
| else if (Feature == "+use-indirect-jump-hazard") |
| UseIndirectJumpHazard = true; |
| } |
| |
| setDataLayout(); |
| |
| return true; |
| } |
| |
| int getEHDataRegisterNumber(unsigned RegNo) const override { |
| if (RegNo == 0) |
| return 4; |
| if (RegNo == 1) |
| return 5; |
| return -1; |
| } |
| |
| bool isCLZForZeroUndef() const override { return false; } |
| |
| ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override { |
| static const TargetInfo::GCCRegAlias O32RegAliases[] = { |
| {{"at"}, "$1"}, {{"v0"}, "$2"}, {{"v1"}, "$3"}, |
| {{"a0"}, "$4"}, {{"a1"}, "$5"}, {{"a2"}, "$6"}, |
| {{"a3"}, "$7"}, {{"t0"}, "$8"}, {{"t1"}, "$9"}, |
| {{"t2"}, "$10"}, {{"t3"}, "$11"}, {{"t4"}, "$12"}, |
| {{"t5"}, "$13"}, {{"t6"}, "$14"}, {{"t7"}, "$15"}, |
| {{"s0"}, "$16"}, {{"s1"}, "$17"}, {{"s2"}, "$18"}, |
| {{"s3"}, "$19"}, {{"s4"}, "$20"}, {{"s5"}, "$21"}, |
| {{"s6"}, "$22"}, {{"s7"}, "$23"}, {{"t8"}, "$24"}, |
| {{"t9"}, "$25"}, {{"k0"}, "$26"}, {{"k1"}, "$27"}, |
| {{"gp"}, "$28"}, {{"sp", "$sp"}, "$29"}, {{"fp", "$fp"}, "$30"}, |
| {{"ra"}, "$31"} |
| }; |
| static const TargetInfo::GCCRegAlias NewABIRegAliases[] = { |
| {{"at"}, "$1"}, {{"v0"}, "$2"}, {{"v1"}, "$3"}, |
| {{"a0"}, "$4"}, {{"a1"}, "$5"}, {{"a2"}, "$6"}, |
| {{"a3"}, "$7"}, {{"a4"}, "$8"}, {{"a5"}, "$9"}, |
| {{"a6"}, "$10"}, {{"a7"}, "$11"}, {{"t0"}, "$12"}, |
| {{"t1"}, "$13"}, {{"t2"}, "$14"}, {{"t3"}, "$15"}, |
| {{"s0"}, "$16"}, {{"s1"}, "$17"}, {{"s2"}, "$18"}, |
| {{"s3"}, "$19"}, {{"s4"}, "$20"}, {{"s5"}, "$21"}, |
| {{"s6"}, "$22"}, {{"s7"}, "$23"}, {{"t8"}, "$24"}, |
| {{"t9"}, "$25"}, {{"k0"}, "$26"}, {{"k1"}, "$27"}, |
| {{"gp"}, "$28"}, {{"sp", "$sp"}, "$29"}, {{"fp", "$fp"}, "$30"}, |
| {{"ra"}, "$31"} |
| }; |
| if (ABI == "o32") |
| return llvm::makeArrayRef(O32RegAliases); |
| return llvm::makeArrayRef(NewABIRegAliases); |
| } |
| |
| bool hasInt128Type() const override { |
| return (ABI == "n32" || ABI == "n64") || getTargetOpts().ForceEnableInt128; |
| } |
| |
| unsigned getUnwindWordWidth() const override; |
| |
| bool validateTarget(DiagnosticsEngine &Diags) const override; |
| }; |
| } // namespace targets |
| } // namespace clang |
| |
| #endif // LLVM_CLANG_LIB_BASIC_TARGETS_MIPS_H |