Skip to content

[RISCV] Use isCompatible when we need runtime VSETVLIInfo equality. NFC #94340

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
merged 4 commits into from
Jun 15, 2024

Conversation

lukel97
Copy link
Contributor

@lukel97 lukel97 commented Jun 4, 2024

In VSETVLIInfo we define == as lattice equality, e.g. unknown == unknown and invalid == invalid. We need this to check if the information at a block's entry or exit has changed.

However I think we may have been conflating it with the notion that the state of VL and VTYPE are the same, which isn't the case for two unknowns, and potentially in an upcoming patch where we don't know the value of an AVL register (see #93796)

This patch switches over the use in emitVSETVLIs to use isCompatible with all fields demanded, since we need VL and VTYPE to be known equal at runtime rather than just the VSETVLIInfos to be the same.

This should be NFC for now we shouldn't reach here with an unknown state. But it's needed if we're to introduce the notion of an AVLReg with a nullptr ValNo, which will need this non-reflexive equality. Specifically, hasSameAVL should return false for this case, but == should return true.

@lukel97 lukel97 requested review from BeMg and preames June 4, 2024 11:58
@llvmbot
Copy link
Member

llvmbot commented Jun 4, 2024

@llvm/pr-subscribers-backend-risc-v

Author: Luke Lau (lukel97)

Changes

In VSETVLIInfo we define == as structural equality, e.g. unknown == unknown and invalid == invalid. We need this to check if the information at a block's entry or exit has changed.

However I think we may have been conflating it with the notion that the state of VL and VTYPE are the same, which isn't the case for two unknowns, and potentially in an upcoming patch where we don't know the value of an AVL register (see #93796)

This patch introduces a new method for checking that the VL and VTYPE are the same and switches it over where it would make a difference.

This should be NFC for now since the two uses of == we're replacing either didn't compare two unknowns or checked for unknowns. But it's needed if we're to introduce the notion of an AVLReg with a nullptr ValNo, which will need this non-reflexive equality.


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

1 Files Affected:

  • (modified) llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp (+23-4)
diff --git a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp
index a96768240a933..6f7f58ca5206d 100644
--- a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp
@@ -732,6 +732,19 @@ class VSETVLIInfo {
     return hasCompatibleVTYPE(Used, Require);
   }
 
+  bool isKnownSame(const VSETVLIInfo &Other) const {
+    if (!isValid() || !Other.isValid())
+      return false;
+    if (isUnknown() || Other.isUnknown())
+      return false;
+    if (SEWLMULRatioOnly || Other.SEWLMULRatioOnly)
+      return false;
+    return hasSameAVL(Other) && hasSameVTYPE(Other);
+  }
+
+  // Whether or not two VSETVLIInfos are the same. Note that this does
+  // not necessarily mean they have the same AVL or VTYPE. Use
+  // isCompatible or isKnownSame for that instead.
   bool operator==(const VSETVLIInfo &Other) const {
     // Uninitialized is only equal to another Uninitialized.
     if (!isValid())
@@ -745,7 +758,13 @@ class VSETVLIInfo {
     if (Other.isUnknown())
       return isUnknown();
 
-    if (!hasSameAVL(Other))
+    // If what we know about the AVL is different, then they aren't equal.
+    if (State != Other.State)
+      return false;
+    if (hasAVLReg() && !(getAVLReg() == Other.getAVLReg() &&
+                         getAVLVNInfo() == Other.getAVLVNInfo()))
+      return false;
+    if (hasAVLImm() && getAVLImm() != Other.getAVLImm())
       return false;
 
     // If the SEWLMULRatioOnly bits are different, then they aren't equal.
@@ -780,7 +799,7 @@ class VSETVLIInfo {
       return VSETVLIInfo::getUnknown();
 
     // If we have an exact, match return this.
-    if (*this == Other)
+    if (isKnownSame(Other))
       return *this;
 
     // Not an exact match, but maybe the AVL and VLMAX are the same. If so,
@@ -1375,7 +1394,7 @@ bool RISCVInsertVSETVLI::needVSETVLIPHI(const VSETVLIInfo &Require,
     // We found a VSET(I)VLI make sure it matches the output of the
     // predecessor block.
     VSETVLIInfo DefInfo = getInfoForVSETVLI(*DefMI);
-    if (DefInfo != PBBExit)
+    if (!DefInfo.isKnownSame(PBBExit))
       return true;
 
     // Require has the same VL as PBBExit, so if the exit from the
@@ -1412,7 +1431,7 @@ void RISCVInsertVSETVLI::emitVSETVLIs(MachineBasicBlock &MBB) {
 
     uint64_t TSFlags = MI.getDesc().TSFlags;
     if (RISCVII::hasSEWOp(TSFlags)) {
-      if (PrevInfo != CurInfo) {
+      if (!PrevInfo.isKnownSame(CurInfo)) {
         // If this is the first implicit state change, and the state change
         // requested can be proven to produce the same register contents, we
         // can skip emitting the actual state change and continue as if we

Copy link
Contributor

@BeMg BeMg left a comment

Choose a reason for hiding this comment

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

LGTM

preames added a commit to preames/llvm-project that referenced this pull request Jun 6, 2024
Stacked on llvm#94658.

We recently moved RISCVInsertVSETVLI from before vector register allocation to after vector register allocation.  When doing so, we added an unconditional dependency on LiveIntervals - even at O0 where LiveIntevals hadn't previously run.  As reported in llvm#93587, this was apparently not safe to do.

This change makes LiveIntervals optional, and adjusts all the update code to only run wen live intervals is present.  The only real tricky part of this change is the abstract state tracking in the dataflow.  We need to represent a "register w/unknown definition" state - but only when we don't have LiveIntervals.

This adjust the abstract state definition so that the AVLIsReg state can represent either a register + valno, or a register + unknown definition.  With LiveIntervals, wehave an exact definition for each AVL use.  Without LiveIntervals, we  treat the definition of a register AVL as being unknown.

The key semantic change is that we now have a state in the lattice for which something is known about the AVL value, but for which two identical lattice elements do *not* neccessarily represent the same AVL value at runtime.  Previously, the only case which could result in such an unknown AVL was the fully unknown state (where VTYPE is also fully unknown).  This requires a small adjustment to hasSameAVL and lattice state equality to draw this important distinction.

The net effect of this patch is that we remove the LiveIntervals dependency at O0, and O0 code quality will regress for cases involving register AVL values.

This patch is an alternative to llvm#93796 and llvm#94340.  It is very directly inspired by review conversation around them, and thus should be considered coauthored by Luke.
@lukel97 lukel97 changed the title [RISCV] Use separate VSETVLIInfo check for VL/VTYPE equality. NFC [RISCV] Use isCompatible when we need runtime VSETVLIInfo equality. NFC Jun 11, 2024
@lukel97
Copy link
Contributor Author

lukel97 commented Jun 11, 2024

I've tweaked this to reuse isCompatible instead of introducing a new method. == should now be strictly for lattice equality

lukel97 added 3 commits June 14, 2024 09:55
In VSETVLIInfo we define == as structural equality, e.g. unknown == unknown and invalid == invalid. We need this to check if the information at a block's entry or exit has changed.

However I think we may have been conflating it with the notion that the state of VL and VTYPE are the same, which isn't the case for two unknowns, and potentially in an upcoming patch where we don't know the value of an AVL register (see llvm#93796)

This patch introduces a new method for checking that the VL and VTYPE are the same and switches it over where it would make a difference.

This should be NFC for now since the two uses of == we're replacing either didn't compare two unknowns or checked for unknowns. But it's needed if we're to introduce the notion of an AVLReg with a nullptr ValNo, which will need this non-reflexive equality.
This should be the only place where after removing LiveIntervals it will
make a difference.

Also don't bother checking for nullptr LIS in hasNonZeroAVL since we
shouldn't hit that path with a fully demanded DemandedFields.
@lukel97 lukel97 force-pushed the insert-vsetvli-isKnownSame branch from d8e68bd to 02f84af Compare June 14, 2024 02:08
@lukel97
Copy link
Contributor Author

lukel97 commented Jun 14, 2024

Turns out we shouldn't use it in intersect, as that still requires lattice equality. We also don't need it for needVSETVLIPHI since we bail out for the case without a VNInfo in #94686

Copy link
Collaborator

@preames preames left a comment

Choose a reason for hiding this comment

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

LGTM to the revised form.

Please make sure you remember to update the commit message when landing though!

@lukel97 lukel97 merged commit 7d06bdc into llvm:main Jun 15, 2024
5 of 7 checks passed
preames added a commit to preames/llvm-project that referenced this pull request Jun 17, 2024
We recently moved RISCVInsertVSETVLI from before vector register allocation to after vector register allocation.  When doing so, we added an unconditional dependency on LiveIntervals - even at O0 where LiveIntevals hadn't previously run.  As reported in llvm#93587, this was apparently not safe to do.

This change makes LiveIntervals optional, and adjusts all the update code to only run wen live intervals is present.  The only real tricky part of this change is the abstract state tracking in the dataflow.  We need to represent a "register w/unknown definition" state - but only when we don't have LiveIntervals.

This adjust the abstract state definition so that the AVLIsReg state can represent either a register + valno, or a register + unknown definition.  With LiveIntervals, wehave an exact definition for each AVL use.  Without LiveIntervals, we  treat the definition of a register AVL as being unknown.

The key semantic change is that we now have a state in the lattice for which something is known about the AVL value, but for which two identical lattice elements do *not* neccessarily represent the same AVL value at runtime.  Previously, the only case which could result in such an unknown AVL was the fully unknown state (where VTYPE is also fully unknown).  This requires a small adjustment to hasSameAVL and lattice state equality to draw this important distinction.

The net effect of this patch is that we remove the LiveIntervals dependency at O0, and O0 code quality will regress for cases involving register AVL values.  In practice, this means we pessimize code written with intrinsics at O0.

This patch is an alternative to llvm#93796 and llvm#94340.  It is very directly inspired by review conversation around them, and thus should be considered coauthored by Luke.
preames added a commit that referenced this pull request Jun 17, 2024
Stacked on #94658.
    
We recently moved RISCVInsertVSETVLI from before vector register
allocation to after vector register allocation. When doing so, we added
an unconditional dependency on LiveIntervals - even at O0 where
LiveIntevals hadn't previously run. As reported in #93587, this was
apparently not safe to do.
    
This change makes LiveIntervals optional, and adjusts all the update
code to only run wen live intervals is present. The only real tricky
part of this change is the abstract state tracking in the dataflow. We
need to represent a "register w/unknown definition" state - but only
when we don't have LiveIntervals.
    
This adjust the abstract state definition so that the AVLIsReg state can
represent either a register + valno, or a register + unknown definition.
With LiveIntervals, we have an exact definition for each AVL use.
Without LiveIntervals, we treat the definition of a register AVL as
being unknown.
    
The key semantic change is that we now have a state in the lattice for
which something is known about the AVL value, but for which two
identical lattice elements do *not* necessarily represent the same AVL
value at runtime. Previously, the only case which could result in such
an unknown AVL was the fully unknown state (where VTYPE is also fully
unknown). This requires a small adjustment to hasSameAVL and lattice
state equality to draw this important distinction.
    
The net effect of this patch is that we remove the LiveIntervals
dependency at O0, and O0 code quality will regress for cases involving
register AVL values.
    
This patch is an alternative to
#93796 and
#94340. It is very directly
inspired by review conversation around them, and thus should be
considered coauthored by Luke.
preames added a commit that referenced this pull request Jun 17, 2024
(Reapplying with corrected commit message)

We recently moved RISCVInsertVSETVLI from before vector register allocation
to after vector register allocation.  When doing so, we added an unconditional
dependency on LiveIntervals - even at O0 where LiveIntevals hadn't previously
run.  As reported in #93587, this was apparently not safe to do.

This change makes LiveIntervals optional, and adjusts all the update code to
only run wen live intervals is present.  The only real tricky part of this
change is the abstract state tracking in the dataflow.  We need to represent
a "register w/unknown definition" state - but only when we don't have
LiveIntervals.

This adjust the abstract state definition so that the AVLIsReg state can
represent either a register + valno, or a register + unknown definition.
With LiveIntervals, we have an exact definition for each AVL use.  Without
LiveIntervals, we  treat the definition of a register AVL as being unknown.

The key semantic change is that we now have a state in the lattice for which
something is known about the AVL value, but for which two identical lattice
elements do *not* neccessarily represent the same AVL value at runtime.
Previously, the only case which could result in such an unknown AVL was the
fully unknown state (where VTYPE is also fully unknown).  This requires a
small adjustment to hasSameAVL and lattice state equality to draw this
important distinction.

The net effect of this patch is that we remove the LiveIntervals dependency
at O0, and O0 code quality will regress for cases involving register AVL values.
In practice, this means we pessimize code written with intrinsics at O0.

This patch is an alternative to #93796 and #94340.  It is very directly
inspired by review conversation around them, and thus should be considered
coauthored by Luke.
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.

4 participants