Skip to content

[LangRef] Clarify the semantics of fast-math flags #89442

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

Closed
wants to merge 1 commit into from

Conversation

andykaylor
Copy link
Contributor

This change clarifies the semantics of the fast-math flags when a
transformation enabled by fast-math flags involves more than one
instruction.

This was discussed at https://discourse.llvm.org/t/rfc-fast-math-optimizations-with-mixed-fast-math-flags/78320

This change clarifies the semantics of the fast-math flags when a
transformation enabled by fast-math flags involves more than one
instruction.

This was discussed at https://discourse.llvm.org/t/rfc-fast-math-optimizations-with-mixed-fast-math-flags/78320
@llvmbot
Copy link
Member

llvmbot commented Apr 19, 2024

@llvm/pr-subscribers-llvm-ir

Author: Andy Kaylor (andykaylor)

Changes

This change clarifies the semantics of the fast-math flags when a
transformation enabled by fast-math flags involves more than one
instruction.

This was discussed at https://discourse.llvm.org/t/rfc-fast-math-optimizations-with-mixed-fast-math-flags/78320


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

1 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+39)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9592929d79feb4..dcb21330fa8156 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -3656,6 +3656,45 @@ floating-point transformations.
 ``fast``
    This flag implies all of the others.
 
+When performing a transformation that involves more that one instruction, the
+flags required to enable the transformation must be set on all instructions
+involved in the transformation, and any new instructions created by the
+transformation should have only those flags set which were set on all the
+original instructions that are being transformed.
+
+For example
+
+::
+
+%mul1 = fmul float %x, %y
+%mul2 = fmul fast float %mul1, %z
+
+cannot be transformed to
+
+::
+
+%mul1 = fmul float %x, %z
+%mul2 = fmul fast float %mul1, %y
+
+because the %mul1 instruction does not have the 'reassoc' flag set.
+
+Similarly, if applying reassociation to
+
+::
+
+%mul1 = fmul reassoc float %x, %y
+%mul2 = fmul fast float %mul1, %z
+
+the result must be
+
+::
+
+%mul1 = fmul reassoc float %x, %z
+%mul2 = fmul reassoc float %mul1, %y
+
+because only the reassoc flag is set on both original instructions.
+
+
 .. _uselistorder:
 
 Use-list Order Directives

Comment on lines +3662 to +3663
transformation should have only those flags set which were set on all the
original instructions that are being transformed.
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't universally true. In some cases, you can or in the flags. In others you can infer new nnan/ninf. It should be true for reassoc

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, I suppose you're right that the semantics we've defined for nnan and ninf make that true in many cases. This leads to an increasing sense I have that those two flags are fundamentally different from the other fast-math flags. Those two flags provide clear, consistent, and rather strict semantics, whereas the other flags all have a sort of "you're allowed to do something here that you normally couldn't" character.

This also significantly complicates the reasoning involved. For instance

%mul1 = fmul reassoc float %x, %y
%mul2 = fmul fast float %mul1, %z

becomes

%mul1.1 = fmul reassoc nnan float %x, %z
%mul2.1 = fmul reassoc nnan float %mul1, %y

which is already tricky because the possibility of %x=INF, %y=0 prevents me from setting ninf on %mul2.1 and %x=0. %y=INF prevents me from setting ninf on %mul1.1

but also

%mul1 = fmul fast float %x, %y
%mul2 = fmul reassoc float %mul1, %z

becomes

%mul1.1 = fmul reassoc float %x, %z
%mul2.1 = fmul reassoc float %mul1, %y

because we don't have enough information to set ninf or nnan on either transformed instruction.

In practice, I think this would be difficult to implement any way other than to apply the rule I stated and rely on a later pass to infer nnan and ninf where it can. Of course, for the purposes of describing the semantics, it may be best to say something like

any new instructions created by the transformation should have only those flags set which were set on all the original instructions that are being transformed unless additional flags can be strictly deduced from the original IR.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you want to add more of a guideline, maybe could say something about how you can infer that. e.g. since nnan/ninf imply something about the inputs in one instruction, it implies something about the value in the other use

Copy link
Contributor

Choose a reason for hiding this comment

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

I've been putting together an RFC on FMF semantics, but one of the things I note is that there's a definite dichotomy between nnan/ninf/nsz (which have clear value-based semantics) and reassoc/afn/arcp/contract (which probably have to be rewrite-based semantics). For the rewrite-based semantics flags, it's necessary that the flags be present on all operations in the expression.

I don't have great wording to suggest at the moment (not unless you want a page of text), but maybe it would be better to delineate the flags for which the all-instructions-must-have-the-flags-to-be-rewritten property is true?

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 have great wording to suggest at the moment (not unless you want a page of text), but maybe it would be better to delineate the flags for which the all-instructions-must-have-the-flags-to-be-rewritten property is true?

I think for the purposes of this change, explicitly listing the flags for which this guidance applies would indeed be best.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants