Skip to content

Commit 8f59bef

Browse files
committed
[LV] Convert uniform-address scatters to scalar store when unmasked.
This patch optimizes vector scatters that have a uniform (single-scalar) address by replacing them with "extract-last-element + scalar store" when the scatter is unmasked. In all of these cases, at least one lane is guaranteed to execute in each vector iteration, so storing the last active element is sufficient. Implementation: - Add optimizeScatterWithUniformAddr(VPlan &), and invoke it from VPlanTransforms::optimize(). - Identify non-consecutive VPWidenStoreRecipe/VPWidenStoreEVLRecipe with uniform addresses. - Replace the scatter with VPInstruction::ExtractLastElement of the stored value and a VPReplicate (scalar) store. Notes: - The legacy cost model can scalarize a store if both the address and the value are uniform. In VPlan we materialize the stored value via ExtractLastElement, so only the address must be uniform. - Some of the loops won't be vectorized any sine no vector instructions will be generated. I plan to have a follow-up patch for convert uniform-address scatters to scalar store when the mask is header maks. This reqiures `extract-last-active-element` to get the correct value to store.
1 parent e57c745 commit 8f59bef

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,14 +1400,47 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
14001400
for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
14011401
vp_depth_first_shallow(Plan.getVectorLoopRegion()->getEntry()))) {
14021402
for (VPRecipeBase &R : make_early_inc_range(reverse(*VPBB))) {
1403-
if (!isa<VPWidenRecipe, VPWidenSelectRecipe, VPReplicateRecipe>(&R))
1403+
if (!isa<VPWidenRecipe, VPWidenSelectRecipe, VPReplicateRecipe,
1404+
VPWidenMemoryRecipe>(&R))
14041405
continue;
14051406
auto *RepR = dyn_cast<VPReplicateRecipe>(&R);
14061407
if (RepR && (RepR->isSingleScalar() || RepR->isPredicated()))
14071408
continue;
14081409

1409-
auto *RepOrWidenR = cast<VPSingleDefRecipe>(&R);
1410-
if (RepR && isa<StoreInst>(RepR->getUnderlyingInstr()) &&
1410+
// Convert scatters with a uniform address that is unmasked into an
1411+
// extract-last-element + scalar store.
1412+
// TODO: Add a profitability check comparing the cost of a scatter vs.
1413+
// extract + scalar store.
1414+
auto *WidenStoreR = dyn_cast<VPWidenMemoryRecipe>(&R);
1415+
if (WidenStoreR && vputils::isSingleScalar(WidenStoreR->getAddr()) &&
1416+
!WidenStoreR->isConsecutive() &&
1417+
isa<VPWidenStoreRecipe, VPWidenStoreEVLRecipe>(WidenStoreR)) {
1418+
assert(!WidenStoreR->isReverse() &&
1419+
"Not consecutive memory recipes shouldn't be reversed");
1420+
VPValue *Mask = WidenStoreR->getMask();
1421+
1422+
// Only convert the scatter to a scalar store if it is unmasked. or
1423+
// TODO: Support converting scatter masked by the header mask to scalar
1424+
// store.
1425+
if (Mask)
1426+
continue;
1427+
1428+
auto *Extract = new VPInstruction(VPInstruction::ExtractLastElement,
1429+
{WidenStoreR->getOperand(1)});
1430+
Extract->insertBefore(WidenStoreR);
1431+
1432+
// TODO: Sink the scalar store recipe to middle block if possible.
1433+
auto *ScalarStore = new VPReplicateRecipe(
1434+
&WidenStoreR->getIngredient(), {Extract, WidenStoreR->getAddr()},
1435+
true /*IsSingleScalar*/, nullptr /*Mask*/,
1436+
*WidenStoreR /*Metadata*/);
1437+
ScalarStore->insertBefore(WidenStoreR);
1438+
WidenStoreR->eraseFromParent();
1439+
continue;
1440+
}
1441+
1442+
auto *RepOrWidenR = dyn_cast<VPSingleDefRecipe>(&R);
1443+
if (RepR && RepOrWidenR && isa<StoreInst>(RepR->getUnderlyingInstr()) &&
14111444
vputils::isSingleScalar(RepR->getOperand(1))) {
14121445
auto *Clone = new VPReplicateRecipe(
14131446
RepOrWidenR->getUnderlyingInstr(), RepOrWidenR->operands(),
@@ -1427,7 +1460,7 @@ static void narrowToSingleScalarRecipes(VPlan &Plan) {
14271460
// Skip recipes that aren't single scalars or don't have only their
14281461
// scalar results used. In the latter case, we would introduce extra
14291462
// broadcasts.
1430-
if (!vputils::isSingleScalar(RepOrWidenR) ||
1463+
if (!RepOrWidenR || !vputils::isSingleScalar(RepOrWidenR) ||
14311464
!all_of(RepOrWidenR->users(), [RepOrWidenR](const VPUser *U) {
14321465
if (auto *Store = dyn_cast<VPWidenStoreRecipe>(U)) {
14331466
// VPWidenStore doesn't have users, and stores are always

llvm/test/Transforms/LoopVectorize/RISCV/narrow-scatter-to-scalar-store.ll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ define void @truncate_i16_to_i8_cse(ptr noalias %src, ptr noalias %dst) {
66
; CHECK-NEXT: [[ENTRY:.*:]]
77
; CHECK-NEXT: br label %[[VECTOR_PH:.*]]
88
; CHECK: [[VECTOR_PH]]:
9-
; CHECK-NEXT: [[BROADCAST_SPLATINSERT1:%.*]] = insertelement <2 x ptr> poison, ptr [[DST]], i64 0
10-
; CHECK-NEXT: [[BROADCAST_SPLAT1:%.*]] = shufflevector <2 x ptr> [[BROADCAST_SPLATINSERT1]], <2 x ptr> poison, <2 x i32> zeroinitializer
119
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
1210
; CHECK: [[VECTOR_BODY]]:
1311
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
1412
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SRC]], align 2
1513
; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <2 x i16> poison, i16 [[TMP0]], i64 0
1614
; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <2 x i16> [[BROADCAST_SPLATINSERT]], <2 x i16> poison, <2 x i32> zeroinitializer
1715
; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i16> [[BROADCAST_SPLAT]] to <2 x i8>
18-
; CHECK-NEXT: call void @llvm.masked.scatter.v2i8.v2p0(<2 x i8> [[TMP1]], <2 x ptr> align 1 zeroinitializer, <2 x i1> splat (i1 true))
19-
; CHECK-NEXT: call void @llvm.masked.scatter.v2i8.v2p0(<2 x i8> [[TMP1]], <2 x ptr> align 1 [[BROADCAST_SPLAT1]], <2 x i1> splat (i1 true))
16+
; CHECK-NEXT: [[TMP2:%.*]] = extractelement <2 x i8> [[TMP1]], i32 1
17+
; CHECK-NEXT: store i8 [[TMP2]], ptr null, align 1
18+
; CHECK-NEXT: store i8 [[TMP2]], ptr [[DST]], align 1
2019
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
2120
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[INDEX_NEXT]], 4294967296
2221
; CHECK-NEXT: br i1 [[TMP3]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]

0 commit comments

Comments
 (0)