Extending fast-math flags

Hi,

I opened this topic in the SPIR-V registry a while back: Clarify SPIR-V fast-math flags semantics compared to LLVM · Issue #326 · KhronosGroup/SPIRV-Registry · GitHub
This highlights the fact that the LLVM IR reassociation flag confusingly allows much more than reassociation, which is not what the corresponding SPIR-V flag means.

I was wondering if aligning with SPIR-V, by splitting the current reassociation flag in 2 would make sense (so general transforms vs only reassociation), but it’s not really possible with the current framework having a hard-coded 7 bits for fast-math flags.

It would also be useful for custom/downstream targets using LLVM to have the ability to extend the existing fast-math flags with their own downstream flags, which is also not really supported right now I think?

So I’m wondering, has there been any work or discussion around the possibility of changing the current implementation to allow for more than the existing 7 bits? :slightly_smiling_face:

1 Like

cc: @chelini @matthias-springer

@jcranmer

See also [RFC] Changing llvm::Value layout. Part of the problem with extending fast math flags is that we’re out of free bits in their current storage location, so this will need layout changes. (I think there is a general agreement that we’re going to need more FMF though.)

As Nikita kindly linked, I’ve previously explored how to change
llvm::Value layout to fit more bits for fast-math flags. The
conclusion I had was that there are viable paths forward for scrounging
the bits necessary, but until I had extra FMF ready to go, I wasn’t
going to push harder on making the changes.

I was wondering if aligning with SPIR-V, by splitting the current
reassociation flag in 2 would make sense (so general transforms vs only
reassociation), but it’s not really possible with the current framework
having a hard-coded 7 bits for fast-math flags.

I’ve not had time to circle back to reassoc in my attempts to firm up
the specification of FMF. My tentative plan for reassoc is to break it
up into one flag which restores the more traditional meaning of
reassociation (permits the assumption of distributivity and
associativity of the basic FP operations) and a second flag which covers
the more recent abuse of reassoc to permit general algebraic
transformations (I proposed this break as far back as
[RFC] Improving IR fast-math semantics).

Thanks a lot for the clarifications.

In SPIR-V the reassociation fast-math flag only permits transforms following associative law as I understand, so it’s a bit awkward for compilers targeting SPIR-V since there’s no way to represent that flag with LLVM IR. This would still be the same situation then if the flag is meant to allow both distributivity and associativity I guess? I understand LLVM does not have to follow SPIR-V semantics, but this potential mismatch might be worth considering when deciding the granularity of the flags.

We would also find it very useful to be able to extend this with custom flags, but I was worried this might be difficult to add to upstream if there’s no use for upstream targets. But if the split of the reassoc flag is done, maybe some of the extra bits which are unlocked with whatever path is chosen could be reserved as custom bits?

I am not sure if something like that would be considered reasonable by the broader LLVM community or not though.

In SPIR-V the reassociation fast-math flag only permits transforms
following associative law as I understand, so it’s a bit awkward for
compilers targeting SPIR-V since there’s no way to represent that flag
with LLVM IR. This would still be the same situation then if the flag
is meant to allow both distributivity and associativity I guess? I
understand LLVM does not have to follow SPIR-V semantics, but this
potential mismatch might be worth considering when deciding the
granularity of the flags.

My experience is that an option named like -fassociative-math enables
both the associativity and the distributive law. Distributivity is kind
of like associativity but applied to different operations. If someone’s
not explicitly mentioning distributivity, it’s generally safe to assume
that “allows associativity” should be read as including both
distributivity and associativity. (And since SPIR-V is somewhat based on
LLVM as of several years ago, I should point out that the (a * b + a *
c) → a * (b + c) transformation was reclassified as requiring reassoc
essentially as soon as the flag was introduced.) In short, the new flag
should be able to represent SPIR-V semantics.

Thanks, that was not really obvious to me so appreciate the clarification.

With that said, the current wording in the SPIR-V spec does not really read like it allows distributive law imo:
”Allows a floating-point operation to be reordered with any operation(s) producing its operands according to real-number associativity rules. The instructions producing the operands do not need to be decorated to allow this transformation.”

There is also an “AllowTransform” flag which seems like it’s expected to cover things like distributive law. If the intention was that the SPIR-V AllowReassoc flag is supposed to allow distributive law, I think the spec needs an update, and I’m also not sure whether that was the intention or not when the extension adding those flags was drafted.

I can open a SPIR-V issue to double check what exactly the intention is here, and whether something needs to change on the SPIR-V side or not based on the clarifications you provided here.

I have raised the issue now with the SPIR-V working group, the feedback I got is:

  • The current SPIR-V flag is intended to strictly apply to associative law only, it does not include distributive law.
  • Changing this now is too late.

So this leaves us in a position where SPIR-V and LLVM flags would become misaligned, which is very unfortunate.

I have two questions as a follow-up:

  • Is there any possibility of aligning the future LLVM flags with what SPIR-V is doing? What would be the implication of going this path?
  • Is there any issue, ticket or similar around this issue of improving/splitting the flags in LLVM that can be linked from the related SPIR-V issues to track what is happening on the LLVM side?