From 785b16ad04a741dce65ebaa11ee86d9dd19dd699 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Tue, 7 Jan 2025 07:50:50 -0800 Subject: [PATCH] [RISCV][GISel] Support G_MERGE_VALUES/G_UNMERGE_VALUES with Zfa. (#120379) Without Zfa we use pseudos that are lowered to a stack load/store. With Zfa we have instructions that can move a pair of registers to an FPR. Or move the high or low half of an FPR to a GPR. I've used a GINodeEquiv to make use of 3 of the 4 tablegen patterns. The split case with Zfa requires 2 instructions which I'm doing through custom isel like we do in SelectionDAG. --- .../RISCV/GISel/RISCVInstructionSelector.cpp | 35 ++++++++----------- llvm/lib/Target/RISCV/RISCVInstrInfoD.td | 2 ++ .../CodeGen/RISCV/GlobalISel/double-zfa.ll | 34 ++++++++++++++++-- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index ef85057ba1264d..3f1539da4a9c84 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -80,7 +80,6 @@ class RISCVInstructionSelector : public InstructionSelector { bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB) const; void emitFence(AtomicOrdering FenceOrdering, SyncScope::ID FenceSSID, MachineIRBuilder &MIB) const; - bool selectMergeValues(MachineInstr &MI, MachineIRBuilder &MIB) const; bool selectUnmergeValues(MachineInstr &MI, MachineIRBuilder &MIB) const; ComplexRendererFns selectShiftMask(MachineOperand &Root, @@ -732,8 +731,6 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { } case TargetOpcode::G_IMPLICIT_DEF: return selectImplicitDef(MI, MIB); - case TargetOpcode::G_MERGE_VALUES: - return selectMergeValues(MI, MIB); case TargetOpcode::G_UNMERGE_VALUES: return selectUnmergeValues(MI, MIB); default: @@ -741,26 +738,13 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { } } -bool RISCVInstructionSelector::selectMergeValues(MachineInstr &MI, - MachineIRBuilder &MIB) const { - assert(MI.getOpcode() == TargetOpcode::G_MERGE_VALUES); - - // Build a F64 Pair from operands - if (MI.getNumOperands() != 3) - return false; - Register Dst = MI.getOperand(0).getReg(); - Register Lo = MI.getOperand(1).getReg(); - Register Hi = MI.getOperand(2).getReg(); - if (!isRegInFprb(Dst) || !isRegInGprb(Lo) || !isRegInGprb(Hi)) - return false; - MI.setDesc(TII.get(RISCV::BuildPairF64Pseudo)); - return constrainSelectedInstRegOperands(MI, TII, TRI, RBI); -} - bool RISCVInstructionSelector::selectUnmergeValues( MachineInstr &MI, MachineIRBuilder &MIB) const { assert(MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES); + if (!Subtarget->hasStdExtZfa()) + return false; + // Split F64 Src into two s32 parts if (MI.getNumOperands() != 3) return false; @@ -769,8 +753,17 @@ bool RISCVInstructionSelector::selectUnmergeValues( Register Hi = MI.getOperand(1).getReg(); if (!isRegInFprb(Src) || !isRegInGprb(Lo) || !isRegInGprb(Hi)) return false; - MI.setDesc(TII.get(RISCV::SplitF64Pseudo)); - return constrainSelectedInstRegOperands(MI, TII, TRI, RBI); + + MachineInstr *ExtractLo = MIB.buildInstr(RISCV::FMV_X_W_FPR64, {Lo}, {Src}); + if (!constrainSelectedInstRegOperands(*ExtractLo, TII, TRI, RBI)) + return false; + + MachineInstr *ExtractHi = MIB.buildInstr(RISCV::FMVH_X_D, {Hi}, {Src}); + if (!constrainSelectedInstRegOperands(*ExtractHi, TII, TRI, RBI)) + return false; + + MI.eraseFromParent(); + return true; } bool RISCVInstructionSelector::replacePtrWithInt(MachineOperand &Op, diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td index ae969bff82fd12..349bc361c90fe8 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td @@ -23,7 +23,9 @@ def SDT_RISCVSplitF64 : SDTypeProfile<2, 1, [SDTCisVT<0, i32>, SDTCisVT<2, f64>]>; def RISCVBuildPairF64 : SDNode<"RISCVISD::BuildPairF64", SDT_RISCVBuildPairF64>; +def : GINodeEquiv; def RISCVSplitF64 : SDNode<"RISCVISD::SplitF64", SDT_RISCVSplitF64>; +def : GINodeEquiv; def AddrRegImmINX : ComplexPattern; diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/double-zfa.ll b/llvm/test/CodeGen/RISCV/GlobalISel/double-zfa.ll index 385156b3b99d48..48786992265824 100644 --- a/llvm/test/CodeGen/RISCV/GlobalISel/double-zfa.ll +++ b/llvm/test/CodeGen/RISCV/GlobalISel/double-zfa.ll @@ -1,9 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 - ; RUN: llc -mtriple=riscv32 -mattr=+zfa,d -global-isel < %s \ -; RUN: | FileCheck %s +; RUN: | FileCheck %s --check-prefixes=CHECK,RV32IDZFA ; RUN: llc -mtriple=riscv64 -mattr=+zfa,d -global-isel < %s \ -; RUN: | FileCheck %s +; RUN: | FileCheck %s --check-prefixes=CHECK,RV64DZFA define double @fceil(double %a) { @@ -86,3 +85,32 @@ define double @fminimum(double %a, double %b) { %c = call double @llvm.minimum.f64(double %a, double %b) ret double %c } + +define i64 @fmvh_x_d(double %fa) { +; RV32IDZFA-LABEL: fmvh_x_d: +; RV32IDZFA: # %bb.0: +; RV32IDZFA-NEXT: fmv.x.w a0, fa0 +; RV32IDZFA-NEXT: fmvh.x.d a1, fa0 +; RV32IDZFA-NEXT: ret +; +; RV64DZFA-LABEL: fmvh_x_d: +; RV64DZFA: # %bb.0: +; RV64DZFA-NEXT: fmv.x.d a0, fa0 +; RV64DZFA-NEXT: ret + %i = bitcast double %fa to i64 + ret i64 %i +} + +define double @fmvp_d_x(i64 %a) { +; RV32IDZFA-LABEL: fmvp_d_x: +; RV32IDZFA: # %bb.0: +; RV32IDZFA-NEXT: fmvp.d.x fa0, a0, a1 +; RV32IDZFA-NEXT: ret +; +; RV64DZFA-LABEL: fmvp_d_x: +; RV64DZFA: # %bb.0: +; RV64DZFA-NEXT: fmv.d.x fa0, a0 +; RV64DZFA-NEXT: ret + %or = bitcast i64 %a to double + ret double %or +}