Skip to content

Commit

Permalink
[RISCV][GISel] Support G_MERGE_VALUES/G_UNMERGE_VALUES with Zfa. (llv…
Browse files Browse the repository at this point in the history
…m#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.
  • Loading branch information
topperc authored Jan 7, 2025
1 parent 4e36d5b commit 785b16a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 24 deletions.
35 changes: 14 additions & 21 deletions llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -732,35 +731,20 @@ 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:
return false;
}
}

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;
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoD.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<G_MERGE_VALUES, RISCVBuildPairF64>;
def RISCVSplitF64 : SDNode<"RISCVISD::SplitF64", SDT_RISCVSplitF64>;
def : GINodeEquiv<G_UNMERGE_VALUES, RISCVSplitF64>;

def AddrRegImmINX : ComplexPattern<iPTR, 2, "SelectAddrRegImmRV32Zdinx">;

Expand Down
34 changes: 31 additions & 3 deletions llvm/test/CodeGen/RISCV/GlobalISel/double-zfa.ll
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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
}

0 comments on commit 785b16a

Please sign in to comment.