Skip to content

CodeGen: Strengthen definition of F{MIN|MAX}NUM_IEEE nodes #85195

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

Merged

Conversation

arsenm
Copy link
Contributor

@arsenm arsenm commented Mar 14, 2024

Previously these were declared as having the 2008 behavior, with underspecified signed zero handling. Currently, AMDGPU, PPC and LoongArch mark these as legal. The AMDGPU and PPC instructions respect the signed zero behavior. The LoongArch documentation doesn't state, but I'm assuming it also does.

Previously these were declared as having the 2008 behavior, with
underspecified signed zero handling. Currently, AMDGPU, PPC and LoongArch
mark these as legal. The AMDGPU and PPC instructions respect the signed
zero behavior. The LoongArch documentation doesn't state, but I'm assuming
it also does.
@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2024

@llvm/pr-subscribers-llvm-globalisel
@llvm/pr-subscribers-llvm-selectiondag

@llvm/pr-subscribers-backend-loongarch

Author: Matt Arsenault (arsenm)

Changes

Previously these were declared as having the 2008 behavior, with underspecified signed zero handling. Currently, AMDGPU, PPC and LoongArch mark these as legal. The AMDGPU and PPC instructions respect the signed zero behavior. The LoongArch documentation doesn't state, but I'm assuming it also does.


Full diff: https://github.com/llvm/llvm-project/pull/85195.diff

3 Files Affected:

  • (modified) llvm/docs/GlobalISel/GenericOpcode.rst (+8-6)
  • (modified) llvm/include/llvm/CodeGen/ISDOpcodes.h (+5-4)
  • (modified) llvm/include/llvm/Target/GenericOpcodes.td (+5-4)
diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst
index ac6217d08e6a60..f7d1d5b3edd6a2 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.rst
+++ b/llvm/docs/GlobalISel/GenericOpcode.rst
@@ -521,16 +521,18 @@ The return value of (FMAXNUM 0.0, -0.0) could be either 0.0 or -0.0.
 G_FMINNUM_IEEE
 ^^^^^^^^^^^^^^
 
-Perform floating-point minimum on two values, following the IEEE-754 2008
-definition. This differs from FMINNUM in the handling of signaling NaNs. If one
-input is a signaling NaN, returns a quiet NaN.
+Perform floating-point minimum on two values, following the IEEE-754
+2019 definition. This differs from FMINNUM in the handling of
+signaling NaNs. If one input is a signaling NaN, returns a quiet
+NaN. This treats -0 as ordered less than +0.
 
 G_FMAXNUM_IEEE
 ^^^^^^^^^^^^^^
 
-Perform floating-point maximum on two values, following the IEEE-754 2008
-definition. This differs from FMAXNUM in the handling of signaling NaNs. If one
-input is a signaling NaN, returns a quiet NaN.
+Perform floating-point maximum on two values, following the IEEE-754
+2019 definition. This differs from FMAXNUM in the handling of
+signaling NaNs. If one input is a signaling NaN, returns a quiet
+NaN. This treats -0 as ordered less than +0.
 
 G_FMINIMUM
 ^^^^^^^^^^
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 49d51a27e3c0f6..3aa33ee2021250 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -971,10 +971,11 @@ enum NodeType {
   FMINNUM,
   FMAXNUM,
 
-  /// FMINNUM_IEEE/FMAXNUM_IEEE - Perform floating-point minimum or maximum on
-  /// two values, following the IEEE-754 2008 definition. This differs from
-  /// FMINNUM/FMAXNUM in the handling of signaling NaNs. If one input is a
-  /// signaling NaN, returns a quiet NaN.
+  /// FMINNUM_IEEE/FMAXNUM_IEEE - Perform floating-point minimumNumber or
+  /// maximumNumber on two values, following the IEEE-754 2019 definition. This
+  /// differs from FMINNUM/FMAXNUM in the handling of signaling NaNs. If one
+  /// input is a signaling NaN, returns a quiet NaN. These treat -0 as ordered
+  /// less than +0.
   FMINNUM_IEEE,
   FMAXNUM_IEEE,
 
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index 67d405ba96fa10..c9513f70a7c613 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -795,10 +795,11 @@ def G_FMAXNUM : GenericInstruction {
   let isCommutable = true;
 }
 
-// FMINNUM_IEEE/FMAXNUM_IEEE - Perform floating-point minimum or maximum on
-// two values, following the IEEE-754 2008 definition. This differs from
-// FMINNUM/FMAXNUM in the handling of signaling NaNs. If one input is a
-// signaling NaN, returns a quiet NaN.
+// FMINNUM_IEEE/FMAXNUM_IEEE - Perform floating-point minimumNumber or
+// maximumNumber on two values, following the IEEE-754 2019
+// definition. This differs from FMINNUM/FMAXNUM in the handling of
+// signaling NaNs. If one input is a signaling NaN, returns a quiet
+// NaN. These treat -0 as ordered less than +0.
 def G_FMINNUM_IEEE : GenericInstruction {
   let OutOperandList = (outs type0:$dst);
   let InOperandList = (ins type0:$src1, type0:$src2);

Perform floating-point minimum on two values, following the IEEE-754
2019 definition. This differs from FMINNUM in the handling of
signaling NaNs. If one input is a signaling NaN, returns a quiet
NaN. This treats -0 as ordered less than +0.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't this what G_FMINNUM is supposed to do? The comment for that says that G_FMINNUM has 2019 behaviour, and G_FMINNUM_IEEE has 2008 behaviour. Are you sure we want both to do the same thing? Could we just have G_FMINNUM_IEEE2008 and G_FMINNUM_IEEE2019 instead, so it's less confusing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, because they differ in the behavior for signaling nans. The minnum/maxnum intrinsics were misnamed. They should have been named fmin/fmax, as they follow the libm behavior which ignores signaling nans. The IEEE signaling nan behavior makes no sense, and returns a quiet nan.

Nothing about the nan handling is changing. This PR only adds the stronger signed 0 behavior

Copy link
Collaborator

Choose a reason for hiding this comment

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

IEEE-754 2019 defines two functions for minimum: minimum and minimumNumber. So it worth mentioning that this node represents just minimumNumber. The described treatment of SNaN is also slightly different from IEEE-754:

minimumNumber(x, y) is x if x < y, y if y < x, and the number if one operand is a number and the
other is a NaN. For this operation, −0 compares less than +0. If x = y and signs are the same it is
either x or y. If both operands are NaNs, a quiet NaN is returned, according to 6.2. If either
operand is a signaling NaN, an invalid operation exception is signaled, but unless both operands
are NaNs, the signaling NaN is otherwise ignored and not converted to a quiet NaN as stated in
6.2 for other operations.

Is this difference intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. The point of these intermediate nodes is to match hardware behavior for legalization, in which case (for AMDGPU at least) they have the 2008 signaling nan behavior with the stronger 2019 signed zero behavior.

But for the unconstrained versions, the langref hand waves away signaling nan handling saying they may be treated as quiet nans

Maybe these nodes need another rename.

input is a signaling NaN, returns a quiet NaN.
Perform floating-point minimum on two values, following the IEEE-754
2019 definition. This differs from FMINNUM in the handling of
signaling NaNs. If one input is a signaling NaN, returns a quiet
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it? I thought it was supposed to return the number. It should only return a quiet NaN if both inputs are NaNs, no?

@arsenm
Copy link
Contributor Author

arsenm commented Mar 20, 2024

ping

@spavloff
Copy link
Collaborator

LGTM.

Copy link

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link

✅ With the latest revision this PR passed the Python code formatter.

@ecnelises
Copy link
Member

The semantics change looks good to me, but I'm still feeling it's (naming?) a little weird: FMAXNUM follows 2008 rules, FMAXIMUM follows 2019 rules, while FMAXNUM_IEEE follows a mix of 2008 and 2019 to match current hardware behavior (and make lowering easier). Will some target be an exception of such assumption?

@arsenm
Copy link
Contributor Author

arsenm commented Mar 28, 2024

Will some target be an exception of such assumption?

Probably, but the point of these intermediates is legalization aid since we don't directly expose them so the point is to match whatever hardware behavior is. We can introduce more variants as necessary

@arsenm
Copy link
Contributor Author

arsenm commented Apr 3, 2024

ping

1 similar comment
@arsenm
Copy link
Contributor Author

arsenm commented Apr 17, 2024

ping

Copy link
Collaborator

@spavloff spavloff left a comment

Choose a reason for hiding this comment

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

LGTM.

@arsenm arsenm merged commit dc9664a into llvm:main Apr 22, 2024
@arsenm arsenm deleted the strengthen-minnum-maxnum-signed-zero-definitions branch April 22, 2024 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants