Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDAG] Reverse the canonicalization of isInf/isNanOrInf #81404

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 46 additions & 25 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3467,12 +3467,51 @@ void SelectionDAGBuilder::visitICmp(const User &I) {
setValue(&I, DAG.getSetCC(getCurSDLoc(), DestVT, Op1, Op2, Opcode));
}

SDValue SelectionDAGBuilder::lowerIsFpClass(Value *ClassVal,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel great about having SelectionDAGBuilder perform random optimizations. I can see the appeal of reusing the IR implementation of fcmpToClassTest. You would also need to reimplement the same thing in GlobalISel.

Maybe it would be better to do this in CodeGenPrepare?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will post an alternative later.

FPClassTest ClassTest) {
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
const DataLayout &DL = DAG.getDataLayout();
SDLoc sdl = getCurSDLoc();

EVT DestVT =
TLI.getValueType(DL, CmpInst::makeCmpResultType(ClassVal->getType()));
EVT ArgVT = TLI.getValueType(DL, ClassVal->getType());
MachineFunction &MF = DAG.getMachineFunction();
const Function &F = MF.getFunction();
SDValue Op = getValue(ClassVal);
SDNodeFlags Flags;
Flags.setNoFPExcept(!F.getAttributes().hasFnAttr(llvm::Attribute::StrictFP));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can just be true, is.fpclass can never raise exceptions (don't even need the flag?)

// If ISD::IS_FPCLASS should be expanded, do it right now, because the
// expansion can use illegal types. Making expansion early allows
// legalizing these types prior to selection.
if (!TLI.isOperationLegalOrCustom(ISD::IS_FPCLASS, ArgVT))
return TLI.expandIS_FPCLASS(DestVT, Op, ClassTest, Flags, sdl, DAG);

SDValue Check = DAG.getTargetConstant(ClassTest, sdl, MVT::i32);
return DAG.getNode(ISD::IS_FPCLASS, sdl, DestVT, {Op, Check}, Flags);
}

void SelectionDAGBuilder::visitFCmp(const User &I) {
FCmpInst::Predicate predicate = FCmpInst::BAD_FCMP_PREDICATE;
if (const FCmpInst *FC = dyn_cast<FCmpInst>(&I))
if (const FCmpInst *FC = dyn_cast<FCmpInst>(&I)) {
predicate = FC->getPredicate();
else if (const ConstantExpr *FC = dyn_cast<ConstantExpr>(&I))

// Reverse the canonicalization if it is a FP class test
auto ShouldReverseTransform = [](FPClassTest ClassTest) {
return ClassTest == fcInf || ClassTest == (fcInf | fcNan);
};
auto [ClassVal, ClassTest] =
fcmpToClassTest(predicate, *FC->getParent()->getParent(),
FC->getOperand(0), FC->getOperand(1));
if (ClassVal && (ShouldReverseTransform(ClassTest) ||
ShouldReverseTransform(~ClassTest))) {
setValue(&I, lowerIsFpClass(ClassVal, ClassTest));
return;
}
} else if (const ConstantExpr *FC = dyn_cast<ConstantExpr>(&I)) {
predicate = FCmpInst::Predicate(FC->getPredicate());
}

dtcxzyw marked this conversation as resolved.
Show resolved Hide resolved
SDValue Op1 = getValue(I.getOperand(0));
SDValue Op2 = getValue(I.getOperand(1));

Expand Down Expand Up @@ -6666,29 +6705,11 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
DAG.setRoot(Res.getValue(0));
return;
case Intrinsic::is_fpclass: {
const DataLayout DLayout = DAG.getDataLayout();
EVT DestVT = TLI.getValueType(DLayout, I.getType());
EVT ArgVT = TLI.getValueType(DLayout, I.getArgOperand(0)->getType());
FPClassTest Test = static_cast<FPClassTest>(
cast<ConstantInt>(I.getArgOperand(1))->getZExtValue());
MachineFunction &MF = DAG.getMachineFunction();
const Function &F = MF.getFunction();
SDValue Op = getValue(I.getArgOperand(0));
SDNodeFlags Flags;
Flags.setNoFPExcept(
!F.getAttributes().hasFnAttr(llvm::Attribute::StrictFP));
// If ISD::IS_FPCLASS should be expanded, do it right now, because the
// expansion can use illegal types. Making expansion early allows
// legalizing these types prior to selection.
if (!TLI.isOperationLegalOrCustom(ISD::IS_FPCLASS, ArgVT)) {
SDValue Result = TLI.expandIS_FPCLASS(DestVT, Op, Test, Flags, sdl, DAG);
setValue(&I, Result);
return;
}

SDValue Check = DAG.getTargetConstant(Test, sdl, MVT::i32);
SDValue V = DAG.getNode(ISD::IS_FPCLASS, sdl, DestVT, {Op, Check}, Flags);
setValue(&I, V);
setValue(&I,
lowerIsFpClass(
I.getArgOperand(0),
static_cast<FPClassTest>(
cast<ConstantInt>(I.getArgOperand(1))->getZExtValue())));
return;
}
case Intrinsic::get_fpenv: {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ class SelectionDAGBuilder {
MCSymbol *&BeginLabel);
SDValue lowerEndEH(SDValue Chain, const InvokeInst *II,
const BasicBlock *EHPadBB, MCSymbol *BeginLabel);
SDValue lowerIsFpClass(Value *ClassVal, FPClassTest ClassTest);
};

/// This struct represents the registers (physical or virtual)
Expand Down
180 changes: 180 additions & 0 deletions llvm/test/CodeGen/AArch64/fpclass-test.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4
; RUN: llc -mtriple=aarch64 -mattr=+sve < %s | FileCheck %s

define i1 @test_is_inf_or_nan(double %arg) {
; CHECK-LABEL: test_is_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov x9, d0
; CHECK-NEXT: mov x8, #9218868437227405311 // =0x7fefffffffffffff
; CHECK-NEXT: and x9, x9, #0x7fffffffffffffff
; CHECK-NEXT: cmp x9, x8
; CHECK-NEXT: cset w0, gt
; CHECK-NEXT: ret
%abs = tail call double @llvm.fabs.f64(double %arg)
%ret = fcmp ueq double %abs, 0x7FF0000000000000
ret i1 %ret
}

define i1 @test_is_not_inf_or_nan(double %arg) {
; CHECK-LABEL: test_is_not_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: fmov x9, d0
; CHECK-NEXT: mov x8, #9218868437227405312 // =0x7ff0000000000000
; CHECK-NEXT: and x9, x9, #0x7fffffffffffffff
; CHECK-NEXT: cmp x9, x8
; CHECK-NEXT: cset w0, lt
; CHECK-NEXT: ret
%abs = tail call double @llvm.fabs.f64(double %arg)
%ret = fcmp one double %abs, 0x7FF0000000000000
ret i1 %ret
}

define i1 @test_is_inf(double %arg) {
; CHECK-LABEL: test_is_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: fabs d0, d0
; CHECK-NEXT: mov x8, #9218868437227405312 // =0x7ff0000000000000
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fcmp d0, d1
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%abs = tail call double @llvm.fabs.f64(double %arg)
%ret = fcmp oeq double %abs, 0x7FF0000000000000
ret i1 %ret
}

define i1 @test_is_not_inf(double %arg) {
; CHECK-LABEL: test_is_not_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: fabs d0, d0
; CHECK-NEXT: mov x8, #9218868437227405312 // =0x7ff0000000000000
; CHECK-NEXT: fmov d1, x8
; CHECK-NEXT: fcmp d0, d1
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%abs = tail call double @llvm.fabs.f64(double %arg)
%ret = fcmp une double %abs, 0x7FF0000000000000
ret i1 %ret
}

define <vscale x 2 x i1> @test_vec_is_inf_or_nan(<vscale x 2 x double> %arg) {
; CHECK-LABEL: test_vec_is_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov z1.d, #0x7ff0000000000000
; CHECK-NEXT: and z0.d, z0.d, #0x7fffffffffffffff
; CHECK-NEXT: cmpge p0.d, p0/z, z0.d, z1.d
; CHECK-NEXT: ret
%abs = tail call <vscale x 2 x double> @llvm.fabs.nxv2f64(<vscale x 2 x double> %arg)
%ret = fcmp ueq <vscale x 2 x double> %abs, splat (double 0x7FF0000000000000)
ret <vscale x 2 x i1> %ret
}

define <vscale x 2 x i1> @test_vec_is_not_inf_or_nan(<vscale x 2 x double> %arg) {
; CHECK-LABEL: test_vec_is_not_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov z1.d, #0x7ff0000000000000
; CHECK-NEXT: and z0.d, z0.d, #0x7fffffffffffffff
; CHECK-NEXT: cmpgt p0.d, p0/z, z1.d, z0.d
; CHECK-NEXT: ret
%abs = tail call <vscale x 2 x double> @llvm.fabs.nxv2f64(<vscale x 2 x double> %arg)
%ret = fcmp one <vscale x 2 x double> %abs, splat (double 0x7FF0000000000000)
ret <vscale x 2 x i1> %ret
}

define <vscale x 2 x i1> @test_vec_is_inf(<vscale x 2 x double> %arg) {
; CHECK-LABEL: test_vec_is_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov x8, #9218868437227405312 // =0x7ff0000000000000
; CHECK-NEXT: mov z1.d, x8
; CHECK-NEXT: fabs z0.d, p0/m, z0.d
; CHECK-NEXT: fcmeq p0.d, p0/z, z0.d, z1.d
; CHECK-NEXT: ret
%abs = tail call <vscale x 2 x double> @llvm.fabs.nxv2f64(<vscale x 2 x double> %arg)
%ret = fcmp oeq <vscale x 2 x double> %abs, splat (double 0x7FF0000000000000)
ret <vscale x 2 x i1> %ret
}

define <vscale x 2 x i1> @test_vec_is_not_inf(<vscale x 2 x double> %arg) {
; CHECK-LABEL: test_vec_is_not_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov x8, #9218868437227405312 // =0x7ff0000000000000
; CHECK-NEXT: mov z1.d, x8
; CHECK-NEXT: fabs z0.d, p0/m, z0.d
; CHECK-NEXT: fcmne p0.d, p0/z, z0.d, z1.d
; CHECK-NEXT: ret
%abs = tail call <vscale x 2 x double> @llvm.fabs.nxv2f64(<vscale x 2 x double> %arg)
%ret = fcmp une <vscale x 2 x double> %abs, splat (double 0x7FF0000000000000)
ret <vscale x 2 x i1> %ret
}

define i1 @test_fp128_is_inf_or_nan(fp128 %arg) {
; CHECK-LABEL: test_fp128_is_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #9223090561878065151 // =0x7ffeffffffffffff
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: ldr x9, [sp, #8]
; CHECK-NEXT: and x9, x9, #0x7fffffffffffffff
; CHECK-NEXT: cmp x9, x8
; CHECK-NEXT: cset w0, gt
; CHECK-NEXT: add sp, sp, #16
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %arg)
%ret = fcmp ueq fp128 %abs, 0xL00000000000000007FFF000000000000
ret i1 %ret
}

define i1 @test_fp128_is_not_inf_or_nan(fp128 %arg) {
; CHECK-LABEL: test_fp128_is_not_inf_or_nan:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x8, #9223090561878065152 // =0x7fff000000000000
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: ldr x9, [sp, #8]
; CHECK-NEXT: and x9, x9, #0x7fffffffffffffff
; CHECK-NEXT: cmp x9, x8
; CHECK-NEXT: cset w0, lt
; CHECK-NEXT: add sp, sp, #16
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %arg)
%ret = fcmp one fp128 %abs, 0xL00000000000000007FFF000000000000
ret i1 %ret
}

define i1 @test_fp128_is_inf(fp128 %arg) {
; CHECK-LABEL: test_fp128_is_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: ldp x9, x8, [sp], #16
; CHECK-NEXT: and x8, x8, #0x7fffffffffffffff
; CHECK-NEXT: eor x8, x8, #0x7fff000000000000
; CHECK-NEXT: orr x8, x9, x8
; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %arg)
%ret = fcmp oeq fp128 %abs, 0xL00000000000000007FFF000000000000
ret i1 %ret
}

define i1 @test_fp128_is_not_inf(fp128 %arg) {
; CHECK-LABEL: test_fp128_is_not_inf:
; CHECK: // %bb.0:
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: ldp x9, x8, [sp], #16
; CHECK-NEXT: and x8, x8, #0x7fffffffffffffff
; CHECK-NEXT: eor x8, x8, #0x7fff000000000000
; CHECK-NEXT: orr x8, x9, x8
; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %arg)
%ret = fcmp une fp128 %abs, 0xL00000000000000007FFF000000000000
ret i1 %ret
}
22 changes: 7 additions & 15 deletions llvm/test/CodeGen/AArch64/isinf.ll
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,14 @@ define i32 @replace_isinf_call_f64(double %x) {
define i32 @replace_isinf_call_f128(fp128 %x) {
; CHECK-LABEL: replace_isinf_call_f128:
; CHECK: // %bb.0:
; CHECK-NEXT: sub sp, sp, #32
; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill
; CHECK-NEXT: .cfi_def_cfa_offset 32
; CHECK-NEXT: .cfi_offset w30, -16
; CHECK-NEXT: str q0, [sp]
; CHECK-NEXT: ldrb w8, [sp, #15]
; CHECK-NEXT: and w8, w8, #0x7f
; CHECK-NEXT: strb w8, [sp, #15]
; CHECK-NEXT: adrp x8, .LCPI3_0
; CHECK-NEXT: ldr q0, [sp]
; CHECK-NEXT: ldr q1, [x8, :lo12:.LCPI3_0]
; CHECK-NEXT: bl __eqtf2
; CHECK-NEXT: cmp w0, #0
; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: ldp x9, x8, [sp], #16
; CHECK-NEXT: and x8, x8, #0x7fffffffffffffff
; CHECK-NEXT: eor x8, x8, #0x7fff000000000000
; CHECK-NEXT: orr x8, x9, x8
; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: add sp, sp, #32
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %x)
%cmpinf = fcmp oeq fp128 %abs, 0xL00000000000000007FFF000000000000
Expand Down
Loading
Loading