Skip to content

[AArch64][SVE] Support copy of PPR2 register class in copyPhysReg - #216303

Open
kmclaughlin-arm wants to merge 2 commits into
llvm:mainfrom
kmclaughlin-arm:copy-ppr2
Open

[AArch64][SVE] Support copy of PPR2 register class in copyPhysReg#216303
kmclaughlin-arm wants to merge 2 commits into
llvm:mainfrom
kmclaughlin-arm:copy-ppr2

Conversation

@kmclaughlin-arm

Copy link
Copy Markdown
Contributor

This fixes the following crash, which was observed after landing #209484:
https://clang.godbolt.org/z/YG1r63oT3

This fixes the following crash, which was observed after landing llvm#209484:
llvm#209484
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-aarch64

Author: Kerry McLaughlin (kmclaughlin-arm)

Changes

This fixes the following crash, which was observed after landing #209484:
https://clang.godbolt.org/z/YG1r63oT3


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

2 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64InstrInfo.cpp (+12)
  • (added) llvm/test/CodeGen/AArch64/sve-copy-pprpair.mir (+24)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index 9e128651598f3..1502724ffa713 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -5771,6 +5771,8 @@ void AArch64InstrInfo::copyPhysRegTuple(MachineBasicBlock &MBB,
     const MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(Opcode));
     AddSubReg(MIB, DestReg, Indices[SubReg], RegState::Define, TRI);
     AddSubReg(MIB, SrcReg, Indices[SubReg], {}, TRI);
+    if (Opcode == AArch64::ORR_PPzPP)
+      AddSubReg(MIB, SrcReg, Indices[SubReg], {}, TRI);
     AddSubReg(MIB, SrcReg, Indices[SubReg], getKillRegState(KillSrc), TRI);
   }
 }
@@ -5991,6 +5993,16 @@ void AArch64InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
     return;
   }
 
+  if (AArch64::PPR2RegClass.contains(DestReg) &&
+      AArch64::PPR2RegClass.contains(SrcReg)) {
+    assert(Subtarget.isSVEorStreamingSVEAvailable() &&
+           "Unexpected SVE predicate register.");
+    static const unsigned Indices[] = {AArch64::psub0, AArch64::psub1};
+    copyPhysRegTuple(MBB, I, DL, DestReg, SrcReg, KillSrc, AArch64::ORR_PPzPP,
+                     Indices);
+    return;
+  }
+
   // Copy a Z register by ORRing with itself.
   if (AArch64::ZPRRegClass.contains(DestReg) &&
       AArch64::ZPRRegClass.contains(SrcReg)) {
diff --git a/llvm/test/CodeGen/AArch64/sve-copy-pprpair.mir b/llvm/test/CodeGen/AArch64/sve-copy-pprpair.mir
new file mode 100644
index 0000000000000..336886f699b49
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-copy-pprpair.mir
@@ -0,0 +1,24 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve -run-pass=postrapseudos -simplify-mir -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            copy_ppr2
+alignment:       4
+tracksRegLiveness: true
+liveins:
+  - { reg: '$p0_p1' }
+frameInfo:
+  maxCallFrameSize: 0
+body:             |
+  bb.0:
+    liveins: $p0_p1
+    ; CHECK-LABEL: name: copy_ppr2
+    ; CHECK: liveins: $p0_p1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $p2 = ORR_PPzPP $p0, $p0, killed $p0
+    ; CHECK-NEXT: $p3 = ORR_PPzPP $p1, $p1, killed $p1
+    ; CHECK-NEXT: RET_ReallyLR
+    $p2_p3 = COPY killed renamable $p0_p1
+    RET_ReallyLR
+
+...

const MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(Opcode));
AddSubReg(MIB, DestReg, Indices[SubReg], RegState::Define, TRI);
AddSubReg(MIB, SrcReg, Indices[SubReg], {}, TRI);
if (Opcode == AArch64::ORR_PPzPP)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need to check the opcode here? I was just looking at the ZPR2RegClass case, also handled in copyPhysReg, and we don't seem to need opcode checking in that case. I assume there is a problem with getKillRegState(KillSrc)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, we still call AddSubReg(MIB, SrcReg, Indices[SubReg], getKillRegState(KillSrc), TRI); anyway even if the opcode is AArch64::ORR_PPzPP. So looks like we call it twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The ORR_PPzPP instruction takes an extra operand because it has an additional governing predicate operand, which the ORR_ZZZ instruction used by ZPR classes do not (this now matches how we handle copies of PPRRegClass above).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Another alternative is to just call copyPhysReg directly at this point, as that function already correctly handles the scalar copies. (and that also simplifies the calls to copyPhysRegTuple)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks @sdesmalen-arm , that does make things a bit simpler here.

Calling copyPhysReg directly has caused one of the existing tests to change (arm64-copy-tuple.ll). This is because for some tuples (e.g. DDD) we are no longer passing in a specific opcode and copyPhysReg can choose to use something else (e.g. FMOV) instead for the subregister class, such as FPR64RegClass. I think the changes are still correct, so I've included this in the latest commit.

Comment thread llvm/lib/Target/AArch64/AArch64InstrInfo.cpp Outdated
; CHECK-NEXT: $p2 = ORR_PPzPP $p0, $p0, killed $p0
; CHECK-NEXT: $p3 = ORR_PPzPP $p1, $p1, killed $p1
; CHECK-NEXT: RET_ReallyLR
$p2_p3 = COPY killed renamable $p0_p1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: maybe also add a test where there is some overlap, e.g. $p1_p2 = COPY killed $p0_p1
(and the test for the case I mentioned above, $p0_p1 = COPY killed $p15_p0)

assert(Subtarget.isSVEorStreamingSVEAvailable() &&
"Unexpected SVE predicate register.");
static const unsigned Indices[] = {AArch64::psub0, AArch64::psub1};
copyPhysRegTuple(MBB, I, DL, DestReg, SrcReg, KillSrc, AArch64::ORR_PPzPP,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

forwardCopyWillClobberTuple considers 32 the maximum number of registers, but for predicates it is 16, so this goes wrong when copying $p15_p0 to $p0_p1, for example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've changed forwardCopyWillClobberTuple to consider 16 the max for predicates.

- Changed forwardCopyWillClobberTuple to consider number of predicate registers
- Added tests with overlap & test for forwardCopyWillClobberTuple
// easily obtainable with a mask.
return ((DestReg - SrcReg) & 0x1f) < NumRegs;
unsigned MaxRegs = IsPred ? 0xf : 0x1f;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this be generalised to take the max regs from the number of registers in the register class, rather than hardcoding it? (e.g. using the largest register class for src/dst)

ArrayRef<unsigned> Indices) const {
assert(Subtarget.hasNEON() && "Unexpected register copy without NEON");
const TargetRegisterInfo *TRI = &getRegisterInfo();
uint16_t DestEncoding = TRI->getEncodingValue(DestReg);
uint16_t SrcEncoding = TRI->getEncodingValue(SrcReg);
unsigned NumRegs = Indices.size();
bool IsPred = AArch64::PPR2RegClass.contains(DestReg);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's probably better to check that the register class of the sub-register is a PPR, otherwise, the current mechanism doesn't support PPR2Mul2 or any other tuple register (classes) we may want to add in the future.

AddSubReg(MIB, SrcReg, Indices[SubReg], getKillRegState(KillSrc), TRI);
MCRegister DestSubReg = TRI->getSubReg(DestReg, Indices[SubReg]);
MCRegister SrcSubReg = TRI->getSubReg(SrcReg, Indices[SubReg]);
copyPhysReg(MBB, I, DL, DestSubReg, SrcSubReg, KillSrc);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I forgot to mention, copyPhysReg increments ++NumCopyInstrs for each COPY, which would be different now. You could consider moving most of the code to a copyPhysRegImpl that doesn't do the increment.

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.

3 participants