From 06dac21852eda1a327c51492d296084df80b152d Mon Sep 17 00:00:00 2001 From: Chris Dewhurst Date: Wed, 18 May 2016 09:14:13 +0000 Subject: [PATCH] [Sparc] Add Soft Float support This change adds support for software floating point operations for Sparc targets. This is the first in a set of patches to enable software floating point on Sparc. The next patch will enable the option to be used with Clang. Differential Revision: http://reviews.llvm.org/D19265 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@269892 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/Sparc/Sparc.td | 3 + lib/Target/Sparc/SparcISelLowering.cpp | 16 +- lib/Target/Sparc/SparcISelLowering.h | 4 +- lib/Target/Sparc/SparcSubtarget.cpp | 1 + lib/Target/Sparc/SparcSubtarget.h | 2 + lib/Target/Sparc/SparcTargetMachine.cpp | 38 +++- lib/Target/Sparc/SparcTargetMachine.h | 7 +- test/CodeGen/SPARC/32abi.ll | 138 ++++++++++---- test/CodeGen/SPARC/64abi.ll | 185 +++++++++++++------ test/CodeGen/SPARC/soft-float.ll | 235 ++++++++++++++++++++++++ 10 files changed, 528 insertions(+), 101 deletions(-) create mode 100644 test/CodeGen/SPARC/soft-float.ll diff --git a/lib/Target/Sparc/Sparc.td b/lib/Target/Sparc/Sparc.td index 6193e97c010..51e0b9acdb0 100644 --- a/lib/Target/Sparc/Sparc.td +++ b/lib/Target/Sparc/Sparc.td @@ -46,6 +46,9 @@ def FeatureHardQuad def UsePopc : SubtargetFeature<"popc", "UsePopc", "true", "Use the popc (population count) instruction">; +def FeatureSoftFloat : SubtargetFeature<"soft-float", "UseSoftFloat", "true", + "Use software emulation for floating point">; + //==== Features added predmoninantly for LEON subtarget support include "LeonFeatures.td" diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp index 2c21c0bc2e4..1787240df91 100644 --- a/lib/Target/Sparc/SparcISelLowering.cpp +++ b/lib/Target/Sparc/SparcISelLowering.cpp @@ -1463,9 +1463,11 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM, // Set up the register classes. addRegisterClass(MVT::i32, &SP::IntRegsRegClass); - addRegisterClass(MVT::f32, &SP::FPRegsRegClass); - addRegisterClass(MVT::f64, &SP::DFPRegsRegClass); - addRegisterClass(MVT::f128, &SP::QFPRegsRegClass); + if (!Subtarget->useSoftFloat()) { + addRegisterClass(MVT::f32, &SP::FPRegsRegClass); + addRegisterClass(MVT::f64, &SP::DFPRegsRegClass); + addRegisterClass(MVT::f128, &SP::QFPRegsRegClass); + } if (Subtarget->is64Bit()) { addRegisterClass(MVT::i64, &SP::I64RegsRegClass); } else { @@ -1760,7 +1762,7 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM, setOperationAction(ISD::FP_ROUND, MVT::f32, Custom); // Setup Runtime library names. - if (Subtarget->is64Bit()) { + if (Subtarget->is64Bit() && !Subtarget->useSoftFloat()) { setLibcallName(RTLIB::ADD_F128, "_Qp_add"); setLibcallName(RTLIB::SUB_F128, "_Qp_sub"); setLibcallName(RTLIB::MUL_F128, "_Qp_mul"); @@ -1778,7 +1780,7 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM, setLibcallName(RTLIB::FPEXT_F64_F128, "_Qp_dtoq"); setLibcallName(RTLIB::FPROUND_F128_F32, "_Qp_qtos"); setLibcallName(RTLIB::FPROUND_F128_F64, "_Qp_qtod"); - } else { + } else if (!Subtarget->useSoftFloat()) { setLibcallName(RTLIB::ADD_F128, "_Q_add"); setLibcallName(RTLIB::SUB_F128, "_Q_sub"); setLibcallName(RTLIB::MUL_F128, "_Q_mul"); @@ -1806,6 +1808,10 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM, computeRegisterProperties(Subtarget->getRegisterInfo()); } +bool SparcTargetLowering::useSoftFloat() const { + return Subtarget->useSoftFloat(); +} + const char *SparcTargetLowering::getTargetNodeName(unsigned Opcode) const { switch ((SPISD::NodeType)Opcode) { case SPISD::FIRST_NUMBER: break; diff --git a/lib/Target/Sparc/SparcISelLowering.h b/lib/Target/Sparc/SparcISelLowering.h index e451afa98fc..20dfd2cabd0 100644 --- a/lib/Target/Sparc/SparcISelLowering.h +++ b/lib/Target/Sparc/SparcISelLowering.h @@ -59,7 +59,9 @@ namespace llvm { public: SparcTargetLowering(const TargetMachine &TM, const SparcSubtarget &STI); SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; - + + bool useSoftFloat() const override; + /// computeKnownBitsForTargetNode - Determine which of the bits specified /// in Mask are known to be either zero or one and return them in the /// KnownZero/KnownOne bitsets. diff --git a/lib/Target/Sparc/SparcSubtarget.cpp b/lib/Target/Sparc/SparcSubtarget.cpp index 8049ae41314..f3b50d40323 100644 --- a/lib/Target/Sparc/SparcSubtarget.cpp +++ b/lib/Target/Sparc/SparcSubtarget.cpp @@ -34,6 +34,7 @@ SparcSubtarget &SparcSubtarget::initializeSubtargetDependencies(StringRef CPU, IsVIS = false; HasHardQuad = false; UsePopc = false; + UseSoftFloat = false; // Leon features HasLeonCasa = false; diff --git a/lib/Target/Sparc/SparcSubtarget.h b/lib/Target/Sparc/SparcSubtarget.h index c573965fb28..4ad963e31d8 100644 --- a/lib/Target/Sparc/SparcSubtarget.h +++ b/lib/Target/Sparc/SparcSubtarget.h @@ -39,6 +39,7 @@ class SparcSubtarget : public SparcGenSubtargetInfo { bool Is64Bit; bool HasHardQuad; bool UsePopc; + bool UseSoftFloat; // LEON features bool HasUmacSmac; @@ -77,6 +78,7 @@ public: bool useDeprecatedV8Instructions() const { return V8DeprecatedInsts; } bool hasHardQuad() const { return HasHardQuad; } bool usePopc() const { return UsePopc; } + bool useSoftFloat() const { return UseSoftFloat; } // Leon options bool hasUmacSmac() const { return HasUmacSmac; } diff --git a/lib/Target/Sparc/SparcTargetMachine.cpp b/lib/Target/Sparc/SparcTargetMachine.cpp index fa6cc73c529..6e75c634d0e 100644 --- a/lib/Target/Sparc/SparcTargetMachine.cpp +++ b/lib/Target/Sparc/SparcTargetMachine.cpp @@ -62,13 +62,47 @@ SparcTargetMachine::SparcTargetMachine(const Target &T, const Triple &TT, CodeGenOpt::Level OL, bool is64bit) : LLVMTargetMachine(T, computeDataLayout(TT, is64bit), TT, CPU, FS, Options, RM, CM, OL), - TLOF(make_unique()), - Subtarget(TT, CPU, FS, *this, is64bit) { + TLOF(make_unique()) { initAsmInfo(); + this->is64Bit = is64bit; } SparcTargetMachine::~SparcTargetMachine() {} +const SparcSubtarget * +SparcTargetMachine::getSubtargetImpl(const Function &F) const { + Attribute CPUAttr = F.getFnAttribute("target-cpu"); + Attribute FSAttr = F.getFnAttribute("target-features"); + + std::string CPU = !CPUAttr.hasAttribute(Attribute::None) + ? CPUAttr.getValueAsString().str() + : TargetCPU; + std::string FS = !FSAttr.hasAttribute(Attribute::None) + ? FSAttr.getValueAsString().str() + : TargetFS; + + // FIXME: This is related to the code below to reset the target options, + // we need to know whether or not the soft float flag is set on the + // function, so we can enable it as a subtarget feature. + bool softFloat = + F.hasFnAttribute("use-soft-float") && + F.getFnAttribute("use-soft-float").getValueAsString() == "true"; + + if (softFloat) + FS += FS.empty() ? "+soft-float" : ",+soft-float"; + + auto &I = SubtargetMap[CPU + FS]; + if (!I) { + // This needs to be done before we create a new subtarget since any + // creation will depend on the TM and the code generation flags on the + // function that reside in TargetOptions. + resetTargetOptions(F); + I = llvm::make_unique(TargetTriple, CPU, FS, *this, + this->is64Bit); + } + return I.get(); +} + namespace { /// Sparc Code Generator Pass Configuration Options. class SparcPassConfig : public TargetPassConfig { diff --git a/lib/Target/Sparc/SparcTargetMachine.h b/lib/Target/Sparc/SparcTargetMachine.h index 903c2d15629..4da9d2ccba9 100644 --- a/lib/Target/Sparc/SparcTargetMachine.h +++ b/lib/Target/Sparc/SparcTargetMachine.h @@ -22,7 +22,8 @@ namespace llvm { class SparcTargetMachine : public LLVMTargetMachine { std::unique_ptr TLOF; - SparcSubtarget Subtarget; + bool is64Bit; + mutable StringMap> SubtargetMap; public: SparcTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, @@ -30,9 +31,7 @@ public: bool is64bit); ~SparcTargetMachine() override; - const SparcSubtarget *getSubtargetImpl(const Function &) const override { - return &Subtarget; - } + const SparcSubtarget *getSubtargetImpl(const Function &) const override; // Pass Pipeline Configuration TargetPassConfig *createPassConfig(PassManagerBase &PM) override; diff --git a/test/CodeGen/SPARC/32abi.ll b/test/CodeGen/SPARC/32abi.ll index 7ac1de5c090..ccea1adceaf 100644 --- a/test/CodeGen/SPARC/32abi.ll +++ b/test/CodeGen/SPARC/32abi.ll @@ -1,5 +1,6 @@ -; RUN: llc < %s -march=sparc -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-BE -; RUN: llc < %s -march=sparcel -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LE +; RUN: llc < %s -march=sparc -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=HARD --check-prefix=CHECK-BE +; RUN: llc < %s -march=sparcel -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=HARD --check-prefix=CHECK-LE +; RUN: llc < %s -march=sparc -disable-sparc-delay-filler -disable-sparc-leaf-proc -mattr=soft-float | FileCheck %s --check-prefix=CHECK --check-prefix=SOFT --check-prefix=CHECK-BE ; CHECK-LABEL: intarg: ; The save/restore frame is not strictly necessary here, but we would need to @@ -55,29 +56,80 @@ define void @call_intarg(i32 %i0, i8* %i1) { ;; straddling the boundary of regs and mem, and floats in regs and mem. ; ; CHECK-LABEL: floatarg: -; CHECK: save %sp, -120, %sp -; CHECK: mov %i5, %g2 -; CHECK-NEXT: ld [%fp+92], %g3 -; CHECK-NEXT: mov %i4, %i5 -; CHECK-NEXT: std %g2, [%fp+-24] -; CHECK-NEXT: mov %i3, %i4 -; CHECK-NEXT: std %i4, [%fp+-16] -; CHECK-NEXT: std %i0, [%fp+-8] -; CHECK-NEXT: st %i2, [%fp+-28] -; CHECK-NEXT: ld [%fp+104], %f0 -; CHECK-NEXT: ldd [%fp+96], %f2 -; CHECK-NEXT: ld [%fp+-28], %f1 -; CHECK-NEXT: ldd [%fp+-8], %f4 -; CHECK-NEXT: ldd [%fp+-16], %f6 -; CHECK-NEXT: ldd [%fp+-24], %f8 -; CHECK-NEXT: fstod %f1, %f10 -; CHECK-NEXT: faddd %f4, %f10, %f4 -; CHECK-NEXT: faddd %f6, %f4, %f4 -; CHECK-NEXT: faddd %f8, %f4, %f4 -; CHECK-NEXT: faddd %f2, %f4, %f2 -; CHECK-NEXT: fstod %f0, %f0 -; CHECK-NEXT: faddd %f0, %f2, %f0 -; CHECK-NEXT: restore +; HARD: save %sp, -120, %sp +; HARD: mov %i5, %g2 +; HARD-NEXT: ld [%fp+92], %g3 +; HARD-NEXT: mov %i4, %i5 +; HARD-NEXT: std %g2, [%fp+-24] +; HARD-NEXT: mov %i3, %i4 +; HARD-NEXT: std %i4, [%fp+-16] +; HARD-NEXT: std %i0, [%fp+-8] +; HARD-NEXT: st %i2, [%fp+-28] +; HARD-NEXT: ld [%fp+104], %f0 +; HARD-NEXT: ldd [%fp+96], %f2 +; HARD-NEXT: ld [%fp+-28], %f1 +; HARD-NEXT: ldd [%fp+-8], %f4 +; HARD-NEXT: ldd [%fp+-16], %f6 +; HARD-NEXT: ldd [%fp+-24], %f8 +; HARD-NEXT: fstod %f1, %f10 +; HARD-NEXT: faddd %f4, %f10, %f4 +; HARD-NEXT: faddd %f6, %f4, %f4 +; HARD-NEXT: faddd %f8, %f4, %f4 +; HARD-NEXT: faddd %f2, %f4, %f2 +; HARD-NEXT: fstod %f0, %f0 +; HARD-NEXT: faddd %f0, %f2, %f0 +; SOFT: save %sp, -96, %sp +; SOFT: ld [%fp+104], %l0 +; SOFT-NEXT: ld [%fp+96], %l1 +; SOFT-NEXT: ld [%fp+100], %l2 +; SOFT-NEXT: ld [%fp+92], %l3 +; SOFT-NEXT: mov %i2, %o0 +; SOFT-NEXT: call __extendsfdf2 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i2 +; SOFT-NEXT: mov %o1, %g2 +; SOFT-NEXT: mov %i0, %o0 +; SOFT-NEXT: mov %i1, %o1 +; SOFT-NEXT: mov %i2, %o2 +; SOFT-NEXT: mov %g2, %o3 +; SOFT-NEXT: call __adddf3 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i0 +; SOFT-NEXT: mov %o1, %i1 +; SOFT-NEXT: mov %i3, %o0 +; SOFT-NEXT: mov %i4, %o1 +; SOFT-NEXT: mov %i0, %o2 +; SOFT-NEXT: mov %i1, %o3 +; SOFT-NEXT: call __adddf3 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i0 +; SOFT-NEXT: mov %o1, %i1 +; SOFT-NEXT: mov %i5, %o0 +; SOFT-NEXT: mov %l3, %o1 +; SOFT-NEXT: mov %i0, %o2 +; SOFT-NEXT: mov %i1, %o3 +; SOFT-NEXT: call __adddf3 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i0 +; SOFT-NEXT: mov %o1, %i1 +; SOFT-NEXT: mov %l1, %o0 +; SOFT-NEXT: mov %l2, %o1 +; SOFT-NEXT: mov %i0, %o2 +; SOFT-NEXT: mov %i1, %o3 +; SOFT-NEXT: call __adddf3 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i0 +; SOFT-NEXT: mov %o1, %i1 +; SOFT-NEXT: mov %l0, %o0 +; SOFT-NEXT: call __extendsfdf2 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %i0, %o2 +; SOFT-NEXT: mov %i1, %o3 +; SOFT-NEXT: call __adddf3 +; SOFT-NEXT: nop +; SOFT-NEXT: mov %o0, %i0 +; SOFT-NEXT: mov %o1, %i1 +; CHECK: restore define double @floatarg(double %a0, ; %i0,%i1 float %a1, ; %i2 double %a2, ; %i3, %i4 @@ -95,18 +147,30 @@ define double @floatarg(double %a0, ; %i0,%i1 } ; CHECK-LABEL: call_floatarg: -; CHECK: save %sp, -112, %sp -; CHECK: mov %i2, %o1 -; CHECK-NEXT: mov %i1, %o0 -; CHECK-NEXT: st %i0, [%sp+104] -; CHECK-NEXT: std %o0, [%sp+96] -; CHECK-NEXT: st %o1, [%sp+92] -; CHECK-NEXT: mov %i0, %o2 -; CHECK-NEXT: mov %o0, %o3 -; CHECK-NEXT: mov %o1, %o4 -; CHECK-NEXT: mov %o0, %o5 -; CHECK-NEXT: call floatarg -; CHECK: std %f0, [%i4] +; HARD: save %sp, -112, %sp +; HARD: mov %i2, %o1 +; HARD-NEXT: mov %i1, %o0 +; HARD-NEXT: st %i0, [%sp+104] +; HARD-NEXT: std %o0, [%sp+96] +; HARD-NEXT: st %o1, [%sp+92] +; HARD-NEXT: mov %i0, %o2 +; HARD-NEXT: mov %o0, %o3 +; HARD-NEXT: mov %o1, %o4 +; HARD-NEXT: mov %o0, %o5 +; HARD-NEXT: call floatarg +; HARD: std %f0, [%i4] +; SOFT: st %i0, [%sp+104] +; SOFT-NEXT: st %i2, [%sp+100] +; SOFT-NEXT: st %i1, [%sp+96] +; SOFT-NEXT: st %i2, [%sp+92] +; SOFT-NEXT: mov %i1, %o0 +; SOFT-NEXT: mov %i2, %o1 +; SOFT-NEXT: mov %i0, %o2 +; SOFT-NEXT: mov %i1, %o3 +; SOFT-NEXT: mov %i2, %o4 +; SOFT-NEXT: mov %i1, %o5 +; SOFT-NEXT: call floatarg +; SOFT: std %o0, [%i4] ; CHECK: restore define void @call_floatarg(float %f1, double %d2, float %f5, double *%p) { %r = call double @floatarg(double %d2, float %f1, double %d2, double %d2, diff --git a/test/CodeGen/SPARC/64abi.ll b/test/CodeGen/SPARC/64abi.ll index 96104ecc3c6..b963be2e985 100644 --- a/test/CodeGen/SPARC/64abi.ll +++ b/test/CodeGen/SPARC/64abi.ll @@ -1,4 +1,5 @@ -; RUN: llc < %s -march=sparcv9 -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s +; RUN: llc < %s -march=sparcv9 -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=HARD +; RUN: llc < %s -march=sparcv9 -disable-sparc-delay-filler -disable-sparc-leaf-proc -mattr=soft-float | FileCheck %s --check-prefix=CHECK --check-prefix=SOFT ; CHECK-LABEL: intarg: ; The save/restore frame is not strictly necessary here, but we would need to @@ -54,13 +55,22 @@ define void @call_intarg(i32 %i0, i8* %i1) { } ; CHECK-LABEL: floatarg: -; CHECK: save %sp, -128, %sp -; CHECK: ld [%fp+2307], [[F:%f[0-9]+]] -; CHECK: fstod %f1, -; CHECK: faddd %f2, -; CHECK: faddd %f4, -; CHECK: faddd %f6, -; CHECK: fadds %f31, [[F]] +; HARD: save %sp, -128, %sp +; HARD: ld [%fp+2307], [[F:%f[0-9]+]] +; HARD: fstod %f1, +; HARD: faddd %f2, +; HARD: faddd %f4, +; HARD: faddd %f6, +; HARD: fadds %f31, [[F]] +; SOFT: save %sp, -176, %sp +; SOFT: srl %i0, 0, %o0 +; SOFT-NEXT: call __extendsfdf2 +; SOFT: mov %o0, %i0 +; SOFT: mov %i1, %o0 +; SOFT: mov %i2, %o0 +; SOFT: mov %i3, %o0 +; SOFT: ld [%fp+2299], %o0 +; SOFT: ld [%fp+2307], %o1 define double @floatarg(float %a0, ; %f1 double %a1, ; %d2 double %a2, ; %d4 @@ -92,13 +102,32 @@ define double @floatarg(float %a0, ; %f1 ; CHECK-LABEL: call_floatarg: ; CHECK: save %sp, -272, %sp ; Store 8 bytes in full slot. -; CHECK: std %f2, [%sp+2311] +; HARD: std %f2, [%sp+2311] ; Store 4 bytes, right-aligned in slot. -; CHECK: st %f1, [%sp+2307] -; CHECK: fmovd %f2, %f4 +; HARD: st %f1, [%sp+2307] +; HARD: fmovd %f2, %f4 +; SOFT: stx %i1, [%sp+2311] +; SOFT: stx %i0, [%sp+2303] +; SOFT: stx %i2, [%sp+2295] +; SOFT: stx %i2, [%sp+2287] +; SOFT: stx %i2, [%sp+2279] +; SOFT: stx %i2, [%sp+2271] +; SOFT: stx %i2, [%sp+2263] +; SOFT: stx %i2, [%sp+2255] +; SOFT: stx %i2, [%sp+2247] +; SOFT: stx %i2, [%sp+2239] +; SOFT: stx %i2, [%sp+2231] +; SOFT: stx %i2, [%sp+2223] +; SOFT: mov %i2, %o0 +; SOFT: mov %i1, %o1 +; SOFT: mov %i1, %o2 +; SOFT: mov %i1, %o3 +; SOFT: mov %i2, %o4 +; SOFT: mov %i2, %o5 ; CHECK: call floatarg ; CHECK-NOT: add %sp ; CHECK: restore + define void @call_floatarg(float %f1, double %d2, float %f5, double *%p) { %r = call double @floatarg(float %f5, double %d2, double %d2, double %d2, float %f5, float %f5, float %f5, float %f5, @@ -112,9 +141,21 @@ define void @call_floatarg(float %f1, double %d2, float %f5, double *%p) { ; CHECK-LABEL: mixedarg: ; CHECK: ldx [%fp+2247] ; CHECK: ldx [%fp+2231] -; CHECK: fstod %f3 -; CHECK: faddd %f6 -; CHECK: faddd %f16 +; SOFT: ldx [%fp+2239], %i0 +; HARD: fstod %f3 +; HARD: faddd %f6 +; HARD: faddd %f16 +; SOFT: mov %o0, %i1 +; SOFT-NEXT: mov %i3, %o0 +; SOFT-NEXT: mov %i1, %o1 +; SOFT-NEXT: call __adddf3 +; SOFT: mov %o0, %i1 +; SOFT-NEXT: mov %i0, %o0 +; SOFT-NEXT: mov %i1, %o1 +; SOFT-NEXT: call __adddf3 +; HARD: std %f0, [%i1] +; SOFT: stx %o0, [%i5] + define void @mixedarg(i8 %a0, ; %i0 float %a1, ; %f3 i16 %a2, ; %i2 @@ -135,12 +176,15 @@ define void @mixedarg(i8 %a0, ; %i0 ; CHECK-LABEL: call_mixedarg: ; CHECK: stx %i2, [%sp+2247] +; SOFT: stx %i1, [%sp+2239] ; CHECK: stx %i0, [%sp+2223] -; CHECK: fmovd %f2, %f6 -; CHECK: fmovd %f2, %f16 +; HARD: fmovd %f2, %f6 +; HARD: fmovd %f2, %f16 +; SOFT: mov %i1, %o3 ; CHECK: call mixedarg ; CHECK-NOT: add %sp ; CHECK: restore + define void @call_mixedarg(i64 %i0, double %f2, i16* %i2) { call void @mixedarg(i8 undef, float undef, @@ -158,8 +202,10 @@ define void @call_mixedarg(i64 %i0, double %f2, i16* %i2) { ; The inreg attribute is used to indicate 32-bit sized struct elements that ; share an 8-byte slot. ; CHECK-LABEL: inreg_fi: -; CHECK: fstoi %f1 -; CHECK: srlx %i0, 32, [[R:%[gilo][0-7]]] +; SOFT: srlx %i0, 32, [[R:%[gilo][0-7]]] +; HARD: fstoi %f1 +; SOFT: call __fixsfsi +; HARD: srlx %i0, 32, [[R:%[gilo][0-7]]] ; CHECK: sub [[R]], define i32 @inreg_fi(i32 inreg %a0, ; high bits of %i0 float inreg %a1) { ; %f1 @@ -171,8 +217,11 @@ define i32 @inreg_fi(i32 inreg %a0, ; high bits of %i0 ; CHECK-LABEL: call_inreg_fi: ; Allocate space for 6 arguments, even when only 2 are used. ; CHECK: save %sp, -176, %sp -; CHECK: sllx %i1, 32, %o0 -; CHECK: fmovs %f5, %f1 +; HARD: sllx %i1, 32, %o0 +; HARD: fmovs %f5, %f1 +; SOFT: srl %i2, 0, %i0 +; SOFT: sllx %i1, 32, %i1 +; SOFT: or %i1, %i0, %o0 ; CHECK: call inreg_fi define void @call_inreg_fi(i32* %p, i32 %i1, float %f5) { %x = call i32 @inreg_fi(i32 %i1, float %f5) @@ -180,7 +229,10 @@ define void @call_inreg_fi(i32* %p, i32 %i1, float %f5) { } ; CHECK-LABEL: inreg_ff: -; CHECK: fsubs %f0, %f1, %f0 +; HARD: fsubs %f0, %f1, %f0 +; SOFT: srlx %i0, 32, %o0 +; SOFT: srl %i0, 0, %o1 +; SOFT: call __subsf3 define float @inreg_ff(float inreg %a0, ; %f0 float inreg %a1) { ; %f1 %rv = fsub float %a0, %a1 @@ -188,8 +240,11 @@ define float @inreg_ff(float inreg %a0, ; %f0 } ; CHECK-LABEL: call_inreg_ff: -; CHECK: fmovs %f3, %f0 -; CHECK: fmovs %f5, %f1 +; HARD: fmovs %f3, %f0 +; HARD: fmovs %f5, %f1 +; SOFT: srl %i2, 0, %i0 +; SOFT: sllx %i1, 32, %i1 +; SOFT: or %i1, %i0, %o0 ; CHECK: call inreg_ff define void @call_inreg_ff(i32* %p, float %f3, float %f5) { %x = call float @inreg_ff(float %f3, float %f5) @@ -197,7 +252,9 @@ define void @call_inreg_ff(i32* %p, float %f3, float %f5) { } ; CHECK-LABEL: inreg_if: -; CHECK: fstoi %f0 +; HARD: fstoi %f0 +; SOFT: srlx %i0, 32, %o0 +; SOFT: call __fixsfsi ; CHECK: sub %i0 define i32 @inreg_if(float inreg %a0, ; %f0 i32 inreg %a1) { ; low bits of %i0 @@ -207,8 +264,11 @@ define i32 @inreg_if(float inreg %a0, ; %f0 } ; CHECK-LABEL: call_inreg_if: -; CHECK: fmovs %f3, %f0 -; CHECK: mov %i2, %o0 +; HARD: fmovs %f3, %f0 +; HARD: mov %i2, %o0 +; SOFT: srl %i2, 0, %i0 +; SOFT: sllx %i1, 32, %i1 +; SOFT: or %i1, %i0, %o0 ; CHECK: call inreg_if define void @call_inreg_if(i32* %p, float %f3, i32 %i2) { %x = call i32 @inreg_if(float %f3, i32 %i2) @@ -265,7 +325,8 @@ define void @call_ret_i64_pair(i64* %i0) { ; This is not a C struct, the i32 member uses 8 bytes, but the float only 4. ; CHECK-LABEL: ret_i32_float_pair: ; CHECK: ld [%i2], %i0 -; CHECK: ld [%i3], %f2 +; HARD: ld [%i3], %f2 +; SOFT: ld [%i3], %i1 define { i32, float } @ret_i32_float_pair(i32 %a0, i32 %a1, i32* %p, float* %q) { %r1 = load i32, i32* %p @@ -279,7 +340,8 @@ define { i32, float } @ret_i32_float_pair(i32 %a0, i32 %a1, ; CHECK-LABEL: call_ret_i32_float_pair: ; CHECK: call ret_i32_float_pair ; CHECK: st %o0, [%i0] -; CHECK: st %f2, [%i1] +; HARD: st %f2, [%i1] +; SOFT: st %o1, [%i1] define void @call_ret_i32_float_pair(i32* %i0, float* %i1) { %rv = call { i32, float } @ret_i32_float_pair(i32 undef, i32 undef, i32* undef, float* undef) @@ -293,7 +355,8 @@ define void @call_ret_i32_float_pair(i32* %i0, float* %i1) { ; This is a C struct, each member uses 4 bytes. ; CHECK-LABEL: ret_i32_float_packed: ; CHECK: ld [%i2], [[R:%[gilo][0-7]]] -; CHECK: ld [%i3], %f1 +; HARD: ld [%i3], %f1 +; SOFT: ld [%i3], %i1 ; CHECK: sllx [[R]], 32, %i0 define inreg { i32, float } @ret_i32_float_packed(i32 %a0, i32 %a1, i32* %p, float* %q) { @@ -309,7 +372,8 @@ define inreg { i32, float } @ret_i32_float_packed(i32 %a0, i32 %a1, ; CHECK: call ret_i32_float_packed ; CHECK: srlx %o0, 32, [[R:%[gilo][0-7]]] ; CHECK: st [[R]], [%i0] -; CHECK: st %f1, [%i1] +; HARD: st %f1, [%i1] +; SOFT: st %o0, [%i1] define void @call_ret_i32_float_packed(i32* %i0, float* %i1) { %rv = call { i32, float } @ret_i32_float_packed(i32 undef, i32 undef, i32* undef, float* undef) @@ -413,13 +477,21 @@ entry: declare i32 @use_buf(i32, i8*) ; CHECK-LABEL: test_fp128_args: -; CHECK-DAG: std %f0, [%fp+{{.+}}] -; CHECK-DAG: std %f2, [%fp+{{.+}}] -; CHECK-DAG: std %f6, [%fp+{{.+}}] -; CHECK-DAG: std %f4, [%fp+{{.+}}] -; CHECK: add %fp, [[Offset:[0-9]+]], %o0 -; CHECK: call _Qp_add -; CHECK: ldd [%fp+[[Offset]]], %f0 +; HARD-DAG: std %f0, [%fp+{{.+}}] +; HARD-DAG: std %f2, [%fp+{{.+}}] +; HARD-DAG: std %f6, [%fp+{{.+}}] +; HARD-DAG: std %f4, [%fp+{{.+}}] +; HARD: add %fp, [[Offset:[0-9]+]], %o0 +; HARD: call _Qp_add +; HARD: ldd [%fp+[[Offset]]], %f0 +; SOFT-DAG: mov %i0, %o0 +; SOFT-DAG: mov %i1, %o1 +; SOFT-DAG: mov %i2, %o2 +; SOFT-DAG: mov %i3, %o3 +; SOFT: call __addtf3 +; SOFT: mov %o0, %i0 +; SOFT: mov %o1, %i1 + define fp128 @test_fp128_args(fp128 %a, fp128 %b) { entry: %0 = fadd fp128 %a, %b @@ -429,11 +501,14 @@ entry: declare i64 @receive_fp128(i64 %a, ...) ; CHECK-LABEL: test_fp128_variable_args: -; CHECK-DAG: std %f4, [%sp+[[Offset0:[0-9]+]]] -; CHECK-DAG: std %f6, [%sp+[[Offset1:[0-9]+]]] -; CHECK-DAG: ldx [%sp+[[Offset0]]], %o2 -; CHECK-DAG: ldx [%sp+[[Offset1]]], %o3 -; CHECK: call receive_fp128 +; HARD-DAG: std %f4, [%sp+[[Offset0:[0-9]+]]] +; HARD-DAG: std %f6, [%sp+[[Offset1:[0-9]+]]] +; HARD-DAG: ldx [%sp+[[Offset0]]], %o2 +; HARD-DAG: ldx [%sp+[[Offset1]]], %o3 +; SOFT-DAG: mov %i0, %o0 +; SOFT-DAG: mov %i1, %o1 +; SOFT-DAG: mov %i2, %o2 +; CHECK: call receive_fp128 define i64 @test_fp128_variable_args(i64 %a, fp128 %b) { entry: %0 = call i64 (i64, ...) @receive_fp128(i64 %a, fp128 %b) @@ -441,14 +516,22 @@ entry: } ; CHECK-LABEL: test_call_libfunc: -; CHECK: st %f1, [%fp+[[Offset0:[0-9]+]]] -; CHECK: fmovs %f3, %f1 -; CHECK: call cosf -; CHECK: st %f0, [%fp+[[Offset1:[0-9]+]]] -; CHECK: ld [%fp+[[Offset0]]], %f1 -; CHECK: call sinf -; CHECK: ld [%fp+[[Offset1]]], %f1 -; CHECK: fmuls %f1, %f0, %f0 +; HARD: st %f1, [%fp+[[Offset0:[0-9]+]]] +; HARD: fmovs %f3, %f1 +; SOFT: srl %i1, 0, %o0 +; CHECK: call cosf +; HARD: st %f0, [%fp+[[Offset1:[0-9]+]]] +; HARD: ld [%fp+[[Offset0]]], %f1 +; SOFT: mov %o0, %i1 +; SOFT: srl %i0, 0, %o0 +; CHECK: call sinf +; HARD: ld [%fp+[[Offset1]]], %f1 +; HARD: fmuls %f1, %f0, %f0 +; SOFT: mov %o0, %i0 +; SOFT: mov %i1, %o0 +; SOFT: mov %i0, %o1 +; SOFT: call __mulsf3 +; SOFT: sllx %o0, 32, %i0 define inreg float @test_call_libfunc(float %arg0, float %arg1) { entry: @@ -460,5 +543,3 @@ entry: declare inreg float @cosf(float %arg) readnone nounwind declare inreg float @sinf(float %arg) readnone nounwind - - diff --git a/test/CodeGen/SPARC/soft-float.ll b/test/CodeGen/SPARC/soft-float.ll new file mode 100644 index 00000000000..53ca1974659 --- /dev/null +++ b/test/CodeGen/SPARC/soft-float.ll @@ -0,0 +1,235 @@ +; RUN: llc -march=sparc -mattr=soft-float -O0 < %s | FileCheck %s + +; Arithmetic functions + +define float @test_addsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_addsf3: + ; CHECK: call __addsf3 + %add = fadd float %a, %b + ret float %add +} + +define double @test_adddf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_adddf3: + ; CHECK: call __adddf3 + %add = fadd double %a, %b + ret double %add +} + +define fp128 @test_addtf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_addtf3: + ; CHECK: call __addtf3 + %add = fadd fp128 %a, %b + ret fp128 %add +} + +define float @test_mulsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_mulsf3: + ; CHECK: call __mulsf3 + %mul = fmul float %a, %b + ret float %mul +} + +define double @test_muldf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_muldf3: + ; CHECK: call __muldf3 + %mul = fmul double %a, %b + ret double %mul +} + +define fp128 @test_multf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_multf3: + ; CHECK: call __multf3 + %mul = fmul fp128 %a, %b + ret fp128 %mul +} + +define float @test_subsf3(float %a, float %b) #0 { + ; CHCEK-LABEL: test_subsf3: + ; CHECK: call __subsf3 + %sub = fsub float %a, %b + ret float %sub +} + +define double @test_subdf3(double %a, double %b) #0 { + ; CHCEK-LABEL: test_subdf3: + ; CHECK: call __subdf3 + %sub = fsub double %a, %b + ret double %sub +} + +define fp128 @test_subtf3(fp128 %a, fp128 %b) #0 { + ; CHCEK-LABEL: test_subtf3: + ; CHECK: call __subtf3 + %sub = fsub fp128 %a, %b + ret fp128 %sub +} + +define float @test_divsf3(float %a, float %b) #0 { + ; CHECK-LABEL: test_divsf3: + ; CHECK: call __divsf3 + %div = fdiv float %a, %b + ret float %div +} + +define double @test_divdf3(double %a, double %b) #0 { + ; CHECK-LABEL: test_divdf3: + ; CHECK: call __divdf3 + %div = fdiv double %a, %b + ret double %div +} + +define fp128 @test_divtf3(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_divtf3: + ; CHECK: call __divtf3 + %div = fdiv fp128 %a, %b + ret fp128 %div +} + +; Comparison functions +define i1 @test_unordsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_unordsf2: + ; CHECK: call __unordsf2 + %cmp = fcmp uno float %a, %b + ret i1 %cmp +} + +define i1 @test_unorddf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_unorddf2: + ; CHECK: call __unorddf2 + %cmp = fcmp uno double %a, %b + ret i1 %cmp +} + +define i1 @test_unordtf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_unordtf2: + ; CHECK: call __unordtf2 + %cmp = fcmp uno fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_eqsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_eqsf2: + ; CHECK: call __eqsf2 + %cmp = fcmp oeq float %a, %b + ret i1 %cmp +} + +define i1 @test_eqdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_eqdf2: + ; CHECK: call __eqdf2 + %cmp = fcmp oeq double %a, %b + ret i1 %cmp +} + +define i1 @test_eqtf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_eqtf2: + ; CHECK: call __eqtf2 + %cmp = fcmp oeq fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_nesf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_nesf2: + ; CHECK: call __nesf2 + %cmp = fcmp une float %a, %b + ret i1 %cmp +} + +define i1 @test_nedf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_nedf2: + ; CHECK: call __nedf2 + %cmp = fcmp une double %a, %b + ret i1 %cmp +} + +define i1 @test_netf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_netf2: + ; CHECK: call __netf2 + %cmp = fcmp une fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_gesf2(float %a, float %b) #0 { + ; CHECK-LABLE: test_gesf2: + ; CHECK: call __gesf2 + %cmp = fcmp oge float %a, %b + ret i1 %cmp +} + +define i1 @test_gedf2(double %a, double %b) #0 { + ; CHECK-LABLE: test_gedf2: + ; CHECK: call __gedf2 + %cmp = fcmp oge double %a, %b + ret i1 %cmp +} + +define i1 @test_getf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABLE: test_getf2: + ; CHECK: call __getf2 + %cmp = fcmp oge fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_ltsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_ltsf2: + ; CHECK: call __ltsf2 + %cmp = fcmp olt float %a, %b + ret i1 %cmp +} + +define i1 @test_ltdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_ltdf2: + ; CHECK: call __ltdf2 + %cmp = fcmp olt double %a, %b + ret i1 %cmp +} + +define i1 @test_lttf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_lttf2: + ; CHECK: call __lttf2 + %cmp = fcmp olt fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_lesf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_lesf2: + ; CHECK: call __lesf2 + %cmp = fcmp ole float %a, %b + ret i1 %cmp +} + +define i1 @test_ledf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_ledf2: + ; CHECK: call __ledf2 + %cmp = fcmp ole double %a, %b + ret i1 %cmp +} + +define i1 @test_letf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_letf2: + ; CHECK: call __letf2 + %cmp = fcmp ole fp128 %a, %b + ret i1 %cmp +} + +define i1 @test_gtsf2(float %a, float %b) #0 { + ; CHECK-LABEL: test_gtsf2: + ; CHECK: call __gtsf2 + %cmp = fcmp ogt float %a, %b + ret i1 %cmp +} + +define i1 @test_gtdf2(double %a, double %b) #0 { + ; CHECK-LABEL: test_gtdf2: + ; CHECK: call __gtdf2 + %cmp = fcmp ogt double %a, %b + ret i1 %cmp +} + +define i1 @test_gttf2(fp128 %a, fp128 %b) #0 { + ; CHECK-LABEL: test_gttf2: + ; CHECK: call __gttf2 + %cmp = fcmp ogt fp128 %a, %b + ret i1 %cmp +}