[llvm-branch-commits] [llvm] PPC: Add MIR examples for missed extsw+word-load fold on subregister input (PR #211106)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Sep 20 02:14:08 PDT 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/211106

>From d91119e6000c49e7373a999ae11038c0d00f1c9c Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 21 Jul 2026 19:28:38 +0200
Subject: [PATCH] PPC: Add MIR examples for missed extsw+word-load fold on
 subregister input

A gprc LWZ/LWZX feeding EXTSW_32_64 folds into a sign-extending LWA/LWAX
load. The equivalent 64-bit zero-extending word loads (LWZ8/LWZX8) whose
sub_32 feeds EXTSW_32_64 are not folded, leaving a redundant lwz+extsw
(or lwzx+extsw) pair. Add MIR examples documenting the missed fold.

Co-authored-by: Claude (Claude Opus 4.8, claude-opus-4-8) <noreply at anthropic.com>
---
 .../peephole-elim-extsw-subreg-input.mir      | 158 +++++++++++++++++-
 1 file changed, 152 insertions(+), 6 deletions(-)

diff --git a/llvm/test/CodeGen/PowerPC/peephole-elim-extsw-subreg-input.mir b/llvm/test/CodeGen/PowerPC/peephole-elim-extsw-subreg-input.mir
index 86caff368f3f29..1c7d3c1f87ca74 100644
--- a/llvm/test/CodeGen/PowerPC/peephole-elim-extsw-subreg-input.mir
+++ b/llvm/test/CodeGen/PowerPC/peephole-elim-extsw-subreg-input.mir
@@ -1,12 +1,9 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
 # RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -mcpu=pwr9 -run-pass=ppc-mi-peepholes -ppc-eliminate-signext -verify-machineinstrs -o - %s | FileCheck %s
 
-# When the EXTSW_32_64 input reads the sub_32 subregister of a 64-bit register
-# that is already sign-extended (here RLDICL with MB >= 33), the high 32 bits of
-# that register already hold the sign extension of the low word, so the result
-# of the sign-extend is the whole 64-bit register. Eliminate the EXTSW by
-# copying that register directly rather than building an INSERT_SUBREG that would
-# use the full register where a 32-bit register is required.
+# EXTSW_32_64 reading the sub_32 of an already-sign-extended 64-bit register
+# (here RLDICL with MB >= 33) is redundant, so it is replaced by a copy of the
+# full register.
 
 ---
 name:            extsw_elim_subreg_input
@@ -28,3 +25,152 @@ body:             |
     $x3 = COPY %2
     BLR8 implicit $lr8, implicit $rm, implicit $x3
 ...
+
+# FIXME: A 64-bit zero-extending word load (LWZ8/LWZX8) feeding an EXTSW that
+# reads its sub_32 could fold into a sign-extending load, the same way a gprc
+# LWZ/LWZX + EXTSW folds. The load-folding path only matches the 32-bit
+# LWZ/LWZX defs, so these subregister cases are left as a separate load + extsw.
+---
+name:            extsw_lwz8_subreg_input
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3
+    ; CHECK-LABEL: name: extsw_lwz8_subreg_input
+    ; CHECK: liveins: $x3
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[LWZ8_:%[0-9]+]]:g8rc = LWZ8 0, killed [[COPY]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_64_:%[0-9]+]]:g8rc = EXTSW_32_64 killed [[LWZ8_]].sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[EXTSW_32_64_]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %1:g8rc = LWZ8 0, %0 :: (load (s32))
+    %2:g8rc = EXTSW_32_64 %1.sub_32
+    $x3 = COPY %2
+    BLR8 implicit $lr8, implicit $rm, implicit $x3
+...
+
+# Indexed load (X-form, no alignment requirement).
+---
+name:            extsw_lwzx8_subreg_input
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3, $x4
+    ; CHECK-LABEL: name: extsw_lwzx8_subreg_input
+    ; CHECK: liveins: $x3, $x4
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:g8rc = COPY killed $x4
+    ; CHECK-NEXT: [[LWZX8_:%[0-9]+]]:g8rc = LWZX8 killed [[COPY]], killed [[COPY1]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_64_:%[0-9]+]]:g8rc = EXTSW_32_64 killed [[LWZX8_]].sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[EXTSW_32_64_]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %3:g8rc = COPY $x4
+    %1:g8rc = LWZX8 %0, %3 :: (load (s32))
+    %2:g8rc = EXTSW_32_64 %1.sub_32
+    $x3 = COPY %2
+    BLR8 implicit $lr8, implicit $rm, implicit $x3
+...
+
+# 32-bit result EXTSW_32.
+---
+name:            extsw32_lwz8_subreg_input
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3
+    ; CHECK-LABEL: name: extsw32_lwz8_subreg_input
+    ; CHECK: liveins: $x3
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[LWZ8_:%[0-9]+]]:g8rc = LWZ8 0, killed [[COPY]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_:%[0-9]+]]:gprc = EXTSW_32 killed [[LWZ8_]].sub_32
+    ; CHECK-NEXT: [[DEF:%[0-9]+]]:g8rc = IMPLICIT_DEF
+    ; CHECK-NEXT: [[INSERT_SUBREG:%[0-9]+]]:g8rc = INSERT_SUBREG killed [[DEF]], killed [[EXTSW_32_]], %subreg.sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[INSERT_SUBREG]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %1:g8rc = LWZ8 0, %0 :: (load (s32))
+    %2:gprc = EXTSW_32 %1.sub_32
+    %3:g8rc = IMPLICIT_DEF
+    %4:g8rc = INSERT_SUBREG %3, %2, %subreg.sub_32
+    $x3 = COPY %4
+    BLR8 implicit $lr8, implicit $rm, implicit $x3
+...
+
+# 32-bit result EXTSW_32, indexed load.
+---
+name:            extsw32_lwzx8_subreg_input
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3, $x4
+    ; CHECK-LABEL: name: extsw32_lwzx8_subreg_input
+    ; CHECK: liveins: $x3, $x4
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:g8rc = COPY killed $x4
+    ; CHECK-NEXT: [[LWZX8_:%[0-9]+]]:g8rc = LWZX8 killed [[COPY]], killed [[COPY1]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_:%[0-9]+]]:gprc = EXTSW_32 killed [[LWZX8_]].sub_32
+    ; CHECK-NEXT: [[DEF:%[0-9]+]]:g8rc = IMPLICIT_DEF
+    ; CHECK-NEXT: [[INSERT_SUBREG:%[0-9]+]]:g8rc = INSERT_SUBREG killed [[DEF]], killed [[EXTSW_32_]], %subreg.sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[INSERT_SUBREG]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %5:g8rc = COPY $x4
+    %1:g8rc = LWZX8 %0, %5 :: (load (s32))
+    %2:gprc = EXTSW_32 %1.sub_32
+    %3:g8rc = IMPLICIT_DEF
+    %4:g8rc = INSERT_SUBREG %3, %2, %subreg.sub_32
+    $x3 = COPY %4
+    BLR8 implicit $lr8, implicit $rm, implicit $x3
+...
+
+# Negative test: no fold, the D-form displacement is not 4-byte aligned.
+---
+name:            extsw_lwz8_subreg_input_unaligned
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3
+    ; CHECK-LABEL: name: extsw_lwz8_subreg_input_unaligned
+    ; CHECK: liveins: $x3
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[LWZ8_:%[0-9]+]]:g8rc = LWZ8 2, killed [[COPY]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_64_:%[0-9]+]]:g8rc = EXTSW_32_64 killed [[LWZ8_]].sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[EXTSW_32_64_]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %1:g8rc = LWZ8 2, %0 :: (load (s32))
+    %2:g8rc = EXTSW_32_64 %1.sub_32
+    $x3 = COPY %2
+    BLR8 implicit $lr8, implicit $rm, implicit $x3
+...
+
+# Negative test: no fold, the load has another use besides the sign-extension.
+---
+name:            extsw_lwz8_subreg_input_multiuse
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $x3
+    ; CHECK-LABEL: name: extsw_lwz8_subreg_input_multiuse
+    ; CHECK: liveins: $x3
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:g8rc_and_g8rc_nox0 = COPY killed $x3
+    ; CHECK-NEXT: [[LWZ8_:%[0-9]+]]:g8rc = LWZ8 0, killed [[COPY]] :: (load (s32))
+    ; CHECK-NEXT: [[EXTSW_32_64_:%[0-9]+]]:g8rc = EXTSW_32_64 [[LWZ8_]].sub_32
+    ; CHECK-NEXT: $x3 = COPY killed [[EXTSW_32_64_]]
+    ; CHECK-NEXT: $x4 = COPY killed [[LWZ8_]]
+    ; CHECK-NEXT: BLR8 implicit $lr8, implicit $rm, implicit killed $x3, implicit killed $x4
+    %0:g8rc_and_g8rc_nox0 = COPY $x3
+    %1:g8rc = LWZ8 0, %0 :: (load (s32))
+    %2:g8rc = EXTSW_32_64 %1.sub_32
+    $x3 = COPY %2
+    $x4 = COPY %1
+    BLR8 implicit $lr8, implicit $rm, implicit $x3, implicit $x4
+...



More information about the llvm-branch-commits mailing list