Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions llvm/lib/Target/AMDGPU/AMDGPULateCodeGenPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,41 @@ class LiveRegOptimizer {
return LK.first != TargetLoweringBase::TypeLegal;
}

bool isOpLegal(Instruction *I) { return isa<StoreInst, IntrinsicInst>(I); }
bool isOpLegal(Instruction *I) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
bool isOpLegal(Instruction *I) {
bool isOpLegal(const Instruction *I) {

if (dyn_cast<IntrinsicInst>(I))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (dyn_cast<IntrinsicInst>(I))
if (isa<IntrinsicInst>(I))

But probably should restrict this to target intrinsics

return true; // FIXME: narrow to known native intrinsics
// (DOT/MFMA/tbuffer) or use TTI cost.

// Any store is a profitable sink (prevents flip-flopping)
if (isa<StoreInst>(I))
return true;

if (auto *BO = dyn_cast<BinaryOperator>(I)) {
if (auto *VTy = dyn_cast<VectorType>(BO->getType())) {
Type *Elt = VTy->getElementType();
// Treat small-int vector binops as profitable when SDWA is available.
Copy link
Contributor

Choose a reason for hiding this comment

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

duplicate comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you! Fixed.

// We explicitly gate to 8/16-bit to avoid i1 vectors and keep behavior
// tight.
// Require SDWA for both i8 and i16, and keep vectors within 32 bits.
std::optional<unsigned> Bits = VTy->getPrimitiveSizeInBits();
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're so specifically checking these types, might as well keep this in terms of element type + element count instead of checking the full width of the vector

if (ST.hasSDWA() && Bits && *Bits <= 32 &&
(Elt->isIntegerTy(8) || Elt->isIntegerTy(16))) {
switch (BO->getOpcode()) {
case Instruction::Add:
case Instruction::Sub:
case Instruction::And:
case Instruction::Or:
case Instruction::Xor:
return true;
default:
break;
}
}
}
}

return false;
}

bool isCoercionProfitable(Instruction *II) {
SmallPtrSet<Instruction *, 4> CVisited;
Expand All @@ -150,7 +184,12 @@ class LiveRegOptimizer {
if (!CVisited.insert(CII).second)
continue;

if (CII->getParent() == II->getParent() && !IsLookThru(II))
// Allow same-BB non-lookthrough users when the def is a PHI:
// loop headers frequently consume the carried value in the header block
// (e.g. byte-wise vector binops). We *do* want to coerce across the
// backedge in that common case to enable packed i32 + SDWA lowering.
if (CII->getParent() == II->getParent() && !IsLookThru(CII) &&
!isa<PHINode>(II))
Copy link
Contributor

Choose a reason for hiding this comment

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

The addition of phi here feels like a separate change from the above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You’re right—there are two separate changes:

  • The II → CII fix is a correctness bug: we must examine the user when pruning same-BB paths, otherwise the walk can terminate prematurely.
  • The PHI exception is a small policy tweak: loop headers commonly consume the carried value in the header block via non-lookthrough ops (e.g., byte-wise vector binops). Without allowing that same-BB non-lookthrough use for PHI, the walk never reaches the profitable sink—even with a better cost model—so the regression remains.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you split this into a separate PR? It's hard to see that all the cases are adequately tested when they're together

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can. Given this PR will be for isOpLegal/isProfitableSink improvement, what would you like to see separated? II->CII fix? PHI exception? Or maybe two separate PRs, one for each?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@arsenm, I instrumented the TTI queries on gfx90a: add <4 x i8> comes out at cost 4 with TCK_SizeAndLatency (and likewise for getArithmeticInstrCost), which is below the previous profitability threshold (8). So switching to TTI-only would reintroduce the regression. I propose to keep the very narrow SDWA safety-net for v4i8/v2i16 (≤32b) here and look at improving AMDGPU TTI separately if needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, TTI is a mess and we need to go fix all the costs, especially for illegal operations. In particular the costs for memory instructions are way too low

continue;

if (isOpLegal(CII))
Expand Down
63 changes: 63 additions & 0 deletions llvm/test/CodeGen/AMDGPU/lro-coerce-v4i8-phi-loop.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
; REQUIRES: amdgpu-registered-target
; RUN: opt -S -passes=amdgpu-late-codegenprepare \
; RUN: -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a %s | FileCheck %s

; Purpose:
; - Input has a loop-carried PHI of type <4 x i8> and byte-wise adds in the
; loop header (same basic block as the PHI).
; - After amdgpu-late-codegenprepare, the PHI must be coerced to i32 across
; the backedge, and a single dominating "bitcast i32 -> <4 x i8>" must be
; placed in the header (enabling SDWA-friendly lowering later).
;
; What we check:
; - PHI is i32 (no loop-carried <4 x i8> PHI remains).
; - A header-local bitcast i32 -> <4 x i8> exists and feeds the vector add.
; - The loopexit produces a bitcast <4 x i8> -> i32 for the backedge.

define amdgpu_kernel void @lro_coerce_v4i8_phi(ptr nocapture %p, i32 %n) {
entry:
br label %loop

loop:
; Loop index
%i = phi i32 [ 0, %entry ], [ %i.next, %loop ]

; Loop-carried accumulator in vector-of-bytes form (problematic on input).
%acc = phi <4 x i8> [ zeroinitializer, %entry ], [ %acc.next, %loop ]

; Make up four i8 values derived from %i to avoid memory noise.
%i0 = trunc i32 %i to i8
%i1i = add i32 %i, 1
%i1 = trunc i32 %i1i to i8
%i2i = add i32 %i, 2
%i2 = trunc i32 %i2i to i8
%i3i = add i32 %i, 3
%i3 = trunc i32 %i3i to i8

; Pack them into <4 x i8>.
%v01 = insertelement <4 x i8> zeroinitializer, i8 %i0, i32 0
%v02 = insertelement <4 x i8> %v01, i8 %i1, i32 1
%v03 = insertelement <4 x i8> %v02, i8 %i2, i32 2
%v = insertelement <4 x i8> %v03, i8 %i3, i32 3

; Byte-wise add in the same block as the PHI (this must make coercion profitable).
%acc.next = add <4 x i8> %acc, %v

; Loop control.
%i.next = add i32 %i, 4
%cond = icmp slt i32 %i.next, %n
br i1 %cond, label %loop, label %exit

exit:
ret void
}

; CHECK-LABEL: define amdgpu_kernel void @lro_coerce_v4i8_phi(
; CHECK: loop:
; CHECK: %i = phi i32
; CHECK-NOT: phi <4 x i8>
; CHECK: %[[ACCI32:[^ ]+]] = phi i32
; CHECK-NEXT: %[[HDRCAST:[^ ]+]] = bitcast i32 %[[ACCI32]] to <4 x i8>
; CHECK: add <4 x i8> %[[HDRCAST]],
; CHECK: br i1