Index: lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -46,12 +46,10 @@ bool isReorder() { return reorder; } void setReorder() { reorder = true; } void setNoreorder() { reorder = false; } - void setFpAbiMode(int Mode) { fpAbiMode = Mode; } bool isMacro() { return macro; } void setMacro() { macro = true; } void setNomacro() { macro = false; } - int getFpAbiMode() { return fpAbiMode; } private: unsigned aTReg; @@ -167,6 +165,7 @@ bool parseDirectiveGpWord(); bool parseDirectiveGpDWord(); bool parseDirectiveModule(); + bool parseDirectiveModuleFP(); MCSymbolRefExpr::VariantKind getVariantKind(StringRef Symbol); @@ -2492,7 +2491,6 @@ reportParseError("unexpected token in statement"); return false; } - Options.setFpAbiMode(FpAbiMode); getTargetStreamer().emitDirectiveSetFp(FpAbiMode, isABI_O32()); Parser.Lex(); // Consume the EndOfStatement. return false; @@ -2813,42 +2811,72 @@ return false; } +/// parseDirectiveModule +/// ::= .module oddspreg +/// ::= .module nooddspreg +/// ::= .module fp=value bool MipsAsmParser::parseDirectiveModule() { - // Line can be: .module fp=32 - // .module fp=xx - // .module fp=64 - unsigned FpAbiVal = 0; + MCAsmLexer &Lexer = getLexer(); + SMLoc L = Lexer.getLoc(); + if (!getTargetStreamer().getCanHaveModuleDir()) { // TODO : get a better message. reportParseError(".module directive must appear before any code"); return false; } - AsmToken Tok = Parser.getTok(); - if (Tok.isNot(AsmToken::Identifier) && Tok.getString() != "fp") { - reportParseError("unexpected token in .module directive, 'fp' expected"); - return false; + + if (Lexer.is(AsmToken::Identifier)) { + StringRef Option = Parser.getTok().getString(); + Parser.Lex(); + + if (Option == "oddspreg") { + getTargetStreamer().emitDirectiveModuleOddSPReg(true); + return false; + } else if (Option == "nooddspreg") { + getTargetStreamer().emitDirectiveModuleOddSPReg(false); + return false; + } else if (Option == "fp") { + return parseDirectiveModuleFP(); + } + + return Error(L, "'" + Twine(Option) + "' is not a valid .module option."); } - Parser.Lex(); // Eat fp token - Tok = Parser.getTok(); - if (Tok.isNot(AsmToken::Equal)) { + + return false; +} + +/// parseDirectiveModuleFP +/// ::= =32 +/// ::= =xx +/// ::= =64 +bool MipsAsmParser::parseDirectiveModuleFP() { + MCAsmLexer &Lexer = getLexer(); + + if (Lexer.isNot(AsmToken::Equal)) { reportParseError("unexpected token in statement"); return false; } Parser.Lex(); // Eat '=' token. - Tok = Parser.getTok(); - if (Tok.is(AsmToken::Identifier)) { - StringRef XX = Tok.getString(); - if (XX != "xx") { + + unsigned FpAbiVal = 0; + if (Lexer.is(AsmToken::Identifier)) { + StringRef Value = Parser.getTok().getString(); + Parser.Lex(); + + if (Value != "xx") { reportParseError("unsupported option"); return false; } + FpAbiVal = Val_GNU_MIPS_ABI_FP_XX; - } else if (Tok.is(AsmToken::Integer)) { - unsigned Value = Tok.getIntVal(); + } else if (Lexer.is(AsmToken::Integer)) { + unsigned Value = Lexer.getTok().getIntVal(); + if (Value != 32 && Value != 64) { reportParseError("unsupported value, expected 32 or 64"); return false; } + if (Value == 64) { if (isABI_N32() || isABI_N64()) FpAbiVal = Val_GNU_MIPS_ABI_FP_DOUBLE; @@ -2856,18 +2884,21 @@ FpAbiVal = Val_GNU_MIPS_ABI_FP_64; } else if (isABI_O32()) FpAbiVal = Val_GNU_MIPS_ABI_FP_DOUBLE; + + Parser.Lex(); // Eat option token. } - Parser.Lex(); // Eat option token. + if (getLexer().isNot(AsmToken::EndOfStatement)) { reportParseError("unexpected token in statement"); return false; } + // Emit appropriate flags. - getTargetStreamer().emitDirectiveModule(FpAbiVal, isABI_O32()); - getTargetStreamer().setFpABI(FpAbiVal); + getTargetStreamer().emitDirectiveModuleFP(FpAbiVal, isABI_O32()); Parser.Lex(); // Consume the EndOfStatement. return false; } + bool MipsAsmParser::ParseDirective(AsmToken DirectiveID) { StringRef IDVal = DirectiveID.getString(); Index: lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp =================================================================== --- lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp +++ lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp @@ -59,6 +59,7 @@ void MipsTargetStreamer::emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, const MCSymbol &Sym, bool IsReg) { } +void MipsTargetStreamer::emitDirectiveModuleOddSPReg(bool Enabled) {} MipsTargetAsmStreamer::MipsTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS) @@ -211,8 +212,10 @@ setCanHaveModuleDir(false); } -void MipsTargetAsmStreamer::emitDirectiveModule(unsigned Value, - bool is32BitAbi) { +void MipsTargetAsmStreamer::emitDirectiveModuleFP(unsigned Value, + bool is32BitAbi) { + MipsTargetStreamer::emitDirectiveModuleFP(Value, is32BitAbi); + StringRef ModuleValue; OS << "\t.module\tfp="; switch (Value) { @@ -261,6 +264,10 @@ // No action required for text output. } +void MipsTargetAsmStreamer::emitDirectiveModuleOddSPReg(bool Enabled) { + OS << "\t.module\t" << (Enabled ? "" : "no") << "oddspreg\n"; +} + // This part is for ELF object output. MipsTargetELFStreamer::MipsTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) @@ -670,6 +677,10 @@ OS.EmitIntValue(MipsABIFlags.fp_abi, 1); // fp_abi OS.EmitIntValue(MipsABIFlags.isa_ext, 4); // isa_ext OS.EmitIntValue(MipsABIFlags.ases, 4); // ases - OS.EmitIntValue(MipsABIFlags.flags1, 4); // flags1 - OS.EmitIntValue(MipsABIFlags.flags2, 4); // flags2 + OS.EmitIntValue(MipsABIFlags.getFlags1(), 4); + OS.EmitIntValue(MipsABIFlags.getFlags2(), 4); +} + +void MipsTargetELFStreamer::emitDirectiveModuleOddSPReg(bool Enabled) { + MipsABIFlags.OddSPReg = Enabled; } Index: lib/Target/Mips/Mips.td =================================================================== --- lib/Target/Mips/Mips.td +++ lib/Target/Mips/Mips.td @@ -73,6 +73,9 @@ "Enable n64 ABI">; def FeatureEABI : SubtargetFeature<"eabi", "MipsABI", "EABI", "Enable eabi ABI">; +def FeatureOddSPReg : SubtargetFeature<"nooddspreg", "UseOddSPReg", "false", + "Disable odd numbered single-precision " + "registers">; def FeatureVFPU : SubtargetFeature<"vfpu", "HasVFPU", "true", "Enable vector FPU instructions.">; def FeatureMips1 : SubtargetFeature<"mips1", "MipsArchVersion", "Mips1", Index: lib/Target/Mips/MipsAsmPrinter.cpp =================================================================== --- lib/Target/Mips/MipsAsmPrinter.cpp +++ lib/Target/Mips/MipsAsmPrinter.cpp @@ -704,6 +704,7 @@ OutContext.getELFSection(".gcc_compiled_long64", ELF::SHT_PROGBITS, 0, SectionKind::getDataRel())); } + getTargetStreamer().updateABIInfo(*Subtarget); unsigned FpAbiVal; if (Subtarget->isABI_N32() || Subtarget->isABI_N64()) @@ -716,7 +717,10 @@ else FpAbiVal = Val_GNU_MIPS_ABI_FP_DOUBLE; } - getTargetStreamer().emitDirectiveModule(FpAbiVal, Subtarget->isABI_O32()); + getTargetStreamer().emitDirectiveModuleFP(FpAbiVal, Subtarget->isABI_O32()); + + if (Subtarget->isFP64bit()) + getTargetStreamer().emitDirectiveModuleOddSPReg(Subtarget->useOddSPReg()); } void MipsAsmPrinter::EmitJal(MCSymbol *Symbol) { Index: lib/Target/Mips/MipsRegisterInfo.cpp =================================================================== --- lib/Target/Mips/MipsRegisterInfo.cpp +++ lib/Target/Mips/MipsRegisterInfo.cpp @@ -201,6 +201,11 @@ Reserved.set(Mips::GP_64); } + if (Subtarget.isFP64bit() && !Subtarget.useOddSPReg()) { + for (const auto &Reg : Mips::OddSPRegClass) + Reserved.set(Reg); + } + return Reserved; } Index: lib/Target/Mips/MipsRegisterInfo.td =================================================================== --- lib/Target/Mips/MipsRegisterInfo.td +++ lib/Target/Mips/MipsRegisterInfo.td @@ -340,6 +340,12 @@ def FGR64 : RegisterClass<"Mips", [f64], 64, (sequence "D%u_64", 0, 31)>; +// Used to reserve odd registers when given -mattr=+nooddspreg +def OddSP : RegisterClass<"Mips", [f32], 32, + (add (decimate (sequence "F%u", 1, 31), 2), + (decimate (sequence "F_HI%u", 1, 31), 2))>, + Unallocatable; + // FP control registers. def CCR : RegisterClass<"Mips", [i32], 32, (sequence "FCR%u", 0, 31)>, Unallocatable; Index: lib/Target/Mips/MipsSubtarget.h =================================================================== --- lib/Target/Mips/MipsSubtarget.h +++ lib/Target/Mips/MipsSubtarget.h @@ -60,6 +60,10 @@ // IsFP64bit - The target processor has 64-bit floating point registers. bool IsFP64bit; + /// Are odd single-precision registers permitted? + /// This corresponds to -modd-spreg and -mno-odd-spreg + bool UseOddSPReg; + // IsNan2008 - IEEE 754-2008 NaN encoding. bool IsNaN2008bit; @@ -186,6 +190,7 @@ bool isLittle() const { return IsLittle; } bool isFP64bit() const { return IsFP64bit; } + bool useOddSPReg() const { return UseOddSPReg; } bool isNaN2008() const { return IsNaN2008bit; } bool isNotFP64bit() const { return !IsFP64bit; } bool isGP64bit() const { return IsGP64bit; } Index: lib/Target/Mips/MipsSubtarget.cpp =================================================================== --- lib/Target/Mips/MipsSubtarget.cpp +++ lib/Target/Mips/MipsSubtarget.cpp @@ -77,13 +77,14 @@ Reloc::Model _RM, MipsTargetMachine *_TM) : MipsGenSubtargetInfo(TT, CPU, FS), MipsArchVersion(Mips32), MipsABI(UnknownABI), IsLittle(little), IsSingleFloat(false), - IsFP64bit(false), IsNaN2008bit(false), IsGP64bit(false), HasVFPU(false), - HasCnMips(false), IsLinux(true), HasMips3_32(false), HasMips3_32r2(false), - HasMips4_32(false), HasMips4_32r2(false), HasMips5_32r2(false), - InMips16Mode(false), InMips16HardFloat(Mips16HardFloat), - InMicroMipsMode(false), HasDSP(false), HasDSPR2(false), - AllowMixed16_32(Mixed16_32 | Mips_Os16), Os16(Mips_Os16), HasMSA(false), - RM(_RM), OverrideMode(NoOverride), TM(_TM), TargetTriple(TT), JITInfo() { + IsFP64bit(false), UseOddSPReg(true), IsNaN2008bit(false), + IsGP64bit(false), HasVFPU(false), HasCnMips(false), IsLinux(true), + HasMips3_32(false), HasMips3_32r2(false), HasMips4_32(false), + HasMips4_32r2(false), HasMips5_32r2(false), InMips16Mode(false), + InMips16HardFloat(Mips16HardFloat), InMicroMipsMode(false), HasDSP(false), + HasDSPR2(false), AllowMixed16_32(Mixed16_32 | Mips_Os16), Os16(Mips_Os16), + HasMSA(false), RM(_RM), OverrideMode(NoOverride), TM(_TM), + TargetTriple(TT), JITInfo() { initializeSubtargetDependencies(CPU, FS); @@ -129,6 +130,10 @@ "See -mattr=+fp64.", false); + if (!isFP64bit() && !useOddSPReg()) + report_fatal_error("-mattr=+nooddspreg is not currently permitted for a " + "32-bit FPU register file (FR=0 mode).", + false); if (hasMips32r6()) { StringRef ISA = hasMips64r6() ? "MIPS64r6" : "MIPS32r6"; Index: lib/Target/Mips/MipsTargetStreamer.h =================================================================== --- lib/Target/Mips/MipsTargetStreamer.h +++ lib/Target/Mips/MipsTargetStreamer.h @@ -15,6 +15,8 @@ namespace llvm { struct Elf_Internal_ABIFlags_v0 { + enum class AFL_FLAGS1 : uint32_t { ODDSPREG = 1 }; + // Version of flags structure. uint16_t version; // The level of the ISA: 1-5, 32, 64. @@ -33,13 +35,23 @@ uint32_t isa_ext; // Mask of ASEs used. uint32_t ases; - // Mask of general flags. - uint32_t flags1; - uint32_t flags2; + + bool OddSPReg; Elf_Internal_ABIFlags_v0() : version(0), isa_level(0), isa_rev(0), gpr_size(0), cpr1_size(0), - cpr2_size(0), fp_abi(0), isa_ext(0), ases(0), flags1(0), flags2(0) {} + cpr2_size(0), fp_abi(0), isa_ext(0), ases(0), OddSPReg(false) {} + + uint32_t getFlags1() { + uint32_t Value = 0; + + if (OddSPReg) + Value |= (uint32_t)AFL_FLAGS1::ODDSPREG; + + return Value; + } + + uint32_t getFlags2() { return 0; } }; // Values for the xxx_size bytes of an ABI flags structure. @@ -133,7 +145,10 @@ virtual void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, const MCSymbol &Sym, bool IsReg); // ABI Flags - virtual void emitDirectiveModule(unsigned Value, bool is32BitAbi){}; + virtual void emitDirectiveModuleFP(unsigned Value, bool is32BitAbi) { + setFpABI(Value); + } + virtual void emitDirectiveModuleOddSPReg(bool Enabled); virtual void emitDirectiveSetFp(unsigned Value, bool is32BitAbi){}; virtual void emitMipsAbiFlags(){}; void setCanHaveModuleDir(bool Can) { canHaveModuleDirective = Can; } @@ -148,8 +163,6 @@ void setFpABI(uint8_t Abi) { MipsABIFlags.fp_abi = Abi; } void setIsaExt(uint32_t IsaExt) { MipsABIFlags.isa_ext = IsaExt; } void setASEs(uint32_t Ases) { MipsABIFlags.ases = Ases; } - void setFlags1(uint32_t Flags) { MipsABIFlags.flags1 = Flags; } - void setFlags2(uint32_t Flags) { MipsABIFlags.flags2 = Flags; } uint8_t getFPAbi() { return MipsABIFlags.fp_abi; } // This method enables template classes to set internal abi flags @@ -278,7 +291,8 @@ const MCSymbol &Sym, bool IsReg) override; // ABI Flags - void emitDirectiveModule(unsigned Value, bool is32BitAbi) override; + void emitDirectiveModuleFP(unsigned Value, bool is32BitAbi) override; + void emitDirectiveModuleOddSPReg(bool Enabled) override; void emitDirectiveSetFp(unsigned Value, bool is32BitAbi) override; void emitMipsAbiFlags() override; }; @@ -333,6 +347,7 @@ const MCSymbol &Sym, bool IsReg) override; // ABI Flags + void emitDirectiveModuleOddSPReg(bool Enabled) override; void emitMipsAbiFlags() override; protected: Index: test/CodeGen/Mips/no-odd-spreg.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/no-odd-spreg.ll @@ -0,0 +1,55 @@ +; RUN: llc -march=mipsel -mcpu=mips32r6 -mattr=fp64 < %s | FileCheck %s -check-prefix=ALL -check-prefix=ODDSPREG +; RUN: llc -march=mipsel -mcpu=mips32r6 -mattr=fp64,+nooddspreg < %s | FileCheck %s -check-prefix=ALL -check-prefix=NOODDSPREG + +; RUN: not llc -march=mipsel -mcpu=mips32 -mattr=+nooddspreg < %s > /dev/null 2> %t0 +; RUN: FileCheck -check-prefix=INVALID %s < %t0 + +; ODDSPREG: .module oddspreg +; NOODDSPREG: .module nooddspreg + +define float @two_floats(float %a) { +entry: + ; Clobber all except $f12 and $f13 + ; + ; The intention is that if odd single precision registers are permitted, the + ; allocator will choose $f12 and $f13 to avoid the spill/reload. + ; + ; On the other hand, if odd single precision registers are not permitted, it + ; will be forced to spill/reload either %a or %0. + + %0 = fadd float %a, 1.0 + call void asm "# Clobber", "~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31}"() + %1 = fadd float %a, %0 + ret float %1 +} + +; ALL-LABEL: two_floats: +; ODDSPREG: add.s $f13, $f12, ${{f[0-9]+}} +; ODDSPREG-NOT: swc1 +; ODDSPREG-NOT: lwc1 +; ODDSPREG: add.s $f0, $f12, $f13 + +; NOODDSPREG: add.s $[[T0:f[0-9]*[02468]]], $f12, ${{f[0-9]+}} +; NOODDSPREG: swc1 $[[T0]], +; NOODDSPREG: lwc1 $[[T1:f[0-9]*[02468]]], +; NOODDSPREG: add.s $f0, $f12, $[[T1]] + +define double @two_doubles(double %a) { +entry: + ; Clobber all except $f12 and $f13 + ; + ; -mno-odd-sp-reg doesn't need to affect double precision values so both cases + ; use $f12 and $f13. + + %0 = fadd double %a, 1.0 + call void asm "# Clobber", "~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31}"() + %1 = fadd double %a, %0 + ret double %1 +} + +; ALL-LABEL: two_doubles: +; ALL: add.d $[[T0:f[0-9]+]], $f12, ${{f[0-9]+}} +; ALL: add.d $f0, $f12, $[[T0]] + + +; INVALID: -mattr=+nooddspreg is not currently permitted for a 32-bit FPU register file (FR=0 mode). Index: test/MC/Mips/oddspreg.s =================================================================== --- /dev/null +++ test/MC/Mips/oddspreg.s @@ -0,0 +1,37 @@ +# RUN: llvm-mc %s -arch=mips -mcpu=mips32 -mattr=+fp64 | \ +# RUN: FileCheck %s -check-prefix=CHECK-ASM +# +# RUN: llvm-mc %s -arch=mips -mcpu=mips32 -mattr=+fp64 -filetype=obj -o - | \ +# RUN: llvm-readobj -sections -section-data -section-relocations - | \ +# RUN: FileCheck %s -check-prefix=CHECK-OBJ + +# CHECK-ASM: .module oddspreg + +# Checking if the Mips.abiflags were correctly emitted. +# CHECK-OBJ: Section { +# CHECK-OBJ: Index: 5 +# CHECK-OBJ: Name: .MIPS.abiflags (12) +# CHECK-OBJ: Type: (0x7000002A) +# CHECK-OBJ: Flags [ (0x2) +# CHECK-OBJ: SHF_ALLOC (0x2) +# CHECK-OBJ: ] +# CHECK-OBJ: Address: 0x0 +# CHECK-OBJ: Offset: 0x50 +# CHECK-OBJ: Size: 24 +# CHECK-OBJ: Link: 0 +# CHECK-OBJ: Info: 0 +# CHECK-OBJ: AddressAlignment: 8 +# CHECK-OBJ: EntrySize: 0 +# CHECK-OBJ: Relocations [ +# CHECK-OBJ: ] +# CHECK-OBJ: SectionData ( +# CHECK-OBJ: 0000: 00002001 01020006 00000000 00000000 |.. .............| +# CHECK-OBJ: 0010: 00000001 00000000 |........| +# CHECK-OBJ: ) +# CHECK-OBJ: } + + .module oddspreg + +# FIXME: Test should include gnu_attributes directive when implemented. +# An explicit .gnu_attribute must be checked against the effective +# command line options and any inconsistencies reported via a warning.