Skip to content

[RISCV] Attach an implicit source operand on vector copies #126155

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 2 commits into from
Feb 9, 2025

Conversation

mshockwave
Copy link
Member

Somtimes when we're breaking up a large vector copy into several smaller ones, not every single smaller source registers are initialized at the time when the original COPY happens, and the verifier will not be pleased when seeing the smaller copies reading from an undef register. This patch is a workaround for the said issue by attaching an implicit read of the source operand on the newly generated copies.

This is tested by llvm/test/CodeGen/RISCV/rvv/vector-interleave.ll which would have crashed the compiler without this fix when LLVM_EXPENSIVE_CHECK is enabled. Original context: #124825 (comment)

Somtimes when we're breaking up a large vector copy into several smaller
ones, not every single smaller source registers are initialized at the
time when the original COPY happens, and the verifier will not be
pleased when seeing the smaller copies reading from an undef register.
This patch is a workaround for the said issue by attaching an implicit
read of the source operand on the newly generated copies.

This is tested by llvm/test/CodeGen/RISCV/rvv/vector-interleave.ll which
would have crashed without this fix when LLVM_EXPENSIVE_CHECK is
enabled.

Co-Authored-By: Craig Topper <[email protected]>
@llvmbot
Copy link
Member

llvmbot commented Feb 6, 2025

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

Author: Min-Yih Hsu (mshockwave)

Changes

Somtimes when we're breaking up a large vector copy into several smaller ones, not every single smaller source registers are initialized at the time when the original COPY happens, and the verifier will not be pleased when seeing the smaller copies reading from an undef register. This patch is a workaround for the said issue by attaching an implicit read of the source operand on the newly generated copies.

This is tested by llvm/test/CodeGen/RISCV/rvv/vector-interleave.ll which would have crashed the compiler without this fix when LLVM_EXPENSIVE_CHECK is enabled. Original context: #124825 (comment)


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

1 Files Affected:

  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+5)
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 12a7af07508136..47d6a5f4f95dfe 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -437,6 +437,11 @@ void RISCVInstrInfo::copyPhysRegVector(
       MIB.addReg(RISCV::VL, RegState::Implicit);
       MIB.addReg(RISCV::VTYPE, RegState::Implicit);
     }
+    // Add an implicit read of the original source to silence the verifier
+    // in the cases where some of the smaller VRs we're copying from might be
+    // undef, caused by the fact that the original, larger source VR might not
+    // be fully initialized at the time this COPY happens.
+    MIB.addReg(SrcReg, RegState::Implicit);
 
     // If we are copying reversely, we should decrease the encoding.
     SrcEncoding += (ReversedCopy ? -NumCopied : NumCopied);

Copy link

github-actions bot commented Feb 6, 2025

⚠️ undef deprecator found issues in your code. ⚠️

You can test this locally with the following command:
git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef[^a-zA-Z0-9_-]|UndefValue::get)' dcb124e820b2bf9dda60f66151591155a385580e aa8e9acd645d48fb971825f42a913a275c016b2c llvm/lib/Target/RISCV/RISCVInstrInfo.cpp

The following files introduce new uses of undef:

  • llvm/lib/Target/RISCV/RISCVInstrInfo.cpp

Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields undef. You should use poison values for placeholders instead.

In tests, avoid using undef and having tests that trigger undefined behavior. If you need an operand with some unimportant value, you can add a new argument to the function and use that instead.

For example, this is considered a bad practice:

define void @fn() {
  ...
  br i1 undef, ...
}

Please use the following instead:

define void @fn(i1 %cond) {
  ...
  br i1 %cond, ...
}

Please refer to the Undefined Behavior Manual for more information.

@topperc topperc requested a review from arsenm February 6, 2025 23:56
// Add an implicit read of the original source to silence the verifier
// in the cases where some of the smaller VRs we're copying from might be
// undef, caused by the fact that the original, larger source VR might not
// be fully initialized at the time this COPY happens.
Copy link
Contributor

Choose a reason for hiding this comment

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

At what time the COPY happens? Why doesn't InitUndef work at this time?

Copy link
Member Author

@mshockwave mshockwave Feb 7, 2025

Choose a reason for hiding this comment

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

The case I saw was created by register coalescer where it generates something like this:

%8.sub_vrm2_0:vrn32 = ...
%15.sub_vrm1_0_sub_vrm1_1_sub_vrm1_2_sub_vrm1_3_sub_vrm1_4:vrn3m2 = COPY %8.sub_vrm1_0_sub_vrm1_1_sub_vrm1_2_sub_vrm1_3_sub_vrm1_4

what happened was that the first instruction only initializes part of %8 and the second one COPY from another partially initialized (or partially undef) sub-register of %8. In this case we can't mark the source operand of COPY with undef. This COPY will eventually got broken down into smaller copies, in which one of those copies were copied from an undef register, hence the verifier error.

An alternative solution would be adding undef on source operand of the lowered, smaller copy instructions when needed. But that requires LiveIntervalsAnaysis.

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

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

MIR test would be helpful, it's a huge pain to get this right and you need to consider kill and undef flags

@mshockwave
Copy link
Member Author

MIR test would be helpful, it's a huge pain to get this right and you need to consider kill and undef flags

Done

Copy link
Contributor

@wangpc-pp wangpc-pp left a comment

Choose a reason for hiding this comment

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

LGTM. I don't see any concern.

@mshockwave mshockwave merged commit c40877d into llvm:main Feb 9, 2025
5 of 8 checks passed
@mshockwave mshockwave deleted the patch/riscv/interleave-fix branch February 9, 2025 00:25
name: copy
isSSA: false
noVRegs: true
liveins:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should set tracksRegLiveness

; CHECK-NEXT: PseudoRET implicit $v0
renamable $v20_v21_v22_v23_v24 = COPY renamable $v14_v15_v16_v17_v18, implicit $vtype
PseudoRET implicit $v0

Copy link
Contributor

Choose a reason for hiding this comment

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

Test undef and kill flags

Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
Somtimes when we're breaking up a large vector copy into several smaller
ones, not every single smaller source registers are initialized at the
time when the original COPY happens, and the verifier will not be
pleased when seeing the smaller copies reading from an undef register.
This patch is a workaround for the said issue by attaching an implicit
read of the source operand on the newly generated copies.

This is tested by llvm/test/CodeGen/RISCV/rvv/vector-interleave.ll which
would have crashed the compiler without this fix when
LLVM_EXPENSIVE_CHECK is enabled. Original context:
llvm#124825 (comment)

---------

Co-authored-by: Craig Topper <[email protected]>
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