[llvm] [SystemZ] Fold i16/i32/i64 logical RMW operations into memory operands in TableGen (PR #192802)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 07:26:17 PDT 2026


https://github.com/anoopkg6 updated https://github.com/llvm/llvm-project/pull/192802

>From b64dc7ae72c913079981a09985ea3b81620ed49a Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Sat, 18 Apr 2026 17:52:12 +0200
Subject: [PATCH 1/7] [SystemZ] Fold i16/i32/i64 logical RMW operations into
 memory operands    - Extend tryFoldLoadStoreIntoMemOperand to support folding
 of AND, OR,      and XOR operations for wider types.    - Handles both 12-bit
 (NI/OI/XI) and 20-bit (NIY/OIY/XIY) displacements.    - Implements big-endian
 adjustment to target the least significant byte.    - Restricts folding to
 8-bit immediates as required by the instructions.

---
 .../Target/SystemZ/SystemZISelDAGToDAG.cpp    | 100 +++-
 llvm/test/CodeGen/SystemZ/and-05-a.ll         | 563 ++++++++++++++++++
 llvm/test/CodeGen/SystemZ/or-05-a.ll          | 536 +++++++++++++++++
 llvm/test/CodeGen/SystemZ/xor-05-a.ll         | 536 +++++++++++++++++
 4 files changed, 1716 insertions(+), 19 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/and-05-a.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/or-05-a.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/xor-05-a.ll

diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index bb148c2dc2ebd..16b1339f3b9a9 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -1328,17 +1328,21 @@ static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode,
   if (!StoredVal.getNode()->hasNUsesOfValue(1, 0))
     return false;
 
-  // Is the store non-extending and non-indexed?
-  if (!ISD::isNormalStore(StoreNode) || StoreNode->isNonTemporal())
+  // Allow truncating stores for sub-word types but still reject
+  // complex addressing (indexed) and special memory hints (non-temporal).
+  if (StoreNode->isIndexed() || StoreNode->isNonTemporal())
     return false;
 
   SDValue Load = StoredVal->getOperand(0);
-  // Is the stored value a non-extending and non-indexed load?
-  if (!ISD::isNormalLoad(Load.getNode()))
-    return false;
 
-  // Return LoadNode by reference.
-  LoadNode = cast<LoadSDNode>(Load);
+  // Peeking through ISD::ANY_EXTEND and allowing non-normal
+  // (Extending/Truncating) Loads/Stores.
+  if (Load.getOpcode() == ISD::ANY_EXTEND)
+    Load = Load.getOperand(0);
+  // Allow truncating stores and extending loads for sub-word logical ops.
+  LoadNode = dyn_cast<LoadSDNode>(Load);
+  if (!LoadNode || LoadNode->isIndexed())
+    return false;
 
   // Is store the only read of the loaded value?
   if (!Load.hasOneUse())
@@ -1415,7 +1419,7 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
   // and opcode we can handle. Note that this must match the code below that
   // actually lowers the opcodes.
   EVT MemVT = StoreNode->getMemoryVT();
-  unsigned NewOpc = 0;
+  std::pair<unsigned, unsigned> LOpcs;
   bool NegateOperand = false;
   switch (Opc) {
   default:
@@ -1425,9 +1429,9 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
     [[fallthrough]];
   case SystemZISD::SADDO:
     if (MemVT == MVT::i32)
-      NewOpc = SystemZ::ASI;
+      LOpcs.first = SystemZ::ASI;
     else if (MemVT == MVT::i64)
-      NewOpc = SystemZ::AGSI;
+      LOpcs.first = SystemZ::AGSI;
     else
       return false;
     break;
@@ -1436,12 +1440,21 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
     [[fallthrough]];
   case SystemZISD::UADDO:
     if (MemVT == MVT::i32)
-      NewOpc = SystemZ::ALSI;
+      LOpcs.first = SystemZ::ALSI;
     else if (MemVT == MVT::i64)
-      NewOpc = SystemZ::ALGSI;
+      LOpcs.first = SystemZ::ALGSI;
     else
       return false;
     break;
+  case ISD::AND:
+    LOpcs = {SystemZ::NI, SystemZ::NIY};
+    break;
+  case ISD::OR:
+    LOpcs = {SystemZ::OI, SystemZ::OIY};
+    break;
+  case ISD::XOR:
+    LOpcs = {SystemZ::XI, SystemZ::XIY};
+    break;
   }
 
   LoadSDNode *LoadNode = nullptr;
@@ -1457,22 +1470,71 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
   auto OperandV = OperandC->getAPIntValue();
   if (NegateOperand)
     OperandV = -OperandV;
-  if (OperandV.getSignificantBits() > 8)
-    return false;
-  Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
+
+  bool IsLogicalByteOp = (LOpcs.second != 0);
+  unsigned NewOpc = 0;
+  if (!IsLogicalByteOp) {
+    if (OperandV.getSignificantBits() > 8)
+      return false;
+    NewOpc = LOpcs.first;
+    Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
+  } else {
+    if (OperandV.getActiveBits() > 8)
+      return false;
+    // Fix for logical byte ops (XI/Y, NI/Y, OI/Y).
+    // Ensure the APInt width matches the requested MVT::i32 width
+    // exactly to satisfy SelectionDAG assertions.
+    Operand = CurDAG->getTargetConstant(OperandV.zextOrTrunc(32), DL, MVT::i32);
+  }
 
   SDValue Base, Disp;
   if (!selectBDAddr20Only(StoreNode->getBasePtr(), Base, Disp))
     return false;
 
+  // Handle Big-Endian offset for any integer width (i16, i32, i64).
+  if (IsLogicalByteOp) {
+    auto *DispC = dyn_cast<ConstantSDNode>(Disp);
+    if (!DispC)
+      return false;
+
+    int64_t NewDisp = DispC->getSExtValue();
+    // Calculate offset to the least significant byte.
+    if (MemVT.getStoreSize() > 1)
+      NewDisp += (MemVT.getStoreSize() - 1);
+    // Select the opcode based on the final displacement.
+    if (isUInt<12>(NewDisp)) {
+      NewOpc = LOpcs.first;
+    } else if (isInt<20>(NewDisp)) {
+      NewOpc = LOpcs.second;
+    } else {
+      return false;
+    }
+    Disp = CurDAG->getTargetConstant(NewDisp, DL, DispC->getValueType(0));
+  }
+  // Arithmetic ops like ASI/AGSI produce a result/CC (i32) and a Chain.
+  // Logical ops like XI/NI/OI produce just a Chain (MVT::Other).
+  SmallVector<EVT, 2> RetVTs;
+  // For ASI/AGSI/ALSI/ALGSI.
+  if (!IsLogicalByteOp)
+    RetVTs.push_back(MVT::i32);
+  // For the Chain.
+  RetVTs.push_back(MVT::Other);
+
   SDValue Ops[] = { Base, Disp, Operand, InputChain };
-  MachineSDNode *Result =
-    CurDAG->getMachineNode(NewOpc, DL, MVT::i32, MVT::Other, Ops);
+  MachineSDNode *Result = CurDAG->getMachineNode(NewOpc, DL, RetVTs, Ops);
+
   CurDAG->setNodeMemRefs(
       Result, {StoreNode->getMemOperand(), LoadNode->getMemOperand()});
 
-  ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
-  ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
+  if (IsLogicalByteOp) {
+    // XI/NI/OI only produce a Chain at index 0.
+    ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 0));
+  } else {
+    // ASI/AGSI produce CC/Result at index 0 and Chain at index 1.
+    ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
+    ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
+  }
+
   CurDAG->RemoveDeadNode(Node);
   return true;
 }
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
new file mode 100644
index 0000000000000..d8f6914ebf95a
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/and-05-a.ll
@@ -0,0 +1,563 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; and-05.ll tests the multiclass RMWIByte for i8 types. 
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
+;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
+;      legalization in the address matcher before folding can occur.
+;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
+;      be reached via a starting GEP offset of 524287, as the Big-Endian 
+;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+;    Negative constants fail to fold for multi-byte types because they are
+;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @and_i16_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i16_ni_low_unsigned:
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @and_i32_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_low_unsigned:
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @and_i64_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i64_ni_low_unsigned:
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @and_i32_ni_boundary_128(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_boundary_128:
+; CHECK: ni 3(%r2), 128
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 128
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
+define void @and_i32_ni_max_byte_minus_1(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_max_byte_minus_1:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 254
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+; Phase ordering, DAG combiner identifies 'and 255' on 32-bit load as
+; zero-extending byte load (llc).
+define void @and_i32_ni_max_byte(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_max_byte:
+; CHECK: llc [[REG:%r[0-9]+]], 3(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 255
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @and_i16_neg_2(ptr %src) {
+; CHECK-LABEL: and_i16_neg_2:
+; CHECK-NOT: ni {{.*}}, 254
+; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: nilf [[REG]], 65534
+; CHECK: sth  [[REG]], 0(%r2)
+; CHECK: br   %r14
+  %val = load i16, ptr %src
+  %and = and i16 %val, -2
+  store i16 %and, ptr %src
+  ret void
+}
+
+define void @and_i32_neg_2(ptr %src) {
+; CHECK-LABEL: and_i32_neg_2:
+; CHECK-NOT:niy {{.*}}, 254
+; CHECK: n [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %src
+  %and = and i32 %val, -2
+  store i32 %and, ptr %src
+  ret void
+}
+
+; DAG Combiner already combines it into a single And Memory (ng) instruction.
+define void @and_i64_neg_2(ptr %src) {
+; CHECK-LABEL: and_i64_neg_2:
+; CHECK: ng %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %src
+  %and = and i64 %val, -2
+  store i64 %and, ptr %src
+  ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to ni/niy.
+define void @and_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: and_i32_too_wide:
+; CHECK-NOT: ni{{y?}} {{.*}}, 256
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 256
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @and_i32_niy_boundary_128(ptr %src) {
+; CHECK-LABEL: and_i32_niy_boundary_128:
+; CHECK: niy 4096(%r2), 128
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 128
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
+define void @and_i32_niy_max_byte_minus_1(ptr %src) {
+; CHECK-LABEL: and_i32_niy_max_byte_minus_1:
+; CHECK: niy 4096(%r2), 254
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 254
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+; DAG Combiner transforms the 4-byte load into an 8-bit load logical (llc).
+define void @and_i32_niy_max_byte(ptr %src) {
+; CHECK-LABEL: and_i32_niy_max_byte:
+; CHECK: llc [[REG:%r[0-9]+]], 4096(%r2)
+; CHECK: st  [[REG]], 4093(%r2)
+; CHECK: br  %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %xor = and i32 %val, 255
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
+define void @and_i16_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i16_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
+define void @and_i32_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i32_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
+define void @and_i64_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i64_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @and_i16_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @and_i32_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64  transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @and_i64_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @and_i16_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524286
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @and_i32_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524284
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @and_i64_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524280
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i16_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i32_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i64_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @and_i16_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @and_i32_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @and_i64_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i16_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_min_disp:
+; CHECK:niy -524287(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i32_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_min_disp:
+; CHECK:niy -524285(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i64_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_min_disp:
+; CHECK:niy -524281(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i16_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i32_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @and_i16_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i16_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4094
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @and_i32_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i32_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4092
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @and_i64_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i64_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4088
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @and_i16_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i16_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @and_i32_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i32_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4093
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @and_i64_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i64_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4089
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: ni {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load volatile i32, ptr %ptr
+  %and = and i32 %val, 1
+  store volatile i32 %and, ptr %ptr
+  ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: nilf {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret i32 %val
+}
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
new file mode 100644
index 0000000000000..b345da8ed8293
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/or-05-a.ll
@@ -0,0 +1,536 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; or-05.ll tests the multiclass RMWIByte for i8 types. 
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
+;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
+;      legalization in the address matcher before folding can occur.
+;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
+;      be reached via a starting GEP offset of 524287, as the Big-Endian 
+;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+;    Negative constants fail to fold for multi-byte types because they are
+;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @or_i16_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i16_oi_low_unsigned:
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @or_i32_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_low_unsigned:
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @or_i64_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i64_oi_low_unsigned:
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @or_i32_oi_boundary_128(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_boundary_128:
+; CHECK: oi 3(%r2), 128
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 128
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @or_i32_oi_max_byte(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_max_byte:
+; CHECK: oi 3(%r2), 255
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 255
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @or_i16_neg_2(ptr %src) {
+; CHECK-LABEL: or_i16_neg_2:
+; CHECK-NOT: oi {{.*}}, 254
+; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oill [[REG]], 65534
+; CHECK: sth  [[REG]], 0(%r2)
+; CHECK: br   %r14
+  %val = load i16, ptr %src
+  %or = or i16 %val, -2
+  store i16 %or, ptr %src
+  ret void
+}
+
+define void @or_i32_neg_2(ptr %src) {
+; CHECK-LABEL: or_i32_neg_2:
+; CHECK-NOT: oiy {{.*}}, 254
+; CHECK: o [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %src
+  %or = or i32 %val, -2
+  store i32 %or, ptr %src
+  ret void
+}
+
+define void @or_i64_neg_2(ptr %src) {
+; CHECK-LABEL: or_i64_neg_2:
+; CHECK-NOT: oiy {{.*}}, 254
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %src
+  %or = or i64 %val, -2
+  store i64 %or, ptr %src
+  ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to oi/oiy.
+define void @or_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: or_i32_too_wide:
+; CHECK-NOT: oi{{y?}} {{.*}}, 256
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 256
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @or_i32_oiy_boundary_128(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_boundary_128:
+; CHECK: oiy 4096(%r2), 128
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 128
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @or_i32_oiy_max_byte(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_max_byte:
+; CHECK: oiy 4096(%r2), 255
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 255
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
+define void @or_i16_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
+define void @or_i32_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
+define void @or_i64_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @or_i16_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @or_i32_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64  transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @or_i64_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @or_i16_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524286
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @or_i32_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524284
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @or_i64_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524280
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i16_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i32_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i64_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @or_i16_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @or_i32_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @or_i64_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i16_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_min_disp:
+; CHECK: oiy -524287(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i32_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_min_disp:
+; CHECK: oiy -524285(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i64_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_min_disp:
+; CHECK: oiy -524281(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i16_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i32_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @or_i16_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i16_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4094
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @or_i32_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i32_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4092
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @or_i64_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i64_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4088
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i16_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i32_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4093
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i64_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4089
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: oi {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load volatile i32, ptr %ptr
+  %or = or i32 %val, 1
+  store volatile i32 %or, ptr %ptr
+  ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oill {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret i32 %val
+}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05-a.ll b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
new file mode 100644
index 0000000000000..5b19d66ca50bf
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
@@ -0,0 +1,536 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; xor-05.ll tests the multiclass RMWIByte for i8 types. 
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
+;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
+;      legalization in the address matcher before folding can occur.
+;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
+;      be reached via a starting GEP offset of 524287, as the Big-Endian 
+;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+;    Negative constants fail to fold for multi-byte types because they are
+;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @xor_i16_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i16_xi_low_unsigned:
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @xor_i32_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_low_unsigned:
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @xor_i64_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i64_xi_low_unsigned:
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @xor_i32_xi_boundary_128(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_boundary_128:
+; CHECK: xi 3(%r2), 128
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 128
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @xor_i32_xi_max_byte(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_max_byte:
+; CHECK: xi 3(%r2), 255
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 255
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @xor_i16_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i16_neg_2:
+; CHECK-NOT: xi {{.*}}, 254
+; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf [[REG]], 65534
+; CHECK: sth  [[REG]], 0(%r2)
+; CHECK: br   %r14
+  %val = load i16, ptr %src
+  %xor = xor i16 %val, -2
+  store i16 %xor, ptr %src
+  ret void
+}
+
+define void @xor_i32_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i32_neg_2:
+; CHECK-NOT: xiy {{.*}}, 254
+; CHECK: x [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %src
+  %xor = xor i32 %val, -2
+  store i32 %xor, ptr %src
+  ret void
+}
+
+define void @xor_i64_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i64_neg_2:
+; CHECK-NOT: xiy {{.*}}, 254
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %src
+  %xor = xor i64 %val, -2
+  store i64 %xor, ptr %src
+  ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to xi/xiy.
+define void @xor_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: xor_i32_too_wide:
+; CHECK-NOT: xi{{y?}} {{.*}}, 256
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 256
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @xor_i32_xiy_boundary_128(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_boundary_128:
+; CHECK: xiy 4096(%r2), 128
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 128
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @xor_i32_xiy_max_byte(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_max_byte:
+; CHECK: xiy 4096(%r2), 255
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 255
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
+define void @xor_i16_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
+define void @xor_i32_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
+define void @xor_i64_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16 transisition to xiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @xor_i16_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32 transisition to xiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @xor_i32_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64  transisition to xiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @xor_i64_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @xor_i16_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524286
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @xor_i32_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524284
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @xor_i64_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524280
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i16_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i32_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i64_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i16_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i32_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i64_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i16_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_min_disp:
+; CHECK: xiy -524287(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i32_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_min_disp:
+; CHECK: xiy -524285(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i64_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_min_disp:
+; CHECK: xiy -524281(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i16_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i32_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i64_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @xor_i16_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i16_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4094
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @xor_i32_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i32_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4092
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @xor_i64_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i64_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4088
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @xor_i16_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i16_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @xor_i32_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i32_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4093
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @xor_i64_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i64_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4089
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: xi {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load volatile i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store volatile i32 %xor, ptr %ptr
+  ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret i32 %val
+}

>From 54aee8cd33074d59a7e85a10f9dee40d1488639e Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Fri, 24 Apr 2026 01:23:20 +0200
Subject: [PATCH 2/7] [SystemZ] Update isFoldableRMW to support i16, i32, and
 i64 logical ops. This   aligns the TTI cost model with the corresponding
 DAGToDAG folding support   for these wider integer types.

---
 .../SystemZ/SystemZTargetTransformInfo.cpp    |  2 +-
 .../CostModel/SystemZ/fold-rmw-cost.ll        | 29 ++++++++++++
 llvm/test/CodeGen/SystemZ/and-05-a.ll         | 46 +++++++++----------
 llvm/test/CodeGen/SystemZ/or-05-a.ll          | 46 +++++++++----------
 4 files changed, 76 insertions(+), 47 deletions(-)

diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index 8932b90f5ff02..b5887da06385a 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -555,7 +555,7 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
   case Instruction::And:
   case Instruction::Or:
   case Instruction::Xor:
-    if (BitWidth != 8)
+    if (BitWidth != 8 && BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
       return false;
     break;
   case Instruction::Add:
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index 98aa0cda026d1..f82ac31797274 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -145,3 +145,32 @@ define void @test_vector_no_fold(<4 x i32> %val, ptr %p) {
   ret void
 }
 
+define void @test_xor_i16(ptr %p) {
+; CHECK-LABEL: 'test_xor_i16'
+; CHECK: cost of 0 {{.*}} xor i16
+; CHECK: cost of 0 {{.*}} store i16
+  %v = load i16, ptr %p
+  %res = xor i16 %v, 1
+  store i16 %res, ptr %p
+  ret void
+}
+
+define void @test_or_i32(ptr %p) {
+; CHECK-LABEL: 'test_or_i32'
+; CHECK: cost of 0 {{.*}} or i32
+; CHECK: cost of 0 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = or i32 %v, 1
+  store i32 %res, ptr %p
+  ret void
+}
+
+define void @test_and_i64(ptr %p) {
+; CHECK-LABEL: 'test_and_i64'
+; CHECK: cost of 0 {{.*}} and i64
+; CHECK: cost of 0 {{.*}} store i64
+  %v = load i64, ptr %p
+  %res = and i64 %v, 1
+  store i64 %res, ptr %p
+  ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
index d8f6914ebf95a..31084f4f8a87c 100644
--- a/llvm/test/CodeGen/SystemZ/and-05-a.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05-a.ll
@@ -2,7 +2,7 @@
 
 ; and-05.ll tests the multiclass RMWIByte for i8 types. 
 ; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+; covering both 12-bit unsigned (NI) and 20-bit signed (NIY) displacements.
 
 ; There are certain liomitations:
 ; 1. Displacement Range Limitations:
@@ -177,7 +177,7 @@ define void @and_i32_niy_max_byte(ptr %src) {
   ret void
 }
 
-; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
+; Check i16 high end of NI range:  Original 4095 + 1 (LSB) = 4095.
 define void @and_i16_ni_high_disp(ptr %src) {
 ; CHECK-LABEL: and_i16_ni_high_disp:
 ; CHECK: ni 4095(%r2), 1
@@ -189,7 +189,7 @@ define void @and_i16_ni_high_disp(ptr %src) {
   ret void
 }
 
-; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
+; Check i32 high end of NI range:  Original 4092 + 3 (LSB) = 4095.
 define void @and_i32_ni_high_disp(ptr %src) {
 ; CHECK-LABEL: and_i32_ni_high_disp:
 ; CHECK: ni 4095(%r2), 1
@@ -201,7 +201,7 @@ define void @and_i32_ni_high_disp(ptr %src) {
   ret void
 }
 
-; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
+; Check i64 high end of NI range:  Original 4088 + 7 (LSB) = 4095.
 define void @and_i64_ni_high_disp(ptr %src) {
 ; CHECK-LABEL: and_i64_ni_high_disp:
 ; CHECK: ni 4095(%r2), 1
@@ -213,7 +213,7 @@ define void @and_i64_ni_high_disp(ptr %src) {
   ret void
 }
 
-; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers NIY).
 define void @and_i16_niy_transition_disp(ptr %src) {
 ; CHECK-LABEL: and_i16_niy_transition_disp:
 ; CHECK:niy 4096(%r2), 1
@@ -225,7 +225,7 @@ define void @and_i16_niy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers NIY).
 define void @and_i32_niy_transition_disp(ptr %src) {
 ; CHECK-LABEL: and_i32_niy_transition_disp:
 ; CHECK:niy 4096(%r2), 1
@@ -237,7 +237,7 @@ define void @and_i32_niy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i64  transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check i64  transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers NIY).
 define void @and_i64_niy_transition_disp(ptr %src) {
 ; CHECK-LABEL: and_i64_niy_transition_disp:
 ; CHECK:niy 4096(%r2), 1
@@ -249,7 +249,7 @@ define void @and_i64_niy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+; Check i16 high end of the NIY range: Original 524286 + 1 (LSB) = 524287.
 define void @and_i16_niy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: and_i16_niy_high_end_disp:
 ; CHECK:niy 524287(%r2), 127
@@ -261,7 +261,7 @@ define void @and_i16_niy_high_end_disp(ptr %src) {
   ret void
 }
 
-; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+; Check i32  high end of the NIY range: Original 524284 + 3 (LSB) = 524287.
 define void @and_i32_niy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: and_i32_niy_high_end_disp:
 ; CHECK:niy 524287(%r2), 127
@@ -273,7 +273,7 @@ define void @and_i32_niy_high_end_disp(ptr %src) {
   ret void
 }
 
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+; Check i64 high end of the NIY range: Original 524280 + 7 (LSB) = 524287.
 define void @and_i64_niy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: and_i64_niy_high_end_disp:
 ; CHECK:niy 524287(%r2), 127
@@ -441,8 +441,8 @@ define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
   ret void
 }
 
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for NI).
 define void @and_i16_ni_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i16_ni_no_index:
 ; CHECK: agr %r2, %r3
@@ -457,8 +457,8 @@ define void @and_i16_ni_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for NI).
 define void @and_i32_ni_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i32_ni_no_index:
 ; CHECK: agr %r2, %r3
@@ -473,8 +473,8 @@ define void @and_i32_ni_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for NI).
 define void @and_i64_ni_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i64_ni_no_index:
 ; CHECK: agr %r2, %r3
@@ -489,8 +489,8 @@ define void @and_i64_ni_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers NIY).
 define void @and_i16_niy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i16_niy_no_index:
 ; CHECK: agr %r2, %r3
@@ -505,8 +505,8 @@ define void @and_i16_niy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers NIY).
 define void @and_i32_niy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i32_niy_no_index:
 ; CHECK: agr %r2, %r3
@@ -521,8 +521,8 @@ define void @and_i32_niy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers NIY).
 define void @and_i64_niy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: and_i64_niy_no_index:
 ; CHECK: agr %r2, %r3
@@ -537,7 +537,7 @@ define void @and_i64_niy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Volatile memory should not be folded into XI.
+; Volatile memory should not be folded into NI.
 define i32 @test_volatile(ptr %ptr) {
 ; CHECK-LABEL: test_volatile:
 ; CHECK-NOT: ni {{.*}}, 1
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
index b345da8ed8293..e87e1deb3f6ea 100644
--- a/llvm/test/CodeGen/SystemZ/or-05-a.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05-a.ll
@@ -2,7 +2,7 @@
 
 ; or-05.ll tests the multiclass RMWIByte for i8 types. 
 ; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+; covering both 12-bit unsigned (OI) and 20-bit signed (OIY) displacements.
 
 ; There are certain liomitations:
 ; 1. Displacement Range Limitations:
@@ -150,7 +150,7 @@ define void @or_i32_oiy_max_byte(ptr %src) {
   ret void
 }
 
-; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
+; Check i16 high end of OI range:  Original 4095 + 1 (LSB) = 4095.
 define void @or_i16_oi_high_disp(ptr %src) {
 ; CHECK-LABEL: or_i16_oi_high_disp:
 ; CHECK: oi 4095(%r2), 1
@@ -162,7 +162,7 @@ define void @or_i16_oi_high_disp(ptr %src) {
   ret void
 }
 
-; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
+; Check i32 high end of OI range:  Original 4092 + 3 (LSB) = 4095.
 define void @or_i32_oi_high_disp(ptr %src) {
 ; CHECK-LABEL: or_i32_oi_high_disp:
 ; CHECK: oi 4095(%r2), 1
@@ -174,7 +174,7 @@ define void @or_i32_oi_high_disp(ptr %src) {
   ret void
 }
 
-; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
+; Check i64 high end of OI range:  Original 4088 + 7 (LSB) = 4095.
 define void @or_i64_oi_high_disp(ptr %src) {
 ; CHECK-LABEL: or_i64_oi_high_disp:
 ; CHECK: oi 4095(%r2), 1
@@ -186,7 +186,7 @@ define void @or_i64_oi_high_disp(ptr %src) {
   ret void
 }
 
-; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers OIY).
 define void @or_i16_oiy_transition_disp(ptr %src) {
 ; CHECK-LABEL: or_i16_oiy_transition_disp:
 ; CHECK: oiy 4096(%r2), 1
@@ -198,7 +198,7 @@ define void @or_i16_oiy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers OIY).
 define void @or_i32_oiy_transition_disp(ptr %src) {
 ; CHECK-LABEL: or_i32_oiy_transition_disp:
 ; CHECK: oiy 4096(%r2), 1
@@ -210,7 +210,7 @@ define void @or_i32_oiy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i64  transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check i64  transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers OIY).
 define void @or_i64_oiy_transition_disp(ptr %src) {
 ; CHECK-LABEL: or_i64_oiy_transition_disp:
 ; CHECK: oiy 4096(%r2), 1
@@ -222,7 +222,7 @@ define void @or_i64_oiy_transition_disp(ptr %src) {
   ret void
 }
 
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+; Check i16 high end of the OIY range: Original 524286 + 1 (LSB) = 524287.
 define void @or_i16_oiy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: or_i16_oiy_high_end_disp:
 ; CHECK: oiy 524287(%r2), 127
@@ -234,7 +234,7 @@ define void @or_i16_oiy_high_end_disp(ptr %src) {
   ret void
 }
 
-; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+; Check i32  high end of the OIY range: Original 524284 + 3 (LSB) = 524287.
 define void @or_i32_oiy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: or_i32_oiy_high_end_disp:
 ; CHECK: oiy 524287(%r2), 127
@@ -246,7 +246,7 @@ define void @or_i32_oiy_high_end_disp(ptr %src) {
   ret void
 }
 
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+; Check i64 high end of the OIY range: Original 524280 + 7 (LSB) = 524287.
 define void @or_i64_oiy_high_end_disp(ptr %src) {
 ; CHECK-LABEL: or_i64_oiy_high_end_disp:
 ; CHECK: oiy 524287(%r2), 127
@@ -414,8 +414,8 @@ define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
   ret void
 }
 
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for OI).
 define void @or_i16_oi_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i16_oi_no_index:
 ; CHECK: agr %r2, %r3
@@ -430,8 +430,8 @@ define void @or_i16_oi_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for OI).
 define void @or_i32_oi_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i32_oi_no_index:
 ; CHECK: agr %r2, %r3
@@ -446,8 +446,8 @@ define void @or_i32_oi_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for OI).
 define void @or_i64_oi_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i64_oi_no_index:
 ; CHECK: agr %r2, %r3
@@ -462,8 +462,8 @@ define void @or_i64_oi_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers OIY).
 define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i16_oiy_no_index:
 ; CHECK: agr %r2, %r3
@@ -478,8 +478,8 @@ define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers OIY).
 define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i32_oiy_no_index:
 ; CHECK: agr %r2, %r3
@@ -494,8 +494,8 @@ define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers OIY).
 define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
 ; CHECK-LABEL: or_i64_oiy_no_index:
 ; CHECK: agr %r2, %r3
@@ -510,7 +510,7 @@ define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
   ret void
 }
 
-; Volatile memory should not be folded into XI.
+; Volatile memory should not be folded into OI.
 define i32 @test_volatile(ptr %ptr) {
 ; CHECK-LABEL: test_volatile:
 ; CHECK-NOT: oi {{.*}}, 1

>From b264db3c92b487fe6170bb551c280e5cbca1f8d4 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 27 Apr 2026 22:37:40 +0200
Subject: [PATCH 3/7] Revert changes to SystemZISelDAGToDAG.cpp as per code
 review

---
 .../Target/SystemZ/SystemZISelDAGToDAG.cpp    | 100 ++++--------------
 1 file changed, 19 insertions(+), 81 deletions(-)

diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index 16b1339f3b9a9..bb148c2dc2ebd 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -1328,22 +1328,18 @@ static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode,
   if (!StoredVal.getNode()->hasNUsesOfValue(1, 0))
     return false;
 
-  // Allow truncating stores for sub-word types but still reject
-  // complex addressing (indexed) and special memory hints (non-temporal).
-  if (StoreNode->isIndexed() || StoreNode->isNonTemporal())
+  // Is the store non-extending and non-indexed?
+  if (!ISD::isNormalStore(StoreNode) || StoreNode->isNonTemporal())
     return false;
 
   SDValue Load = StoredVal->getOperand(0);
-
-  // Peeking through ISD::ANY_EXTEND and allowing non-normal
-  // (Extending/Truncating) Loads/Stores.
-  if (Load.getOpcode() == ISD::ANY_EXTEND)
-    Load = Load.getOperand(0);
-  // Allow truncating stores and extending loads for sub-word logical ops.
-  LoadNode = dyn_cast<LoadSDNode>(Load);
-  if (!LoadNode || LoadNode->isIndexed())
+  // Is the stored value a non-extending and non-indexed load?
+  if (!ISD::isNormalLoad(Load.getNode()))
     return false;
 
+  // Return LoadNode by reference.
+  LoadNode = cast<LoadSDNode>(Load);
+
   // Is store the only read of the loaded value?
   if (!Load.hasOneUse())
     return false;
@@ -1419,7 +1415,7 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
   // and opcode we can handle. Note that this must match the code below that
   // actually lowers the opcodes.
   EVT MemVT = StoreNode->getMemoryVT();
-  std::pair<unsigned, unsigned> LOpcs;
+  unsigned NewOpc = 0;
   bool NegateOperand = false;
   switch (Opc) {
   default:
@@ -1429,9 +1425,9 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
     [[fallthrough]];
   case SystemZISD::SADDO:
     if (MemVT == MVT::i32)
-      LOpcs.first = SystemZ::ASI;
+      NewOpc = SystemZ::ASI;
     else if (MemVT == MVT::i64)
-      LOpcs.first = SystemZ::AGSI;
+      NewOpc = SystemZ::AGSI;
     else
       return false;
     break;
@@ -1440,21 +1436,12 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
     [[fallthrough]];
   case SystemZISD::UADDO:
     if (MemVT == MVT::i32)
-      LOpcs.first = SystemZ::ALSI;
+      NewOpc = SystemZ::ALSI;
     else if (MemVT == MVT::i64)
-      LOpcs.first = SystemZ::ALGSI;
+      NewOpc = SystemZ::ALGSI;
     else
       return false;
     break;
-  case ISD::AND:
-    LOpcs = {SystemZ::NI, SystemZ::NIY};
-    break;
-  case ISD::OR:
-    LOpcs = {SystemZ::OI, SystemZ::OIY};
-    break;
-  case ISD::XOR:
-    LOpcs = {SystemZ::XI, SystemZ::XIY};
-    break;
   }
 
   LoadSDNode *LoadNode = nullptr;
@@ -1470,71 +1457,22 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
   auto OperandV = OperandC->getAPIntValue();
   if (NegateOperand)
     OperandV = -OperandV;
-
-  bool IsLogicalByteOp = (LOpcs.second != 0);
-  unsigned NewOpc = 0;
-  if (!IsLogicalByteOp) {
-    if (OperandV.getSignificantBits() > 8)
-      return false;
-    NewOpc = LOpcs.first;
-    Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
-  } else {
-    if (OperandV.getActiveBits() > 8)
-      return false;
-    // Fix for logical byte ops (XI/Y, NI/Y, OI/Y).
-    // Ensure the APInt width matches the requested MVT::i32 width
-    // exactly to satisfy SelectionDAG assertions.
-    Operand = CurDAG->getTargetConstant(OperandV.zextOrTrunc(32), DL, MVT::i32);
-  }
+  if (OperandV.getSignificantBits() > 8)
+    return false;
+  Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
 
   SDValue Base, Disp;
   if (!selectBDAddr20Only(StoreNode->getBasePtr(), Base, Disp))
     return false;
 
-  // Handle Big-Endian offset for any integer width (i16, i32, i64).
-  if (IsLogicalByteOp) {
-    auto *DispC = dyn_cast<ConstantSDNode>(Disp);
-    if (!DispC)
-      return false;
-
-    int64_t NewDisp = DispC->getSExtValue();
-    // Calculate offset to the least significant byte.
-    if (MemVT.getStoreSize() > 1)
-      NewDisp += (MemVT.getStoreSize() - 1);
-    // Select the opcode based on the final displacement.
-    if (isUInt<12>(NewDisp)) {
-      NewOpc = LOpcs.first;
-    } else if (isInt<20>(NewDisp)) {
-      NewOpc = LOpcs.second;
-    } else {
-      return false;
-    }
-    Disp = CurDAG->getTargetConstant(NewDisp, DL, DispC->getValueType(0));
-  }
-  // Arithmetic ops like ASI/AGSI produce a result/CC (i32) and a Chain.
-  // Logical ops like XI/NI/OI produce just a Chain (MVT::Other).
-  SmallVector<EVT, 2> RetVTs;
-  // For ASI/AGSI/ALSI/ALGSI.
-  if (!IsLogicalByteOp)
-    RetVTs.push_back(MVT::i32);
-  // For the Chain.
-  RetVTs.push_back(MVT::Other);
-
   SDValue Ops[] = { Base, Disp, Operand, InputChain };
-  MachineSDNode *Result = CurDAG->getMachineNode(NewOpc, DL, RetVTs, Ops);
-
+  MachineSDNode *Result =
+    CurDAG->getMachineNode(NewOpc, DL, MVT::i32, MVT::Other, Ops);
   CurDAG->setNodeMemRefs(
       Result, {StoreNode->getMemOperand(), LoadNode->getMemOperand()});
 
-  if (IsLogicalByteOp) {
-    // XI/NI/OI only produce a Chain at index 0.
-    ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 0));
-  } else {
-    // ASI/AGSI produce CC/Result at index 0 and Chain at index 1.
-    ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
-    ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
-  }
-
+  ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
+  ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
   CurDAG->RemoveDeadNode(Node);
   return true;
 }

>From 3b8f6f08a2756f908ad39d176b365dd45ad67fb1 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 4 May 2026 01:38:01 +0200
Subject: [PATCH 4/7] [SystemZ] Fold i16/i32/i64 logical RMW operations into
 memory operands    - This implementation extends TableGen to support folding
 of AND, OR,      and XOR operations for wider types(i16/i32/i64) by using
 RMWIByteLSB      to adjust the displacement to target the LSB.    - Handles
 both 12-bit (NI/OI/XI) and 20-bit (NIY/OIY/XIY) displacements.      Also,
 optimizes out-of-range 20-bit addresses legalized into registers.    -
 Restricts folding to 8-bit positive immediates.

---
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td |   6 +
 llvm/lib/Target/SystemZ/SystemZOperands.td  |  43 ++
 llvm/lib/Target/SystemZ/SystemZOperators.td |  41 ++
 llvm/lib/Target/SystemZ/SystemZPatterns.td  |  52 ++
 llvm/test/CodeGen/SystemZ/and-05-a.ll       | 563 --------------------
 llvm/test/CodeGen/SystemZ/and-05.ll         | 526 +++++++++++++++++-
 llvm/test/CodeGen/SystemZ/or-05-a.ll        | 536 -------------------
 llvm/test/CodeGen/SystemZ/or-05.ll          | 528 +++++++++++++++++-
 llvm/test/CodeGen/SystemZ/xor-05-a.ll       | 536 -------------------
 llvm/test/CodeGen/SystemZ/xor-05.ll         | 529 +++++++++++++++++-
 10 files changed, 1687 insertions(+), 1673 deletions(-)
 delete mode 100644 llvm/test/CodeGen/SystemZ/and-05-a.ll
 delete mode 100644 llvm/test/CodeGen/SystemZ/or-05-a.ll
 delete mode 100644 llvm/test/CodeGen/SystemZ/xor-05-a.ll

diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 269fa6ffda0b9..b91c27e2ee22c 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1318,6 +1318,8 @@ let Defs = [CC] in {
 }
 defm : RMWIByte<and, bdaddr12pair, NI>;
 defm : RMWIByte<and, bdaddr20pair, NIY>;
+defm : RMWIByteLSB<and, bdaddr12pair, NI>;
+defm : RMWIByteLSB<and, bdaddr20pair, NIY>;
 
 //===----------------------------------------------------------------------===//
 // OR
@@ -1375,6 +1377,8 @@ let Defs = [CC] in {
 }
 defm : RMWIByte<or, bdaddr12pair, OI>;
 defm : RMWIByte<or, bdaddr20pair, OIY>;
+defm : RMWIByteLSB<or,  bdaddr12pair, OI>;
+defm : RMWIByteLSB<or,  bdaddr20pair, OIY>;
 
 //===----------------------------------------------------------------------===//
 // XOR
@@ -1415,6 +1419,8 @@ let Defs = [CC] in {
 }
 defm : RMWIByte<xor, bdaddr12pair, XI>;
 defm : RMWIByte<xor, bdaddr20pair, XIY>;
+defm : RMWIByteLSB<xor, bdaddr12pair, XI>;
+defm : RMWIByteLSB<xor, bdaddr20pair, XIY>;
 
 //===----------------------------------------------------------------------===//
 // Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 119bf4f7ca615..ceb5038d50b5b 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -694,3 +694,46 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
   let IsAPInt = true;
   let FastIselShouldIgnore = true;
 }
+
+// Extracts the base register from the load node.
+def get_base_from_load : SDNodeXForm<ld, [{
+  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+  // If the address is (Base + Disp), return Base.
+  // Otherwise, return the whole Addr.
+  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+    return Addr.getOperand(0);
+  return Addr;
+}]>;
+
+// i16: Target the displacement of the load and add +1 for Big-Endian LSB.
+def get_disp_lsb1 : SDNodeXForm<ld, [{
+  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+  int64_t Disp = 0;
+  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+  return CurDAG->getTargetConstant(Disp + 1, SDLoc(N), MVT::i64);
+}]>;
+
+// i32: Target the displacement of the load and add +3 for Big-Endian LSB.
+def get_disp_lsb3 : SDNodeXForm<ld, [{
+  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+  int64_t Disp = 0;
+  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+  return CurDAG->getTargetConstant(Disp + 3, SDLoc(N), MVT::i64);
+}]>;
+
+// i64: Target the displacement of the load and add +7 for Big-Endian LSB.
+def get_disp_lsb7 : SDNodeXForm<ld, [{
+  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+  int64_t Disp = 0;
+  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+  return CurDAG->getTargetConstant(Disp + 7, SDLoc(N), MVT::i64);
+}]>;
+
+// Normalizes the immediate to i32.
+def as_i32imm : SDNodeXForm<imm, [{
+  uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
+  return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index b69f84d78aaf1..82db8e4f35349 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1367,3 +1367,44 @@ class z_vse_by_parts<SDPatternOperator operator, int index1, int index2>
 def z_vsei8_by_parts  : z_vse_by_parts<sext8dbl, 7, 15>;
 def z_vsei16_by_parts : z_vse_by_parts<sext16dbl, 3, 7>;
 def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
+
+// Matches any integer constant where only the last byte is set.
+def immLSB8 : PatLeaf<(imm), [{
+  return N->getAPIntValue().getActiveBits() <= 8;
+}]>;
+
+// Fragment for i16 any-extending loads with a 20-bit signed displacement check.
+def anyextloadi16_20bit : PatFrag<(ops node:$addr), (anyextloadi16 $addr), [{
+  auto *Load = cast<MemSDNode>(N);
+  SDValue BaseAddr = Load->getBasePtr();
+  if (BaseAddr.getOpcode() == ISD::ADD &&
+      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+    return Disp >= -524288 && Disp <= 524287;
+  }
+  return true;
+}]>;
+
+// Fragment for i32/i64 loads with a 20-bit signed displacement check.
+def load_20bit : PatFrag<(ops node:$addr), (load $addr), [{
+  auto *Load = cast<MemSDNode>(N);
+  SDValue BaseAddr = Load->getBasePtr();
+  if (BaseAddr.getOpcode() == ISD::ADD &&
+      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+    return Disp >= -524288 && Disp <= 524287;
+  }
+  return true;
+}]>;
+
+// Check if displacement is safe for RMW operations on wider types.
+def load_RMW_safe : PatFrag<(ops node:$addr), (load $addr), [{
+  auto *Load = cast<MemSDNode>(N);
+  SDValue BaseAddr = Load->getBasePtr();
+  if (BaseAddr.getOpcode() == ISD::ADD &&
+      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+    return Disp >= -524288 && Disp <= 524287;
+  }
+  return true;
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 4d6bc68e9a7ed..7cf4a932dc5b7 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -53,6 +53,58 @@ multiclass RMWIByte<SDPatternOperator operator, AddressingMode mode,
   def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm64, insn>;
 }
 
+// Handle RMW operations on the Least Significant Byte (LSB) of wider types.
+// This allows targeting byte-level instructions (NI, NIY, etc.) for i16, i32,
+// and i64 memory locations when only the last byte is modified.
+multiclass RMWIByteLSB<SDPatternOperator operator, AddressingMode mode,
+                       Instruction insn> {
+
+  // Base + Displacement.
+  // Uses XForms to adjust the displacement to target the LSB.
+  let AddedComplexity = 20 in {
+    def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16_20bit:$src 
+                                                  mode:$addr)),
+                                            (i32 immLSB8:$imm))),
+                             mode:$addr),
+              (insn (get_base_from_load $src), 
+                    (get_disp_lsb1 $src), 
+                    (as_i32imm $imm))>;
+
+    def : Pat<(store (i32 (operator (i32 (load_20bit:$src mode:$addr)),
+                                    (i32 immLSB8:$imm))),
+                     mode:$addr),
+              (insn (get_base_from_load $src), 
+                    (get_disp_lsb3 $src), 
+                    (as_i32imm $imm))>;
+
+    def : Pat<(store (i64 (operator (i64 (load_20bit:$src mode:$addr)),
+                                    (i64 immLSB8:$imm))),
+                     mode:$addr),
+              (insn (get_base_from_load $src), 
+                    (get_disp_lsb7 $src), 
+                    (as_i32imm $imm))>;
+  }
+
+  // Register-only fallback (for AGFI or legalized addresses).
+  // Targets out-of-range addresses legalized into registers.
+  let AddedComplexity = 15 in {
+    def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 ADDR64:$base)),
+                                            (i32 immLSB8:$imm))),
+                             ADDR64:$base),
+              (insn ADDR64:$base, 1, (as_i32imm $imm))>;
+
+    def : Pat<(store (i32 (operator (i32 (load ADDR64:$base)),
+                                    (i32 immLSB8:$imm))),
+                     ADDR64:$base),
+              (insn ADDR64:$base, 3, (as_i32imm $imm))>;
+
+    def : Pat<(store (i64 (operator (i64 (load ADDR64:$base)),
+                                    (i64 immLSB8:$imm))),
+                     ADDR64:$base),
+              (insn ADDR64:$base, 7, (as_i32imm $imm))>;
+  }
+}
+
 // Record that INSN performs insertion TYPE into a register of class CLS.
 // The inserted operand is loaded using LOAD from an address of mode MODE.
 multiclass InsertMem<string type, Instruction insn, RegisterOperand cls,
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
deleted file mode 100644
index 31084f4f8a87c..0000000000000
--- a/llvm/test/CodeGen/SystemZ/and-05-a.ll
+++ /dev/null
@@ -1,563 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; and-05.ll tests the multiclass RMWIByte for i8 types. 
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (NI) and 20-bit signed (NIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
-;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
-;      legalization in the address matcher before folding can occur.
-;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
-;      be reached via a starting GEP offset of 524287, as the Big-Endian 
-;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-;    Negative constants fail to fold for multi-byte types because they are
-;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @and_i16_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i16_ni_low_unsigned:
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @and_i32_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_low_unsigned:
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @and_i64_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i64_ni_low_unsigned:
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @and_i32_ni_boundary_128(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_boundary_128:
-; CHECK: ni 3(%r2), 128
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 128
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
-define void @and_i32_ni_max_byte_minus_1(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_max_byte_minus_1:
-; CHECK: ni 3(%r2), 254
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 254
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-; Phase ordering, DAG combiner identifies 'and 255' on 32-bit load as
-; zero-extending byte load (llc).
-define void @and_i32_ni_max_byte(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_max_byte:
-; CHECK: llc [[REG:%r[0-9]+]], 3(%r2)
-; CHECK: st [[REG]], 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 255
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @and_i16_neg_2(ptr %src) {
-; CHECK-LABEL: and_i16_neg_2:
-; CHECK-NOT: ni {{.*}}, 254
-; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: nilf [[REG]], 65534
-; CHECK: sth  [[REG]], 0(%r2)
-; CHECK: br   %r14
-  %val = load i16, ptr %src
-  %and = and i16 %val, -2
-  store i16 %and, ptr %src
-  ret void
-}
-
-define void @and_i32_neg_2(ptr %src) {
-; CHECK-LABEL: and_i32_neg_2:
-; CHECK-NOT:niy {{.*}}, 254
-; CHECK: n [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %src
-  %and = and i32 %val, -2
-  store i32 %and, ptr %src
-  ret void
-}
-
-; DAG Combiner already combines it into a single And Memory (ng) instruction.
-define void @and_i64_neg_2(ptr %src) {
-; CHECK-LABEL: and_i64_neg_2:
-; CHECK: ng %r0, 0(%r2)
-; CHECK: br %r14
-  %val = load i64, ptr %src
-  %and = and i64 %val, -2
-  store i64 %and, ptr %src
-  ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to ni/niy.
-define void @and_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: and_i32_too_wide:
-; CHECK-NOT: ni{{y?}} {{.*}}, 256
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 256
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @and_i32_niy_boundary_128(ptr %src) {
-; CHECK-LABEL: and_i32_niy_boundary_128:
-; CHECK: niy 4096(%r2), 128
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 128
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
-define void @and_i32_niy_max_byte_minus_1(ptr %src) {
-; CHECK-LABEL: and_i32_niy_max_byte_minus_1:
-; CHECK: niy 4096(%r2), 254
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 254
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-; DAG Combiner transforms the 4-byte load into an 8-bit load logical (llc).
-define void @and_i32_niy_max_byte(ptr %src) {
-; CHECK-LABEL: and_i32_niy_max_byte:
-; CHECK: llc [[REG:%r[0-9]+]], 4096(%r2)
-; CHECK: st  [[REG]], 4093(%r2)
-; CHECK: br  %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %xor = and i32 %val, 255
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of NI range:  Original 4095 + 1 (LSB) = 4095.
-define void @and_i16_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i16_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4094
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32 high end of NI range:  Original 4092 + 3 (LSB) = 4095.
-define void @and_i32_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i32_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4092
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of NI range:  Original 4088 + 7 (LSB) = 4095.
-define void @and_i64_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i64_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4088
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers NIY).
-define void @and_i16_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers NIY).
-define void @and_i32_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64  transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers NIY).
-define void @and_i64_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4089
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of the NIY range: Original 524286 + 1 (LSB) = 524287.
-define void @and_i16_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524286
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32  high end of the NIY range: Original 524284 + 3 (LSB) = 524287.
-define void @and_i32_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524284
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of the NIY range: Original 524280 + 7 (LSB) = 524287.
-define void @and_i64_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524280
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i16_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i32_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i64_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @and_i16_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -2
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @and_i32_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -4
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @and_i64_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -8
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i16_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_min_disp:
-; CHECK:niy -524287(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i32_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_min_disp:
-; CHECK:niy -524285(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i64_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_min_disp:
-; CHECK:niy -524281(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i16_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i32_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check that NI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for NI).
-define void @and_i16_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i16_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4094
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check that NI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for NI).
-define void @and_i32_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i32_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4092
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check that NI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for NI).
-define void @and_i64_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i64_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4088
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Check that NIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers NIY).
-define void @and_i16_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i16_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %and = and i16 %val, 1
-  store i16 %and, ptr %ptr
-  ret void
-}
-
-; Check that NIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers NIY).
-define void @and_i32_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i32_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4093
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret void
-}
-
-; Check that NIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers NIY).
-define void @and_i64_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i64_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4089
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %and = and i64 %val, 1
-  store i64 %and, ptr %ptr
-  ret void
-}
-
-; Volatile memory should not be folded into NI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: ni {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load volatile i32, ptr %ptr
-  %and = and i32 %val, 1
-  store volatile i32 %and, ptr %ptr
-  ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: nilf {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %and = and i32 %val, 1
-  store i32 %and, ptr %ptr
-  ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 5f28480966e10..22b73ff41ff39 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -2,7 +2,7 @@
 ;
 ; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
 
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
 define void @f1(ptr %ptr) {
 ; CHECK-LABEL: f1:
 ; CHECK: ni 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
   ret void
 }
 
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: nill %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, -255
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, -255
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi %r0, -255
+; CHECK: ng %r0, 0(%r2)
+; CHECK: stg %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -255
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
 define void @f2(ptr %ptr) {
 ; CHECK-LABEL: f2:
 ; CHECK: ni 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
   ret void
 }
 
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: ni {{[0-9]+}}(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: nilf %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, -2
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, -2
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi %r0, -2
+; CHECK: ng %r0, 0(%r2)
+; CHECK: stg %r0, 0(%r2)
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -2
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
 define void @f3(ptr %ptr) {
 ; CHECK-LABEL: f3:
 ; CHECK: ni 0(%r2), 1
@@ -35,6 +117,39 @@ define void @f3(ptr %ptr) {
   ret void
 }
 
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 1
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 1
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 1
+  store i64 %and, ptr %ptr
+  ret void
+}
+
 ; Check the highest useful constant, expressed as a unsigned integer.
 define void @f4(ptr %ptr) {
 ; CHECK-LABEL: f4:
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
   ret void
 }
 
-; Check the high end of the NI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: ni 1(%r2), 254
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 254
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 254
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: ni 7(%r2), 254
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 254
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NI range for i8.
 define void @f5(ptr %src) {
 ; CHECK-LABEL: f5:
 ; CHECK: ni 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which should use NIY instead of NI.
+; Check the high end of the NI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: ni 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: ni 4098(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: ni 4102(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which should use NIY instead of NI.
 define void @f6(ptr %src) {
 ; CHECK-LABEL: f6:
 ; CHECK: niy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
   ret void
 }
 
-; Check the high end of the NIY range.
+; Check the next byte up for i16, which should use NIY instead of NI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: niy 4097(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which should use NIY instead of NI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: niy 4099(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which should use NIY instead of NI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: niy 4103(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NIY range for i8.
 define void @f7(ptr %src) {
 ; CHECK-LABEL: f7:
 ; CHECK: niy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the NIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: niy 524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: niy 524290(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the NIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: niy 524294(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f8(ptr %src) {
 ; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
   ret void
 }
 
-; Check the high end of the negative NIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative NIY range for i8.
 define void @f9(ptr %src) {
 ; CHECK-LABEL: f9:
 ; CHECK: niy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
   ret void
 }
 
-; Check the low end of the NIY range.
+; Check the high end of the negative NIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: niy 0(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative NIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: niy 2(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative NIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: niy 6(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the low end of the NIY range for i8.
 define void @f10(ptr %src) {
 ; CHECK-LABEL: f10:
 ; CHECK: niy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
   ret void
 }
 
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the NIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: niy -524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the low end of the NIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: niy -524285(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the low end of the NIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: niy -524281(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f11(ptr %src) {
 ; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
   ret void
 }
 
-; Check that NI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check that NI does not allow an index for i8.
 define void @f12(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12:
 ; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that NIY does not allow an index
+; Check that NI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4096(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the NI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4098(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the NI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4102(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check that NIY does not allow an index for i8.
 define void @f13(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13:
 ; CHECK: agr %r2, %r3
@@ -163,3 +620,48 @@ define void @f13(i64 %src, i64 %index) {
   store i8 %and, ptr %ptr
   ret void
 }
+
+; Check the NIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4097(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 127
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check the NIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4099(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 127
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check the NIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4103(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, 127
+  store i64 %and, ptr %ptr
+  ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
deleted file mode 100644
index e87e1deb3f6ea..0000000000000
--- a/llvm/test/CodeGen/SystemZ/or-05-a.ll
+++ /dev/null
@@ -1,536 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; or-05.ll tests the multiclass RMWIByte for i8 types. 
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (OI) and 20-bit signed (OIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
-;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
-;      legalization in the address matcher before folding can occur.
-;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
-;      be reached via a starting GEP offset of 524287, as the Big-Endian 
-;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-;    Negative constants fail to fold for multi-byte types because they are
-;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @or_i16_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i16_oi_low_unsigned:
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @or_i32_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_low_unsigned:
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @or_i64_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i64_oi_low_unsigned:
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @or_i32_oi_boundary_128(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_boundary_128:
-; CHECK: oi 3(%r2), 128
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 128
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @or_i32_oi_max_byte(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_max_byte:
-; CHECK: oi 3(%r2), 255
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 255
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @or_i16_neg_2(ptr %src) {
-; CHECK-LABEL: or_i16_neg_2:
-; CHECK-NOT: oi {{.*}}, 254
-; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: oill [[REG]], 65534
-; CHECK: sth  [[REG]], 0(%r2)
-; CHECK: br   %r14
-  %val = load i16, ptr %src
-  %or = or i16 %val, -2
-  store i16 %or, ptr %src
-  ret void
-}
-
-define void @or_i32_neg_2(ptr %src) {
-; CHECK-LABEL: or_i32_neg_2:
-; CHECK-NOT: oiy {{.*}}, 254
-; CHECK: o [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %src
-  %or = or i32 %val, -2
-  store i32 %or, ptr %src
-  ret void
-}
-
-define void @or_i64_neg_2(ptr %src) {
-; CHECK-LABEL: or_i64_neg_2:
-; CHECK-NOT: oiy {{.*}}, 254
-; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: stg [[REG]], 0(%r2)
-; CHECK: br %r14
-  %val = load i64, ptr %src
-  %or = or i64 %val, -2
-  store i64 %or, ptr %src
-  ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to oi/oiy.
-define void @or_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: or_i32_too_wide:
-; CHECK-NOT: oi{{y?}} {{.*}}, 256
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 256
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @or_i32_oiy_boundary_128(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_boundary_128:
-; CHECK: oiy 4096(%r2), 128
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 128
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @or_i32_oiy_max_byte(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_max_byte:
-; CHECK: oiy 4096(%r2), 255
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 255
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of OI range:  Original 4095 + 1 (LSB) = 4095.
-define void @or_i16_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4094
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32 high end of OI range:  Original 4092 + 3 (LSB) = 4095.
-define void @or_i32_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4092
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of OI range:  Original 4088 + 7 (LSB) = 4095.
-define void @or_i64_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4088
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers OIY).
-define void @or_i16_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers OIY).
-define void @or_i32_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64  transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers OIY).
-define void @or_i64_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4089
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of the OIY range: Original 524286 + 1 (LSB) = 524287.
-define void @or_i16_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524286
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 127
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32  high end of the OIY range: Original 524284 + 3 (LSB) = 524287.
-define void @or_i32_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524284
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 127
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of the OIY range: Original 524280 + 7 (LSB) = 524287.
-define void @or_i64_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524280
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 127
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i16_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 127
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i32_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 127
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i64_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 127
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @or_i16_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -2
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @or_i32_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -4
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @or_i64_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -8
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i16_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_min_disp:
-; CHECK: oiy -524287(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i32_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_min_disp:
-; CHECK: oiy -524285(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i64_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_min_disp:
-; CHECK: oiy -524281(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i16_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i32_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check that OI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for OI).
-define void @or_i16_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i16_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4094
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check that OI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for OI).
-define void @or_i32_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i32_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4092
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check that OI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for OI).
-define void @or_i64_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i64_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4088
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Check that OIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers OIY).
-define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i16_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %or = or i16 %val, 1
-  store i16 %or, ptr %ptr
-  ret void
-}
-
-; Check that OIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers OIY).
-define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i32_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4093
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret void
-}
-
-; Check that OIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers OIY).
-define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i64_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4089
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %or = or i64 %val, 1
-  store i64 %or, ptr %ptr
-  ret void
-}
-
-; Volatile memory should not be folded into OI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: oi {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load volatile i32, ptr %ptr
-  %or = or i32 %val, 1
-  store volatile i32 %or, ptr %ptr
-  ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: oill {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %or = or i32 %val, 1
-  store i32 %or, ptr %ptr
-  ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index 6ec352fa7b419..e6b05a77f1f6f 100644
--- a/llvm/test/CodeGen/SystemZ/or-05.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05.ll
@@ -2,7 +2,7 @@
 ;
 ; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
 
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
 define void @f1(ptr %ptr) {
 ; CHECK-LABEL: f1:
 ; CHECK: oi 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
   ret void
 }
 
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: oi 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: oill %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, -255
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: oi 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: o %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, -255
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: oi 7(%r2)
+; CHECK: lg {{%r[0-9]+}}, 0(%r2)
+; CHECK: oihf    %r0, 4294967295
+; CHECK: stg {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, -255
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
 define void @f2(ptr %ptr) {
 ; CHECK-LABEL: f2:
 ; CHECK: oi 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
   ret void
 }
 
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: oi 1(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: oill %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, -2
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: oi 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: o %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, -2
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: oi 7(%r2)
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oilf [[REG]], 4294967294
+; CHECK: stg [[REG]], 0(%r2)
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, -2
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
 define void @f3(ptr %ptr) {
 ; CHECK-LABEL: f3:
 ; CHECK: oi 0(%r2), 1
@@ -35,7 +117,40 @@ define void @f3(ptr %ptr) {
   ret void
 }
 
-; Check the highest useful constant, expressed as a unsigned integer.
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 1
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 1
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 1
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant i8, expressed as a unsigned integer.
 define void @f4(ptr %ptr) {
 ; CHECK-LABEL: f4:
 ; CHECK: oi 0(%r2), 254
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
   ret void
 }
 
-; Check the high end of the OI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: oi 1(%r2), 254
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 254
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: oi 3(%r2), 254
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 254
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: oi 7(%r2), 254
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 254
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OI range for i8.
 define void @f5(ptr %src) {
 ; CHECK-LABEL: f5:
 ; CHECK: oi 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which should use OIY instead of OI.
+; Check the high end of the OI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: oi 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: oi 4098(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: oi 4102(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which should use OIY instead of OI.
 define void @f6(ptr %src) {
 ; CHECK-LABEL: f6:
 ; CHECK: oiy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
   ret void
 }
 
-; Check the high end of the OIY range.
+; Check the next byte up for i16, which should use OIY instead of OI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: oiy 4097(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which should use OIY instead of OI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: oiy 4099(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which should use OIY instead of OI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: oiy 4103(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OIY range for i8.
 define void @f7(ptr %src) {
 ; CHECK-LABEL: f7:
 ; CHECK: oiy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the OIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: oiy 524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: oiy 524290(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the OIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: oiy 524294(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f8(ptr %src) {
 ; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
   ret void
 }
 
-; Check the high end of the negative OIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative OIY range for i8.
 define void @f9(ptr %src) {
 ; CHECK-LABEL: f9:
 ; CHECK: oiy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
   ret void
 }
 
-; Check the low end of the OIY range.
+; Check the high end of the negative OIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: oiy 0(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative OIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: oiy 2(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative OIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: oiy 6(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the low end of the OIY range for i8.
 define void @f10(ptr %src) {
 ; CHECK-LABEL: f10:
 ; CHECK: oiy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
   ret void
 }
 
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the OIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: oiy -524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the low end of the OIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: oiy -524285(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the low end of the OIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: oiy -524281(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f11(ptr %src) {
 ; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
   ret void
 }
 
-; Check that OI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check that OI does not allow an index for i8.
 define void @f12(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12:
 ; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that OIY does not allow an index
+; Check the OI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4096(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the OI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4098(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the OI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4102(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check that OIY does not allow an index for i8.
 define void @f13(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13:
 ; CHECK: agr %r2, %r3
@@ -163,3 +620,48 @@ define void @f13(i64 %src, i64 %index) {
   store i8 %or, ptr %ptr
   ret void
 }
+
+; Check the OIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4097(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %or = or i16 %val, 127
+  store i16 %or, ptr %ptr
+  ret void
+}
+
+; Check the OIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4099(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %or = or i32 %val, 127
+  store i32 %or, ptr %ptr
+  ret void
+}
+
+; Check the OIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4103(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %or = or i64 %val, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05-a.ll b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
deleted file mode 100644
index 5b19d66ca50bf..0000000000000
--- a/llvm/test/CodeGen/SystemZ/xor-05-a.ll
+++ /dev/null
@@ -1,536 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; xor-05.ll tests the multiclass RMWIByte for i8 types. 
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-;    - Minimum: For multi-byte types, the absolute minimum 20-bit displacement 
-;      (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI 
-;      legalization in the address matcher before folding can occur.
-;    - Maximum: Similarly, the absolute maximum displacement (524287) cannot 
-;      be reached via a starting GEP offset of 524287, as the Big-Endian 
-;      adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-;    Negative constants fail to fold for multi-byte types because they are
-;    sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-;    active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @xor_i16_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i16_xi_low_unsigned:
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @xor_i32_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_low_unsigned:
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @xor_i64_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i64_xi_low_unsigned:
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @xor_i32_xi_boundary_128(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_boundary_128:
-; CHECK: xi 3(%r2), 128
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 128
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @xor_i32_xi_max_byte(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_max_byte:
-; CHECK: xi 3(%r2), 255
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 255
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value 
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ 
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @xor_i16_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i16_neg_2:
-; CHECK-NOT: xi {{.*}}, 254
-; CHECK: lh   [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: xilf [[REG]], 65534
-; CHECK: sth  [[REG]], 0(%r2)
-; CHECK: br   %r14
-  %val = load i16, ptr %src
-  %xor = xor i16 %val, -2
-  store i16 %xor, ptr %src
-  ret void
-}
-
-define void @xor_i32_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i32_neg_2:
-; CHECK-NOT: xiy {{.*}}, 254
-; CHECK: x [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %src
-  %xor = xor i32 %val, -2
-  store i32 %xor, ptr %src
-  ret void
-}
-
-define void @xor_i64_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i64_neg_2:
-; CHECK-NOT: xiy {{.*}}, 254
-; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: stg [[REG]], 0(%r2)
-; CHECK: br %r14
-  %val = load i64, ptr %src
-  %xor = xor i64 %val, -2
-  store i64 %xor, ptr %src
-  ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to xi/xiy.
-define void @xor_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: xor_i32_too_wide:
-; CHECK-NOT: xi{{y?}} {{.*}}, 256
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 256
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @xor_i32_xiy_boundary_128(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_boundary_128:
-; CHECK: xiy 4096(%r2), 128
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 128
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @xor_i32_xiy_max_byte(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_max_byte:
-; CHECK: xiy 4096(%r2), 255
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 255
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of XI range:  Original 4095 + 1 (LSB) = 4095.
-define void @xor_i16_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4094
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32 high end of XI range:  Original 4092 + 3 (LSB) = 4095.
-define void @xor_i32_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4092
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of XI range:  Original 4088 + 7 (LSB) = 4095.
-define void @xor_i64_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4088
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16 transisition to xiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
-define void @xor_i16_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32 transisition to xiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
-define void @xor_i32_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4093
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64  transisition to xiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
-define void @xor_i64_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4089
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
-define void @xor_i16_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524286
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 127
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32  high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
-define void @xor_i32_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524284
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 127
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
-define void @xor_i64_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524280
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 127
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i16_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 127
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i32_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 127
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i64_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524288
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 127
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i16_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -2
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i32_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -4
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i64_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -8
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i16_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_min_disp:
-; CHECK: xiy -524287(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i32_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_min_disp:
-; CHECK: xiy -524285(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i64_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_min_disp:
-; CHECK: xiy -524281(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i16_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i32_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i64_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
-define void @xor_i16_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i16_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4094
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
-define void @xor_i32_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i32_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4092
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
-define void @xor_i64_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i64_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4088
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
-define void @xor_i16_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i16_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i16, ptr %ptr
-  %xor = xor i16 %val, 1
-  store i16 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
-define void @xor_i32_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i32_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4093
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret void
-}
-
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
-define void @xor_i64_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i64_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
-  %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4089
-  %ptr = inttoptr i64 %add2 to ptr
-  %val = load i64, ptr %ptr
-  %xor = xor i64 %val, 1
-  store i64 %xor, ptr %ptr
-  ret void
-}
-
-; Volatile memory should not be folded into XI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: xi {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load volatile i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store volatile i32 %xor, ptr %ptr
-  ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: xilf {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
-  %val = load i32, ptr %ptr
-  %xor = xor i32 %val, 1
-  store i32 %xor, ptr %ptr
-  ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 5640e236793e5..50bc759837d79 100644
--- a/llvm/test/CodeGen/SystemZ/xor-05.ll
+++ b/llvm/test/CodeGen/SystemZ/xor-05.ll
@@ -2,7 +2,7 @@
 ;
 ; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
 
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
 define void @f1(ptr %ptr) {
 ; CHECK-LABEL: f1:
 ; CHECK: xi 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
   ret void
 }
 
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: xi 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: xilf %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, -255
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: xi 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: x %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, -255
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: xi 7(%r2)
+; CHECK: lg {{%r[0-9]+}}, 0(%r2)
+; CHECK: xihf    %r0, 4294967295
+; CHECK: stg {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, -255
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
 define void @f2(ptr %ptr) {
 ; CHECK-LABEL: f2:
 ; CHECK: xi 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
   ret void
 }
 
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: xi 1(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: xilf %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, -2
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: xi 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: x %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, -2
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: xi 7(%r2)
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf [[REG]], 4294967294
+; CHECK: stg [[REG]], 0(%r2)
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, -2
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
 define void @f3(ptr %ptr) {
 ; CHECK-LABEL: f3:
 ; CHECK: xi 0(%r2), 1
@@ -35,7 +117,40 @@ define void @f3(ptr %ptr) {
   ret void
 }
 
-; Check the highest useful constant, expressed as a unsigned integer.
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 1
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 1
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 1
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i8, expressed as a unsigned integer.
 define void @f4(ptr %ptr) {
 ; CHECK-LABEL: f4:
 ; CHECK: xi 0(%r2), 254
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
   ret void
 }
 
-; Check the high end of the XI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: xi 1(%r2), 254 
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 254 
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: xi 3(%r2), 254 
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 254 
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: xi 7(%r2), 254 
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 254 
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XI range for i8.
 define void @f5(ptr %src) {
 ; CHECK-LABEL: f5:
 ; CHECK: xi 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which should use XIY instead of XI.
+; Check the high end of the XI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: xi 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: xi 4098(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: xi 4102(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which should use XIY instead of XI.
 define void @f6(ptr %src) {
 ; CHECK-LABEL: f6:
 ; CHECK: xiy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
   ret void
 }
 
-; Check the high end of the XIY range.
+; Check the next byte up for i16, which should use XIY instead of XI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: xiy 4097(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which should use XIY instead of XI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: xiy 4099(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which should use XIY instead of XI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: xiy 4103(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4096
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XIY range for i8.
 define void @f7(ptr %src) {
 ; CHECK-LABEL: f7:
 ; CHECK: xiy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
   ret void
 }
 
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the XIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: xiy 524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: xiy 524290(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the XIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: xiy 524294(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524287
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f8(ptr %src) {
 ; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
   ret void
 }
 
-; Check the high end of the negative XIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524288
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative XIY range for i8.
 define void @f9(ptr %src) {
 ; CHECK-LABEL: f9:
 ; CHECK: xiy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
   ret void
 }
 
-; Check the low end of the XIY range.
+; Check the high end of the negative XIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: xiy 0(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative XIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: xiy 2(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the high end of the negative XIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: xiy 6(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -1
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the low end of the XIY range for i8.
 define void @f10(ptr %src) {
 ; CHECK-LABEL: f10:
 ; CHECK: xiy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
   ret void
 }
 
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the XIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: xiy -524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the low end of the XIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: xiy -524285(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the low end of the XIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: xiy -524281(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
 ; Other sequences besides this one would be OK.
 define void @f11(ptr %src) {
 ; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
   ret void
 }
 
-; Check that XI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XI does not allow an index for i8.
 define void @f12(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12:
 ; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
   ret void
 }
 
-; Check that XIY does not allow an index
+; Check the XI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4096(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the XI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4098(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the XI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4102(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4095
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check that XIY does not allow an index for i8.
 define void @f13(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13:
 ; CHECK: agr %r2, %r3
@@ -163,3 +620,49 @@ define void @f13(i64 %src, i64 %index) {
   store i8 %xor, ptr %ptr
   ret void
 }
+
+; Check the XIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4097(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i16, ptr %ptr
+  %xor = xor i16 %val, 127
+  store i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check the XIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4099(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i32, ptr %ptr
+  %xor = xor i32 %val, 127
+  store i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check the XIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4103(%r2), 127
+; CHECK: br %r14
+  %add1 = add i64 %src, %index
+  %add2 = add i64 %add1, 4096
+  %ptr = inttoptr i64 %add2 to ptr
+  %val = load i64, ptr %ptr
+  %xor = xor i64 %val, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+

>From 271eafcc4f37897f7806b89e11935afd80524309 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Fri, 22 May 2026 16:48:15 +0200
Subject: [PATCH 5/7] [SystemZ] Address code review feedback for folding
 i16/i32/i64 logical     RMW operations into memory operands    - Consolidate
 RMWIByte multiclass to handle both i8 and wider types.    - RMWIByteLSB
 multiclass for i16/i32/i63 with conditional selection      based on operation
 type.    - Extended selectBDAddr to accept offest parameter for wrapper     
 functions selectBDAddr12/20off1/3/7 to apply fixed byte offsets      for LSB
 access (i16: +1, i32: +3, i64: +7).    - Added immAndLSB8 pattern matching
 for valid constants for 'and'      operation.

---
 .../Target/SystemZ/SystemZISelDAGToDAG.cpp    |  41 ++-
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td   |  15 +-
 llvm/lib/Target/SystemZ/SystemZOperands.td    |  58 ++--
 llvm/lib/Target/SystemZ/SystemZOperators.td   |  56 ++--
 llvm/lib/Target/SystemZ/SystemZPatterns.td    | 109 ++++----
 llvm/test/CodeGen/SystemZ/and-05.ll           | 253 ++++++++++++++----
 6 files changed, 336 insertions(+), 196 deletions(-)

diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index bb148c2dc2ebd..992ad39690fc8 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -165,7 +165,7 @@ class SystemZDAGToDAGISel : public SelectionDAGISel {
   // Return true on success, storing the base and displacement in
   // Base and Disp respectively.
   bool selectBDAddr(SystemZAddressingMode::DispRange DR, SDValue Addr,
-                    SDValue &Base, SDValue &Disp) const;
+                    SDValue &Base, SDValue &Disp, int64_t Offset = 0) const;
 
   // Try to match Addr as a FormBDX address with displacement type DR.
   // Return true on success and if the result had no index.  Store the
@@ -267,6 +267,15 @@ class SystemZDAGToDAGISel : public SelectionDAGISel {
   // in Base, Disp and Index respectively.
   bool selectBDVAddr12Only(SDValue Addr, SDValue Elem, SDValue &Base,
                            SDValue &Disp, SDValue &Index) const;
+  // Wrapper functions for LSB access on big-endian multi-byte types.
+  // Selects a Base + Displacement address and applies a fixed byte Offset.
+  // Offsets: i16 LSB = +1, i32 LSB = +3, i64 LSB = +7.
+  bool selectBDAddr12off1(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+  bool selectBDAddr12off3(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+  bool selectBDAddr12off7(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+  bool selectBDAddr20off1(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+  bool selectBDAddr20off3(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+  bool selectBDAddr20off7(SDValue Addr, SDValue &Base, SDValue &Disp) const;
 
   // Check whether (or Op (and X InsertMask)) is effectively an insertion
   // of X into bits InsertMask of some Y != Op.  Return true if so and
@@ -693,12 +702,14 @@ void SystemZDAGToDAGISel::getAddressOperands(const SystemZAddressingMode &AM,
 
 bool SystemZDAGToDAGISel::selectBDAddr(SystemZAddressingMode::DispRange DR,
                                        SDValue Addr, SDValue &Base,
-                                       SDValue &Disp) const {
+                                       SDValue &Disp, int64_t Offset) const {
   SystemZAddressingMode AM(SystemZAddressingMode::FormBD, DR);
   if (!selectAddress(Addr, AM))
     return false;
 
+  AM.Disp += Offset;
   getAddressOperands(AM, Addr.getValueType(), Base, Disp);
+  Disp = CurDAG->getTargetConstant(AM.Disp, SDLoc(Addr), MVT::i64);
   return true;
 }
 
@@ -749,6 +760,32 @@ bool SystemZDAGToDAGISel::selectBDVAddr12Only(SDValue Addr, SDValue Elem,
   return false;
 }
 
+bool SystemZDAGToDAGISel::selectBDAddr12off1(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 1);
+}
+bool SystemZDAGToDAGISel::selectBDAddr12off3(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 3);
+}
+bool SystemZDAGToDAGISel::selectBDAddr12off7(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 7);
+}
+
+bool SystemZDAGToDAGISel::selectBDAddr20off1(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 1);
+}
+bool SystemZDAGToDAGISel::selectBDAddr20off3(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 3);
+}
+bool SystemZDAGToDAGISel::selectBDAddr20off7(SDValue A, SDValue &B,
+                                             SDValue &D) const {
+  return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 7);
+}
+
 bool SystemZDAGToDAGISel::detectOrAndInsertion(SDValue &Op,
                                                uint64_t InsertMask) const {
   // We're only interested in cases where the insertion is into some operand
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index b91c27e2ee22c..2832dadbb585e 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1316,10 +1316,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm NC : MemorySS<"nc", 0xD4, z_nc>;
 }
-defm : RMWIByte<and, bdaddr12pair, NI>;
-defm : RMWIByte<and, bdaddr20pair, NIY>;
-defm : RMWIByteLSB<and, bdaddr12pair, NI>;
-defm : RMWIByteLSB<and, bdaddr20pair, NIY>;
+defm : RMWIByte<and, NI, NIY>;
 
 //===----------------------------------------------------------------------===//
 // OR
@@ -1375,10 +1372,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm OC : MemorySS<"oc", 0xD6, z_oc>;
 }
-defm : RMWIByte<or, bdaddr12pair, OI>;
-defm : RMWIByte<or, bdaddr20pair, OIY>;
-defm : RMWIByteLSB<or,  bdaddr12pair, OI>;
-defm : RMWIByteLSB<or,  bdaddr20pair, OIY>;
+defm : RMWIByte<or,  OI, OIY>;
 
 //===----------------------------------------------------------------------===//
 // XOR
@@ -1417,10 +1411,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm XC : MemorySS<"xc", 0xD7, z_xc>;
 }
-defm : RMWIByte<xor, bdaddr12pair, XI>;
-defm : RMWIByte<xor, bdaddr20pair, XIY>;
-defm : RMWIByteLSB<xor, bdaddr12pair, XI>;
-defm : RMWIByteLSB<xor, bdaddr20pair, XIY>;
+defm : RMWIByte<xor, XI, XIY>;
 
 //===----------------------------------------------------------------------===//
 // Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index ceb5038d50b5b..921b6ed5020fd 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -695,45 +695,31 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
   let FastIselShouldIgnore = true;
 }
 
-// Extracts the base register from the load node.
-def get_base_from_load : SDNodeXForm<ld, [{
-  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
-  // If the address is (Base + Disp), return Base.
-  // Otherwise, return the whole Addr.
-  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
-    return Addr.getOperand(0);
-  return Addr;
-}]>;
-
-// i16: Target the displacement of the load and add +1 for Big-Endian LSB.
-def get_disp_lsb1 : SDNodeXForm<ld, [{
-  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
-  int64_t Disp = 0;
-  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
-    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
-  return CurDAG->getTargetConstant(Disp + 1, SDLoc(N), MVT::i64);
-}]>;
-
-// i32: Target the displacement of the load and add +3 for Big-Endian LSB.
-def get_disp_lsb3 : SDNodeXForm<ld, [{
-  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
-  int64_t Disp = 0;
-  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
-    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
-  return CurDAG->getTargetConstant(Disp + 3, SDLoc(N), MVT::i64);
-}]>;
-
-// i64: Target the displacement of the load and add +7 for Big-Endian LSB.
-def get_disp_lsb7 : SDNodeXForm<ld, [{
-  SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
-  int64_t Disp = 0;
-  if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
-    Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
-  return CurDAG->getTargetConstant(Disp + 7, SDLoc(N), MVT::i64);
-}]>;
+// These patterns target the Least Significant Byte (LSB) of multi-byte 
+// integers (i16, i32, i64). On SystemZ (Big-Endian), the LSB is located at 
+// a fixed positive offset from the base address:
+// i16: offset +1 (byte 1 of 2)
+// i32: offset +3 (byte 3 of 4)
+// i64: offset +7 (byte 7 of 8)
+
+// 12-bit unsigned displacement variants (for NI, OI, XI)
+def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
+def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
+def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
+
+// 20-bit signed displacement variants (for NIY, OIY, XIY)
+def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
+def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
+def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
 
 // Normalizes the immediate to i32.
 def as_i32imm : SDNodeXForm<imm, [{
   uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
   return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
 }]>;
+
+// Extracts only the bottom 8 bits.
+def as_i8imm : SDNodeXForm<imm, [{
+  uint64_t Val = N->getZExtValue() & 0xFF;
+  return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 82db8e4f35349..af14dbdd68a2a 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1368,43 +1368,33 @@ def z_vsei8_by_parts  : z_vse_by_parts<sext8dbl, 7, 15>;
 def z_vsei16_by_parts : z_vse_by_parts<sext16dbl, 3, 7>;
 def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
 
-// Matches any integer constant where only the last byte is set.
+// Matches any integer constant where only the last byte is set (0x00...XX).
+// Safe for OR/XOR because x | 0 == x.
 def immLSB8 : PatLeaf<(imm), [{
   return N->getAPIntValue().getActiveBits() <= 8;
 }]>;
 
-// Fragment for i16 any-extending loads with a 20-bit signed displacement check.
-def anyextloadi16_20bit : PatFrag<(ops node:$addr), (anyextloadi16 $addr), [{
-  auto *Load = cast<MemSDNode>(N);
-  SDValue BaseAddr = Load->getBasePtr();
-  if (BaseAddr.getOpcode() == ISD::ADD &&
-      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
-    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
-    return Disp >= -524288 && Disp <= 524287;
+// Matches constants where all bits except the last byte are 1 (0xFF...XX).
+def immAndLSB8 : PatLeaf<(imm), [{
+  uint64_t Val = N->getZExtValue();
+  uint64_t Mask = 0;
+  uint64_t Size = N->getValueType(0).getSizeInBits();
+
+  switch (Size) {
+  case 16:
+    Mask = 0xff00ULL;
+    break;
+  case 32:
+    // Support both full i32 and zero-extended i16 constant.
+    Mask = ((Val & 0xffffff00ULL) == 0xffffff00ULL) ? 0xffffff00ULL
+                                                    : 0x0000ff00ULL;
+    break;
+  case 64:
+    Mask = 0xffffffffffffff00ULL;
+    break;
+  default:
+    return false;
   }
-  return true;
-}]>;
-
-// Fragment for i32/i64 loads with a 20-bit signed displacement check.
-def load_20bit : PatFrag<(ops node:$addr), (load $addr), [{
-  auto *Load = cast<MemSDNode>(N);
-  SDValue BaseAddr = Load->getBasePtr();
-  if (BaseAddr.getOpcode() == ISD::ADD &&
-      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
-    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
-    return Disp >= -524288 && Disp <= 524287;
-  }
-  return true;
-}]>;
 
-// Check if displacement is safe for RMW operations on wider types.
-def load_RMW_safe : PatFrag<(ops node:$addr), (load $addr), [{
-  auto *Load = cast<MemSDNode>(N);
-  SDValue BaseAddr = Load->getBasePtr();
-  if (BaseAddr.getOpcode() == ISD::ADD &&
-      isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
-    int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
-    return Disp >= -524288 && Disp <= 524287;
-  }
-  return true;
+  return (Val & Mask) == Mask;
 }]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 7cf4a932dc5b7..bc733f8260bef 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -45,64 +45,61 @@ class RMWI<SDPatternOperator load, SDPatternOperator operator,
   : Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
         (insn mode:$addr, (UIMM8 imm:$src))>;
 
-// Record that INSN performs binary operation OPERATION on a byte
-// memory location.  IMM is the type of the second operand.
-multiclass RMWIByte<SDPatternOperator operator, AddressingMode mode,
-                    Instruction insn> {
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm32, insn>;
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm64, insn>;
-}
-
 // Handle RMW operations on the Least Significant Byte (LSB) of wider types.
-// This allows targeting byte-level instructions (NI, NIY, etc.) for i16, i32,
+// This allows targeting byte-level logical instructions for i16, i32,
 // and i64 memory locations when only the last byte is modified.
-multiclass RMWIByteLSB<SDPatternOperator operator, AddressingMode mode,
-                       Instruction insn> {
-
-  // Base + Displacement.
-  // Uses XForms to adjust the displacement to target the LSB.
-  let AddedComplexity = 20 in {
-    def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16_20bit:$src 
-                                                  mode:$addr)),
-                                            (i32 immLSB8:$imm))),
-                             mode:$addr),
-              (insn (get_base_from_load $src), 
-                    (get_disp_lsb1 $src), 
-                    (as_i32imm $imm))>;
-
-    def : Pat<(store (i32 (operator (i32 (load_20bit:$src mode:$addr)),
-                                    (i32 immLSB8:$imm))),
-                     mode:$addr),
-              (insn (get_base_from_load $src), 
-                    (get_disp_lsb3 $src), 
-                    (as_i32imm $imm))>;
-
-    def : Pat<(store (i64 (operator (i64 (load_20bit:$src mode:$addr)),
-                                    (i64 immLSB8:$imm))),
-                     mode:$addr),
-              (insn (get_base_from_load $src), 
-                    (get_disp_lsb7 $src), 
-                    (as_i32imm $imm))>;
-  }
-
-  // Register-only fallback (for AGFI or legalized addresses).
-  // Targets out-of-range addresses legalized into registers.
-  let AddedComplexity = 15 in {
-    def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 ADDR64:$base)),
-                                            (i32 immLSB8:$imm))),
-                             ADDR64:$base),
-              (insn ADDR64:$base, 1, (as_i32imm $imm))>;
-
-    def : Pat<(store (i32 (operator (i32 (load ADDR64:$base)),
-                                    (i32 immLSB8:$imm))),
-                     ADDR64:$base),
-              (insn ADDR64:$base, 3, (as_i32imm $imm))>;
-
-    def : Pat<(store (i64 (operator (i64 (load ADDR64:$base)),
-                                    (i64 immLSB8:$imm))),
-                     ADDR64:$base),
-              (insn ADDR64:$base, 7, (as_i32imm $imm))>;
-  }
+multiclass RMWIByteLSB<SDPatternOperator operator, 
+                       ComplexPattern mode12_1, ComplexPattern mode20_1,
+                       ComplexPattern mode12_3, ComplexPattern mode20_3,
+                       ComplexPattern mode12_7, ComplexPattern mode20_7,
+                       Instruction insn12, Instruction insn20,
+                       PatLeaf mask> {
+  // i16 LSB (+1 offset)
+  def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode12_1:$addr)),
+             (i32 mask:$imm))), mode12_1:$addr),
+            (insn12 mode12_1:$addr, (as_i8imm $imm))>;
+  def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode20_1:$addr)),
+             (i32 mask:$imm))), mode20_1:$addr),
+            (insn20 mode20_1:$addr, (as_i8imm $imm))>;
+
+
+  // i32 LSB (+3 offset)
+  def : Pat<(store (i32 (operator (i32 (load mode12_3:$addr)),
+             (i32 mask:$imm))), mode12_3:$addr),
+            (insn12 mode12_3:$addr, (as_i8imm $imm))>;
+  def : Pat<(store (i32 (operator (i32 (load mode20_3:$addr)),
+             (i32 mask:$imm))), mode20_3:$addr),
+            (insn20 mode20_3:$addr, (as_i8imm $imm))>;
+
+  // i64 LSB (+7 offset)
+  def : Pat<(store (i64 (operator (i64 (load mode12_7:$addr)),
+             (i64 mask:$imm))), mode12_7:$addr),
+            (insn12 mode12_7:$addr, (as_i8imm $imm))>;
+  def : Pat<(store (i64 (operator (i64 (load mode20_7:$addr)),
+             (i64 mask:$imm))), mode20_7:$addr),
+            (insn20 mode20_7:$addr, (as_i8imm $imm))>;
+}
+
+// Record that INSN performs binary operation OPERATION on a byte
+// memory location.  IMM is the type of the second operand.
+multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
+                    Instruction insn20> {
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm32,
+             insn12>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm64,
+             insn12>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm32,
+             insn20>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
+             insn20>;
+
+  // Conditional folding for wider types.
+  defm : RMWIByteLSB<operator,
+                     bdaddr12off1, bdaddr20off1,
+                     bdaddr12off3, bdaddr20off3,
+                     bdaddr12off7, bdaddr20off7,
+                     insn12, insn20,
+                     !if(!eq(operator, and), immAndLSB8, immLSB8)>;
 }
 
 // Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 22b73ff41ff39..f181c6f9a9c4f 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -16,10 +16,7 @@ define void @f1(ptr %ptr) {
 ; Check lowest useful constant for i16, expressed as a signed integer.
 define void @f1_i16(ptr %ptr) {
 ; CHECK-LABEL: f1_i16:
-; CHECK-NOT: ni 1(%r2)
-; CHECK: lh %r0, 0(%r2)
-; CHECK: nill %r0, 65281
-; CHECK: sth %r0, 0(%r2)
+; CHECK: ni 1(%r2), 1
 ; CHECK: br %r14
   %val = load i16, ptr %ptr
   %and = and i16 %val, -255
@@ -30,10 +27,7 @@ define void @f1_i16(ptr %ptr) {
 ; Check lowest useful constant for i32, expressed as a signed integer.
 define void @f1_i32(ptr %ptr) {
 ; CHECK-LABEL: f1_i32:
-; CHECK-NOT: ni 3(%r2)
-; CHECK: lhi %r0, -255
-; CHECK: n %r0, 0(%r2)
-; CHECK: st %r0, 0(%r2)
+; CHECK: ni 3(%r2), 1
 ; CHECK: br %r14
   %val = load i32, ptr %ptr
   %and = and i32 %val, -255
@@ -44,10 +38,7 @@ define void @f1_i32(ptr %ptr) {
 ; Check lowest useful constant for i64, expressed as a signed integer.
 define void @f1_i64(ptr %ptr) {
 ; CHECK-LABEL: f1_i64:
-; CHECK-NOT: ni 7(%r2)
-; CHECK: lghi %r0, -255
-; CHECK: ng %r0, 0(%r2)
-; CHECK: stg %r0, 0(%r2)
+; CHECK: ni 7(%r2), 1
 ; CHECK: br %r14
   %val = load i64, ptr %ptr
   %and = and i64 %val, -255
@@ -69,10 +60,8 @@ define void @f2(ptr %ptr) {
 ; Check the highest useful constant for i16, expressed as a signed integer.
 define void @f2_i16(ptr %ptr) {
 ; CHECK-LABEL: f2_i16:
-; CHECK-NOT: ni {{[0-9]+}}(%r2), 254
-; CHECK: lh %r0, 0(%r2)
-; CHECK: nilf %r0, 65534
-; CHECK: sth %r0, 0(%r2)
+; CHECK: ni 1(%r2), 254
+; CHECK: br %r14
   %val = load i16, ptr %ptr
   %and = and i16 %val, -2
   store i16 %and, ptr %ptr
@@ -82,10 +71,7 @@ define void @f2_i16(ptr %ptr) {
 ; Check the highest useful constant for i32, expressed as a signed integer.
 define void @f2_i32(ptr %ptr) {
 ; CHECK-LABEL: f2_i32:
-; CHECK-NOT: ni 3(%r2)
-; CHECK: lhi %r0, -2
-; CHECK: n %r0, 0(%r2)
-; CHECK: st %r0, 0(%r2)
+; CHECK: ni 3(%r2), 254
 ; CHECK: br %r14
   %val = load i32, ptr %ptr
   %and = and i32 %val, -2
@@ -93,13 +79,22 @@ define void @f2_i32(ptr %ptr) {
   ret void
 }
 
+; Check i32 with zero-extended i16 pattern (0x0000FFxx).
+define void @f2_i32_zext(ptr %ptr) {
+; CHECK-LABEL: f2_i32_zext:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 65534  ; 0x0000FFFE
+  store i32 %and, ptr %ptr
+  ret void
+}
+
 ; Check the highest useful constant for i64, expressed as a signed integer.
 define void @f2_i64(ptr %ptr) {
 ; CHECK-LABEL: f2_i64:
-; CHECK-NOT: ni 7(%r2)
-; CHECK: lghi %r0, -2
-; CHECK: ng %r0, 0(%r2)
-; CHECK: stg %r0, 0(%r2)
+; CHECK: ni 7(%r2), 254
+; CHECK: br %r14
   %val = load i64, ptr %ptr
   %and = and i64 %val, -2
   store i64 %and, ptr %ptr
@@ -118,9 +113,13 @@ define void @f3(ptr %ptr) {
 }
 
 ; Check the lowest useful constant for i16, expressed as an unsigned integer.
+; Constant 1 should not fold for i16, as it requires clearing the high byte.
 define void @f3_i16(ptr %ptr) {
 ; CHECK-LABEL: f3_i16:
-; CHECK: ni 1(%r2), 1
+; CHECK-NOT: ni 1(%r2), 1
+; CHECK: llh %r0, 0(%r2)
+; CHECK: nilf %r0, 1
+; CHECK: sth %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i16, ptr %ptr
   %and = and i16 %val, 1
@@ -129,9 +128,13 @@ define void @f3_i16(ptr %ptr) {
 }
 
 ; Check the lowest useful constant for i32, expressed as an unsigned integer.
+; Constant 1 should not fold for i32, as it requires clearing the high bytes.
 define void @f3_i32(ptr %ptr) {
 ; CHECK-LABEL: f3_i32:
-; CHECK: ni 3(%r2), 1
+; CHECK-NOT: ni 3(%r2), 1
+; CHECK: lhi %r0, 1
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i32, ptr %ptr
   %and = and i32 %val, 1
@@ -140,9 +143,13 @@ define void @f3_i32(ptr %ptr) {
 }
 
 ; Check the lowest useful constant for i64, expressed as an unsigned integer.
+; Constant 1 should not fold for i64, as it requires clearing the high bytes.
 define void @f3_i64(ptr %ptr) {
 ; CHECK-LABEL: f3_i64:
-; CHECK: ni 7(%r2), 1
+; CHECK-NOT: ni 7(%r2), 1
+; CHECK: lg %r0, 0(%r2)
+; CHECK: risbg %r0, %r0, 63, 191, 0
+; CHECK: stg %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i64, ptr %ptr
   %and = and i64 %val, 1
@@ -162,9 +169,14 @@ define void @f4(ptr %ptr) {
 }
 
 ; Check the highest useful constant for i16, expressed as a unsigned integer.
+; Constant 254 (0x00FE) should not fold for i16 because the high byte must be
+; cleared.
 define void @f4_i16(ptr %ptr) {
 ; CHECK-LABEL: f4_i16:
-; CHECK: ni 1(%r2), 254
+; CHECK-NOT: ni 1(%r2), 254
+; CHECK: llh %r0, 0(%r2)
+; CHECK: nilf %r0, 254
+; CHECK: sth %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i16, ptr %ptr
   %and = and i16 %val, 254
@@ -173,9 +185,13 @@ define void @f4_i16(ptr %ptr) {
 }
 
 ; Check the highest useful constant for i32, expressed as a unsigned integer.
+; Constant 254 should not fold for i32 because the high byte must be cleared.
 define void @f4_i32(ptr %ptr) {
 ; CHECK-LABEL: f4_i32:
-; CHECK: ni 3(%r2), 254
+; CHECK-NOT: ni 3(%r2), 254
+; CHECK: lhi %r0, 254
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i32, ptr %ptr
   %and = and i32 %val, 254
@@ -184,9 +200,13 @@ define void @f4_i32(ptr %ptr) {
 }
 
 ; Check the highest useful constant for i64, expressed as a unsigned integer.
+; Constant 254 should not fold for i64 because the high byte must be cleared.
 define void @f4_i64(ptr %ptr) {
 ; CHECK-LABEL: f4_i64:
-; CHECK: ni 7(%r2), 254
+; CHECK-NOT: ni 7(%r2), 254
+; CHECK: lg %r0, 0(%r2)
+; CHECK: risbg %r0, %r0, 56, 190, 0
+; CHECK: stg %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i64, ptr %ptr
   %and = and i64 %val, 254
@@ -213,7 +233,7 @@ define void @f5_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -225,7 +245,7 @@ define void @f5_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -237,7 +257,7 @@ define void @f5_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -261,7 +281,7 @@ define void @f6_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4096
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -273,7 +293,7 @@ define void @f6_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4096
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -285,7 +305,7 @@ define void @f6_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 4096
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -309,7 +329,7 @@ define void @f7_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524287
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -321,7 +341,7 @@ define void @f7_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524287
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -333,7 +353,7 @@ define void @f7_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524287
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -361,7 +381,7 @@ define void @f8_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524288
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -375,7 +395,7 @@ define void @f8_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524288
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -389,7 +409,7 @@ define void @f8_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 524288
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -413,7 +433,7 @@ define void @f9_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -1
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -425,7 +445,7 @@ define void @f9_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -1
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -437,7 +457,7 @@ define void @f9_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -1
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -461,7 +481,7 @@ define void @f10_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524288
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -473,7 +493,7 @@ define void @f10_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524288
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -485,7 +505,7 @@ define void @f10_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524288
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -513,7 +533,7 @@ define void @f11_i16(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -527,7 +547,7 @@ define void @f11_i32(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -541,7 +561,7 @@ define void @f11_i64(ptr %src) {
 ; CHECK: br %r14
   %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -571,7 +591,7 @@ define void @f12_i16(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -586,7 +606,7 @@ define void @f12_i32(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -601,7 +621,7 @@ define void @f12_i64(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
   store i64 %and, ptr %ptr
   ret void
 }
@@ -631,7 +651,7 @@ define void @f13_i16(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4096
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
-  %and = and i16 %val, 127
+  %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
   ret void
 }
@@ -646,7 +666,7 @@ define void @f13_i32(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4096
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
-  %and = and i32 %val, 127
+  %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
   ret void
 }
@@ -661,7 +681,126 @@ define void @f13_i64(i64 %src, i64 %index) {
   %add2 = add i64 %add1, 4096
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
-  %and = and i64 %val, 127
+  %and = and i64 %val, -129
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check folding of multi-byte 'and' operations into byte-immediate memory
+; operation 'ni'.
+; Additional tests for immAndLSB8 PatLeaf logic - preservation of upper bytes
+; for i16, i32, and i64. Low/high constant (signed/unsigned) tests have already
+; been covered (f1 to f4).
+
+; Check i16 - should not fold. High byte has bit cleared.
+define void @f_const_i16_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i16_no_fold:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK: nilf [[REG]], 65278
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65278  ; 0xFEFE
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 - should not fold. Upper bytes have bit cleared.
+define void @f_const_i32_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi [[REG:%r[0-5]]], -258
+; CHECK: n [[REG]], 0(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967038  ; 0xFFFFFEFE
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 - should not fold. Upper bytes have bit cleared.
+define void @f_const_i64_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi [[REG:%r[0-5]]], -258
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -258  ; 0xFFFFFFFFFFFFFFFE
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i16 - should not fold. Constant 0xFE00 affects more than just the LSB.
+define void @f_const_i16_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i16_multi_byte_no_fold:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK: nilf [[REG]], 65024
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65024  ; 0xFE00
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 - should not fold. Constant 0xFFFFFE00 affects more than just the
+; LSB.
+define void @f_const_i32_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_multi_byte_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -512
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294966784  ; 0xFFFFFE00
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i32 - should not fold. Constant affects a non-LSB byte.
+define void @f_const_i32_wrong_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_wrong_byte_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: iilf [[REG:%r[0-5]]], 4294902015
+; CHECK: n [[REG]], 0(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294902015  ; 0xFFFF00FF
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 - should not fold. Constant affects more than just the LSB.
+define void @f_const_i64_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_multi_byte_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi [[REG:%r[0-5]]], -512
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -512  ; 0xFFFFFFFFFFFFFE00
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check i64 - should not fold. Constant affects a non-LSB byte.
+define void @f_const_i64_wrong_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_wrong_byte_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lgfi [[REG:%r[0-5]]], -65281
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -65281  ; 0xFFFFFFFFFFFF00FF
   store i64 %and, ptr %ptr
   ret void
 }

>From f7d18f453f5d0a2e3239fcd4baa846da3c9c2c69 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Tue, 2 Jun 2026 16:25:06 +0200
Subject: [PATCH 6/7] [SystemZ] Address code review feedback for folding
 i16/i32/i64 logical     RMW operations into memory operands    - Updated
 isFoldableRMW() in SystemZTTI cost model to check constant      immediate
 masks for logical ops on wider types, as required      by instruction hw
 capability.    - Implemented imm32ll8c and imm64ll8c in SystemZOperators.td. 
   - Added offset addition before selectAddress to avoid overflow,      and
 changes made to displacement tests, it might limit      optimization close to
 boundary edge displacement.    - Eliminated RMWIByteLSB.

---
 .../Target/SystemZ/SystemZISelDAGToDAG.cpp    |   3 +-
 llvm/lib/Target/SystemZ/SystemZOperands.td    |  61 +++----
 llvm/lib/Target/SystemZ/SystemZOperators.td   |  25 ---
 llvm/lib/Target/SystemZ/SystemZPatterns.td    |  77 ++++-----
 .../SystemZ/SystemZTargetTransformInfo.cpp    |  27 ++-
 .../CostModel/SystemZ/fold-rmw-cost.ll        | 160 ++++++++++++++++--
 llvm/test/CodeGen/SystemZ/and-05.ll           |  96 +++++------
 llvm/test/CodeGen/SystemZ/or-05.ll            |  96 +++++------
 llvm/test/CodeGen/SystemZ/xor-05.ll           |  96 +++++------
 9 files changed, 384 insertions(+), 257 deletions(-)

diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index 992ad39690fc8..cb09611db7d16 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -704,12 +704,11 @@ bool SystemZDAGToDAGISel::selectBDAddr(SystemZAddressingMode::DispRange DR,
                                        SDValue Addr, SDValue &Base,
                                        SDValue &Disp, int64_t Offset) const {
   SystemZAddressingMode AM(SystemZAddressingMode::FormBD, DR);
+  AM.Disp += Offset;
   if (!selectAddress(Addr, AM))
     return false;
 
-  AM.Disp += Offset;
   getAddressOperands(AM, Addr.getValueType(), Base, Disp);
-  Disp = CurDAG->getTargetConstant(AM.Disp, SDLoc(Addr), MVT::i64);
   return true;
 }
 
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 921b6ed5020fd..c1f62c036b283 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -444,6 +444,28 @@ defm imm64hh16c : Immediate<i64, [{
          SystemZ::isImmHH(uint64_t(~Imm.getZExtValue()));
 }], HH16, "U16Imm">;
 
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one
+// or zero-extended from a 16-bit.
+defm imm32ll8c : Immediate<i32, [{
+  if (!Imm.isIntN(32)) return false;
+  uint64_t Val = Imm.getZExtValue();
+  // Case 1: Upper 24 bits are all ones.
+  if ((Val & 0xffffff00ULL) == 0xffffff00ULL)
+    return true;
+  // Case 2: Zero-extended i16 where the upper 16 bits are zero and the 
+  // second least significant byte is 0xff (0x0000ff00).
+  if ((Val & 0xffffff00ULL) == 0x0000ff00ULL) {
+    return true;
+  }
+  return false;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
+defm imm64ll8c : Immediate<i64, [{
+  return Imm.isIntN(64) &&
+         (uint64_t(~Imm.getZExtValue()) & 0xffffffffffffff00ULL) == 0;
+}], UIMM8, "U8Imm">;
+
 // Immediates for the lower and upper 32 bits of an i64, with the other
 // bits of the i32 being zero.
 defm imm64lf32 : Immediate<i64, [{
@@ -681,6 +703,16 @@ def bdladdr12onlylen8 : BDLMode<"BDLAddr",  "64", "12", "Only", "8">;
 def bdraddr12only     : BDRMode<"BDRAddr",  "64", "12", "Only">;
 def bdvaddr12only     : BDVMode<            "64", "12">;
 
+// 12-bit unsigned displacement variants (for NI, OI, XI)
+def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
+def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
+def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
+
+// 20-bit signed displacement variants (for NIY, OIY, XIY)
+def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
+def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
+def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
+
 //===----------------------------------------------------------------------===//
 // Miscellaneous
 //===----------------------------------------------------------------------===//
@@ -694,32 +726,3 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
   let IsAPInt = true;
   let FastIselShouldIgnore = true;
 }
-
-// These patterns target the Least Significant Byte (LSB) of multi-byte 
-// integers (i16, i32, i64). On SystemZ (Big-Endian), the LSB is located at 
-// a fixed positive offset from the base address:
-// i16: offset +1 (byte 1 of 2)
-// i32: offset +3 (byte 3 of 4)
-// i64: offset +7 (byte 7 of 8)
-
-// 12-bit unsigned displacement variants (for NI, OI, XI)
-def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
-def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
-def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
-
-// 20-bit signed displacement variants (for NIY, OIY, XIY)
-def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
-def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
-def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
-
-// Normalizes the immediate to i32.
-def as_i32imm : SDNodeXForm<imm, [{
-  uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
-  return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
-}]>;
-
-// Extracts only the bottom 8 bits.
-def as_i8imm : SDNodeXForm<imm, [{
-  uint64_t Val = N->getZExtValue() & 0xFF;
-  return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
-}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index af14dbdd68a2a..d021ec742321f 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1373,28 +1373,3 @@ def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
 def immLSB8 : PatLeaf<(imm), [{
   return N->getAPIntValue().getActiveBits() <= 8;
 }]>;
-
-// Matches constants where all bits except the last byte are 1 (0xFF...XX).
-def immAndLSB8 : PatLeaf<(imm), [{
-  uint64_t Val = N->getZExtValue();
-  uint64_t Mask = 0;
-  uint64_t Size = N->getValueType(0).getSizeInBits();
-
-  switch (Size) {
-  case 16:
-    Mask = 0xff00ULL;
-    break;
-  case 32:
-    // Support both full i32 and zero-extended i16 constant.
-    Mask = ((Val & 0xffffff00ULL) == 0xffffff00ULL) ? 0xffffff00ULL
-                                                    : 0x0000ff00ULL;
-    break;
-  case 64:
-    Mask = 0xffffffffffffff00ULL;
-    break;
-  default:
-    return false;
-  }
-
-  return (Val & Mask) == Mask;
-}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index bc733f8260bef..9d92e88113c07 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -41,45 +41,10 @@ multiclass ZXB<SDPatternOperator operator, RegisterOperand cls,
 // of the second operand.
 class RMWI<SDPatternOperator load, SDPatternOperator operator,
            SDPatternOperator store, AddressingMode mode,
-           PatFrag imm, Instruction insn>
+           ImmLeaf imm, Instruction insn>
   : Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
         (insn mode:$addr, (UIMM8 imm:$src))>;
 
-// Handle RMW operations on the Least Significant Byte (LSB) of wider types.
-// This allows targeting byte-level logical instructions for i16, i32,
-// and i64 memory locations when only the last byte is modified.
-multiclass RMWIByteLSB<SDPatternOperator operator, 
-                       ComplexPattern mode12_1, ComplexPattern mode20_1,
-                       ComplexPattern mode12_3, ComplexPattern mode20_3,
-                       ComplexPattern mode12_7, ComplexPattern mode20_7,
-                       Instruction insn12, Instruction insn20,
-                       PatLeaf mask> {
-  // i16 LSB (+1 offset)
-  def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode12_1:$addr)),
-             (i32 mask:$imm))), mode12_1:$addr),
-            (insn12 mode12_1:$addr, (as_i8imm $imm))>;
-  def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode20_1:$addr)),
-             (i32 mask:$imm))), mode20_1:$addr),
-            (insn20 mode20_1:$addr, (as_i8imm $imm))>;
-
-
-  // i32 LSB (+3 offset)
-  def : Pat<(store (i32 (operator (i32 (load mode12_3:$addr)),
-             (i32 mask:$imm))), mode12_3:$addr),
-            (insn12 mode12_3:$addr, (as_i8imm $imm))>;
-  def : Pat<(store (i32 (operator (i32 (load mode20_3:$addr)),
-             (i32 mask:$imm))), mode20_3:$addr),
-            (insn20 mode20_3:$addr, (as_i8imm $imm))>;
-
-  // i64 LSB (+7 offset)
-  def : Pat<(store (i64 (operator (i64 (load mode12_7:$addr)),
-             (i64 mask:$imm))), mode12_7:$addr),
-            (insn12 mode12_7:$addr, (as_i8imm $imm))>;
-  def : Pat<(store (i64 (operator (i64 (load mode20_7:$addr)),
-             (i64 mask:$imm))), mode20_7:$addr),
-            (insn20 mode20_7:$addr, (as_i8imm $imm))>;
-}
-
 // Record that INSN performs binary operation OPERATION on a byte
 // memory location.  IMM is the type of the second operand.
 multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
@@ -93,13 +58,39 @@ multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
   def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
              insn20>;
 
-  // Conditional folding for wider types.
-  defm : RMWIByteLSB<operator,
-                     bdaddr12off1, bdaddr20off1,
-                     bdaddr12off3, bdaddr20off3,
-                     bdaddr12off7, bdaddr20off7,
-                     insn12, insn20,
-                     !if(!eq(operator, and), immAndLSB8, immLSB8)>;
+  // 12-bit instruction variants.
+  def : Pat<(truncstorei16 (i32 (operator
+               (i32 (anyextloadi16 bdaddr12off1:$addr)),
+               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+               bdaddr12off1:$addr),
+            (insn12 bdaddr12off1:$addr, (UIMM8 $imm))>;
+
+  def : Pat<(store (i32 (operator (i32 (load bdaddr12off3:$addr)),
+               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+               bdaddr12off3:$addr),
+            (insn12 bdaddr12off3:$addr, (UIMM8 $imm))>;
+
+  def : Pat<(store (i64 (operator (i64 (load bdaddr12off7:$addr)),
+               (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
+               bdaddr12off7:$addr),
+            (insn12 bdaddr12off7:$addr, (UIMM8 $imm))>;
+
+  // 20-bit instruction variants.
+  def : Pat<(truncstorei16 (i32 (operator
+               (i32 (anyextloadi16 bdaddr20off1:$addr)),
+               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+               bdaddr20off1:$addr),
+            (insn20 bdaddr20off1:$addr, (UIMM8 $imm))>;
+
+  def : Pat<(store (i32 (operator (i32 (load bdaddr20off3:$addr)),
+               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+               bdaddr20off3:$addr),
+            (insn20 bdaddr20off3:$addr, (UIMM8 $imm))>;
+
+  def : Pat<(store (i64 (operator (i64 (load bdaddr20off7:$addr)),
+               (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
+               bdaddr20off7:$addr),
+            (insn20 bdaddr20off7:$addr, (UIMM8 $imm))>;
 }
 
 // Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index b5887da06385a..435180198b790 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -554,10 +554,33 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
   switch (Opcode) {
   case Instruction::And:
   case Instruction::Or:
-  case Instruction::Xor:
-    if (BitWidth != 8 && BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
+  case Instruction::Xor: {
+    if (BitWidth == 8)
+      break;
+    if (BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
+      return false;
+
+    auto *CI = dyn_cast<ConstantInt>(I->getOperand(1));
+    if (!CI)
       return false;
+
+    uint64_t Val = CI->getZExtValue();
+    if (Opcode == Instruction::And) {
+      if (BitWidth == 16 && (Val & 0xff00ULL) != 0xff00ULL)
+        return false;
+      if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL &&
+          (Val & 0xffffff00ULL) != 0x0000ff00ULL)
+        return false;
+      if (BitWidth == 64 &&
+          (Val & 0xffffffffffffff00ULL) != 0xffffffffffffff00ULL)
+        return false;
+    } else {
+      if (CI->getValue().getActiveBits() > 8) {
+        return false;
+      }
+    }
     break;
+  }
   case Instruction::Add:
   case Instruction::Sub:
     if (BitWidth != 32 && BitWidth != 64)
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index f82ac31797274..c05fb3cd12a17 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -145,32 +145,168 @@ define void @test_vector_no_fold(<4 x i32> %val, ptr %p) {
   ret void
 }
 
-define void @test_xor_i16(ptr %p) {
-; CHECK-LABEL: 'test_xor_i16'
-; CHECK: cost of 0 {{.*}} xor i16
+; Logical operations for i16/i32/i64 types.
+
+; Modifies only LSB bits.
+define void @test_and_i16_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i16_valid'
+; CHECK: cost of 0 {{.*}} and i16 %v, -255
 ; CHECK: cost of 0 {{.*}} store i16
   %v = load i16, ptr %p
-  %res = xor i16 %v, 1
+  %res = and i16 %v, 65281 ; 0xff01
+  store i16 %res, ptr %p
+  ret void
+}
+
+; Clears bit 8, just above LSB window.
+define void @test_and_i16_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i16_invalid_above'
+; CHECK: cost of 1 {{.*}} and i16 %v, -511
+; CHECK: cost of 1 {{.*}} store i16
+  %v = load i16, ptr %p
+  %res = and i16 %v, 65025 ; 0xfe01
+  store i16 %res, ptr %p
+  ret void
+}
+
+; Active bits <=8.
+define void @test_or_i16_valid(ptr %p) {
+; CHECK-LABEL: 'test_or_i16_valid'
+; CHECK: cost of 0 {{.*}} or i16 %v, 255
+; CHECK: cost of 0 {{.*}} store i16
+  %v = load i16, ptr %p
+  %res = or i16 %v, 255 ; 0x00ff
+  store i16 %res, ptr %p
+  ret void
+}
+
+; Active bits > 8.
+define void @test_or_i16_invalid(ptr %p) {
+; CHECK-LABEL: 'test_or_i16_invalid'
+; CHECK: cost of 1 {{.*}} or i16 %v, 256
+; CHECK: cost of 1 {{.*}} store i16
+  %v = load i16, ptr %p
+  %res = or i16 %v, 256 ; 0x0100
   store i16 %res, ptr %p
   ret void
 }
 
-define void @test_or_i32(ptr %p) {
-; CHECK-LABEL: 'test_or_i32'
-; CHECK: cost of 0 {{.*}} or i32
+; Modifies only LSB bits.
+define void @test_and_i32_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_valid'
+; CHECK: cost of 0 {{.*}} and i32 %v, -255
 ; CHECK: cost of 0 {{.*}} store i32
   %v = load i32, ptr %p
-  %res = or i32 %v, 1
+  %res = and i32 %v, 4294967041 ; 0xffffff01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; Zero-extended i16 context.
+define void @test_and_i32_zext_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_valid'
+; CHECK: cost of 0 {{.*}} and i32 %v, 65281
+; CHECK: cost of 0 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = and i32 %v, 65281 ; 0x0000ff01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; One bit down from zero-extended threshold.
+define void @test_and_i32_invalid_zext_below(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid_zext_below'
+; CHECK: cost of 1 {{.*}} and i32 %v, 65025
+; CHECK: cost of 1 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = and i32 %v, 65025 ; 0x0000fe01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; One bit above zero-extended threshold.
+define void @test_and_i32_invalid_zext_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid_zext_above'
+; CHECK: cost of 1 {{.*}} and i32 %v, 130817
+; CHECK: cost of 1 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = and i32 %v, 130817 ; 0x0001ff01
   store i32 %res, ptr %p
   ret void
 }
 
-define void @test_and_i64(ptr %p) {
-; CHECK-LABEL: 'test_and_i64'
-; CHECK: cost of 0 {{.*}} and i64
+; Modifies bits outside LSB.
+define void @test_and_i32_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid'
+; CHECK: cost of 1 {{.*}} and i32 %v, 16
+; CHECK: cost of 1 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = and i32 %v, 16
+  store i32 %res, ptr %p
+  ret void
+}
+
+; Active bits <=8.
+define void @test_xor_i32_valid(ptr %p) {
+; CHECK-LABEL: 'test_xor_i32_valid'
+; CHECK: cost of 0 {{.*}} xor i32 %v, 255
+; CHECK: cost of 0 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = xor i32 %v, 255 ; 0x000000ff
+  store i32 %res, ptr %p
+  ret void
+}
+
+; Active bits > 8.
+define void @test_xor_i32_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_xor_i32_invalid_above'
+; CHECK: cost of 1 {{.*}} xor i32 %v, 256
+; CHECK: cost of 1 {{.*}} store i32
+  %v = load i32, ptr %p
+  %res = xor i32 %v, 256 ; 0x00000100
+  store i32 %res, ptr %p
+  ret void
+}
+
+; Modifies only LSB bits.
+define void @test_and_i64_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i64_valid'
+; CHECK: cost of 0 {{.*}} and i64 %v, -255
 ; CHECK: cost of 0 {{.*}} store i64
   %v = load i64, ptr %p
-  %res = and i64 %v, 1
+  %res = and i64 %v, -255 ; 0xffffffffffffff01
+  store i64 %res, ptr %p
+  ret void
+}
+
+; Clears bit 8.
+define void @test_and_i64_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i64_invalid_above'
+; CHECK: cost of 1 {{.*}} and i64 %v, -511
+; CHECK: cost of 1 {{.*}} store i64
+  %v = load i64, ptr %p
+  %res = and i64 %v, -511 ; 0xfffffffffffffffe01
+  store i64 %res, ptr %p
+  ret void
+}
+
+; Active bits <=8.
+define void @test_or_i64_valid(ptr %p) {
+; CHECK-LABEL: 'test_or_i64_valid'
+; CHECK: cost of 0 {{.*}} or i64 %v, 255
+; CHECK: cost of 0 {{.*}} store i64
+  %v = load i64, ptr %p
+  %res = or i64 %v, 255 ; 0x00000000000000ff
+  store i64 %res, ptr %p
+  ret void
+}
+
+; Active bits > 8.
+define void @test_or_i64_invalid(ptr %p) {
+; CHECK-LABEL: 'test_or_i64_invalid'
+; CHECK: cost of 1 {{.*}} or i64 %v, 256
+  %v = load i64, ptr %p
+  %res = or i64 %v, 256 ; 0x0000000000000100
   store i64 %res, ptr %p
   ret void
 }
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index f181c6f9a9c4f..56e3871683857 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -229,9 +229,9 @@ define void @f5(ptr %src) {
 ; Check the high end of the NI range for i16.
 define void @f5_i16(ptr %src) {
 ; CHECK-LABEL: f5_i16:
-; CHECK: ni 4096(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4094
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -241,9 +241,9 @@ define void @f5_i16(ptr %src) {
 ; Check the high end of the NI range for i32.
 define void @f5_i32(ptr %src) {
 ; CHECK-LABEL: f5_i32:
-; CHECK: ni 4098(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4092
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -253,9 +253,9 @@ define void @f5_i32(ptr %src) {
 ; Check the high end of the NI range for i64.
 define void @f5_i64(ptr %src) {
 ; CHECK-LABEL: f5_i64:
-; CHECK: ni 4102(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4088
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -277,9 +277,9 @@ define void @f6(ptr %src) {
 ; Check the next byte up for i16, which should use NIY instead of NI.
 define void @f6_i16(ptr %src) {
 ; CHECK-LABEL: f6_i16:
-; CHECK: niy 4097(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -289,9 +289,9 @@ define void @f6_i16(ptr %src) {
 ; Check the next byte up for i32, which should use NIY instead of NI.
 define void @f6_i32(ptr %src) {
 ; CHECK-LABEL: f6_i32:
-; CHECK: niy 4099(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4093
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -301,9 +301,9 @@ define void @f6_i32(ptr %src) {
 ; Check the next byte up for i64, which should use NIY instead of NI.
 define void @f6_i64(ptr %src) {
 ; CHECK-LABEL: f6_i64:
-; CHECK: niy 4103(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4089
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -325,9 +325,9 @@ define void @f7(ptr %src) {
 ; Check the high end of the NIY range for i16.
 define void @f7_i16(ptr %src) {
 ; CHECK-LABEL: f7_i16:
-; CHECK: niy 524288(%r2), 127
+; CHECK: niy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524286
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -337,9 +337,9 @@ define void @f7_i16(ptr %src) {
 ; Check the high end of the NIY range for i32.
 define void @f7_i32(ptr %src) {
 ; CHECK-LABEL: f7_i32:
-; CHECK: niy 524290(%r2), 127
+; CHECK: niy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524284
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -349,9 +349,9 @@ define void @f7_i32(ptr %src) {
 ; Check the high end of the NIY range for i64.
 define void @f7_i64(ptr %src) {
 ; CHECK-LABEL: f7_i64:
-; CHECK: niy 524294(%r2), 127
+; CHECK: niy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524280
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -429,9 +429,9 @@ define void @f9(ptr %src) {
 ; Check the high end of the negative NIY range for i16.
 define void @f9_i16(ptr %src) {
 ; CHECK-LABEL: f9_i16:
-; CHECK: niy 0(%r2), 127
+; CHECK: niy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -2
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -441,9 +441,9 @@ define void @f9_i16(ptr %src) {
 ; Check the high end of the negative NIY range for i32.
 define void @f9_i32(ptr %src) {
 ; CHECK-LABEL: f9_i32:
-; CHECK: niy 2(%r2), 127
+; CHECK: niy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -4
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -453,9 +453,9 @@ define void @f9_i32(ptr %src) {
 ; Check the high end of the negative NIY range for i64.
 define void @f9_i64(ptr %src) {
 ; CHECK-LABEL: f9_i64:
-; CHECK: niy 6(%r2), 127
+; CHECK: niy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -8
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -477,9 +477,9 @@ define void @f10(ptr %src) {
 ; Check the low end of the NIY range for i16.
 define void @f10_i16(ptr %src) {
 ; CHECK-LABEL: f10_i16:
-; CHECK: niy -524287(%r2), 127
+; CHECK: niy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -489,9 +489,9 @@ define void @f10_i16(ptr %src) {
 ; Check the low end of the NIY range for i32.
 define void @f10_i32(ptr %src) {
 ; CHECK-LABEL: f10_i32:
-; CHECK: niy -524285(%r2), 127
+; CHECK: niy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524291
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -501,9 +501,9 @@ define void @f10_i32(ptr %src) {
 ; Check the low end of the NIY range for i64.
 define void @f10_i64(ptr %src) {
 ; CHECK-LABEL: f10_i64:
-; CHECK: niy -524281(%r2), 127
+; CHECK: niy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524295
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -528,10 +528,10 @@ define void @f11(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i16(ptr %src) {
 ; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
 ; CHECK: ni 1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524290
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
   store i16 %and, ptr %ptr
@@ -542,10 +542,10 @@ define void @f11_i16(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i32(ptr %src) {
 ; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
 ; CHECK: ni 3(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524292
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
   store i32 %and, ptr %ptr
@@ -556,10 +556,10 @@ define void @f11_i32(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i64(ptr %src) {
 ; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
 ; CHECK: ni 7(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524296
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
   store i64 %and, ptr %ptr
@@ -585,10 +585,10 @@ define void @f12(i64 %src, i64 %index) {
 define void @f12_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: ni 4096(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4094
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
@@ -600,10 +600,10 @@ define void @f12_i16(i64 %src, i64 %index) {
 define void @f12_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: ni 4098(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4092
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
@@ -615,10 +615,10 @@ define void @f12_i32(i64 %src, i64 %index) {
 define void @f12_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: ni 4102(%r2), 127
+; CHECK: ni 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4088
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
@@ -645,10 +645,10 @@ define void @f13(i64 %src, i64 %index) {
 define void @f13_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: niy 4097(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %and = and i16 %val, 65407
@@ -660,10 +660,10 @@ define void @f13_i16(i64 %src, i64 %index) {
 define void @f13_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: niy 4099(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4093
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %and = and i32 %val, 4294967167
@@ -675,10 +675,10 @@ define void @f13_i32(i64 %src, i64 %index) {
 define void @f13_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: niy 4103(%r2), 127
+; CHECK: niy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4089
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %and = and i64 %val, -129
diff --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index e6b05a77f1f6f..84dfab8a2ef56 100644
--- a/llvm/test/CodeGen/SystemZ/or-05.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05.ll
@@ -209,9 +209,9 @@ define void @f5(ptr %src) {
 ; Check the high end of the OI range for i16.
 define void @f5_i16(ptr %src) {
 ; CHECK-LABEL: f5_i16:
-; CHECK: oi 4096(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4094
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -221,9 +221,9 @@ define void @f5_i16(ptr %src) {
 ; Check the high end of the OI range for i32.
 define void @f5_i32(ptr %src) {
 ; CHECK-LABEL: f5_i32:
-; CHECK: oi 4098(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4092
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -233,9 +233,9 @@ define void @f5_i32(ptr %src) {
 ; Check the high end of the OI range for i64.
 define void @f5_i64(ptr %src) {
 ; CHECK-LABEL: f5_i64:
-; CHECK: oi 4102(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4088
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -257,9 +257,9 @@ define void @f6(ptr %src) {
 ; Check the next byte up for i16, which should use OIY instead of OI.
 define void @f6_i16(ptr %src) {
 ; CHECK-LABEL: f6_i16:
-; CHECK: oiy 4097(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -269,9 +269,9 @@ define void @f6_i16(ptr %src) {
 ; Check the next byte up for i32, which should use OIY instead of OI.
 define void @f6_i32(ptr %src) {
 ; CHECK-LABEL: f6_i32:
-; CHECK: oiy 4099(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4093
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -281,9 +281,9 @@ define void @f6_i32(ptr %src) {
 ; Check the next byte up for i64, which should use OIY instead of OI.
 define void @f6_i64(ptr %src) {
 ; CHECK-LABEL: f6_i64:
-; CHECK: oiy 4103(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4089
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -305,9 +305,9 @@ define void @f7(ptr %src) {
 ; Check the high end of the OIY range for i16.
 define void @f7_i16(ptr %src) {
 ; CHECK-LABEL: f7_i16:
-; CHECK: oiy 524288(%r2), 127
+; CHECK: oiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524286
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -317,9 +317,9 @@ define void @f7_i16(ptr %src) {
 ; Check the high end of the OIY range for i32.
 define void @f7_i32(ptr %src) {
 ; CHECK-LABEL: f7_i32:
-; CHECK: oiy 524290(%r2), 127
+; CHECK: oiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524284
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -329,9 +329,9 @@ define void @f7_i32(ptr %src) {
 ; Check the high end of the OIY range for i64.
 define void @f7_i64(ptr %src) {
 ; CHECK-LABEL: f7_i64:
-; CHECK: oiy 524294(%r2), 127
+; CHECK: oiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524280
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -409,9 +409,9 @@ define void @f9(ptr %src) {
 ; Check the high end of the negative OIY range for i16.
 define void @f9_i16(ptr %src) {
 ; CHECK-LABEL: f9_i16:
-; CHECK: oiy 0(%r2), 127
+; CHECK: oiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -2
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -421,9 +421,9 @@ define void @f9_i16(ptr %src) {
 ; Check the high end of the negative OIY range for i32.
 define void @f9_i32(ptr %src) {
 ; CHECK-LABEL: f9_i32:
-; CHECK: oiy 2(%r2), 127
+; CHECK: oiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -4
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -433,9 +433,9 @@ define void @f9_i32(ptr %src) {
 ; Check the high end of the negative OIY range for i64.
 define void @f9_i64(ptr %src) {
 ; CHECK-LABEL: f9_i64:
-; CHECK: oiy 6(%r2), 127
+; CHECK: oiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -8
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -457,9 +457,9 @@ define void @f10(ptr %src) {
 ; Check the low end of the OIY range for i16.
 define void @f10_i16(ptr %src) {
 ; CHECK-LABEL: f10_i16:
-; CHECK: oiy -524287(%r2), 127
+; CHECK: oiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -469,9 +469,9 @@ define void @f10_i16(ptr %src) {
 ; Check the low end of the OIY range for i32.
 define void @f10_i32(ptr %src) {
 ; CHECK-LABEL: f10_i32:
-; CHECK: oiy -524285(%r2), 127
+; CHECK: oiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524291
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -481,9 +481,9 @@ define void @f10_i32(ptr %src) {
 ; Check the low end of the OIY range for i64.
 define void @f10_i64(ptr %src) {
 ; CHECK-LABEL: f10_i64:
-; CHECK: oiy -524281(%r2), 127
+; CHECK: oiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524295
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -508,10 +508,10 @@ define void @f11(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i16(ptr %src) {
 ; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
 ; CHECK: oi 1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524290
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
   store i16 %or, ptr %ptr
@@ -522,10 +522,10 @@ define void @f11_i16(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i32(ptr %src) {
 ; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
 ; CHECK: oi 3(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524292
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
   store i32 %or, ptr %ptr
@@ -536,10 +536,10 @@ define void @f11_i32(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i64(ptr %src) {
 ; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
 ; CHECK: oi 7(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524296
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
   store i64 %or, ptr %ptr
@@ -565,10 +565,10 @@ define void @f12(i64 %src, i64 %index) {
 define void @f12_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: oi 4096(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4094
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
@@ -580,10 +580,10 @@ define void @f12_i16(i64 %src, i64 %index) {
 define void @f12_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: oi 4098(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4092
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
@@ -595,10 +595,10 @@ define void @f12_i32(i64 %src, i64 %index) {
 define void @f12_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: oi 4102(%r2), 127
+; CHECK: oi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4088
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
@@ -625,10 +625,10 @@ define void @f13(i64 %src, i64 %index) {
 define void @f13_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: oiy 4097(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %or = or i16 %val, 127
@@ -640,10 +640,10 @@ define void @f13_i16(i64 %src, i64 %index) {
 define void @f13_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: oiy 4099(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4093
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %or = or i32 %val, 127
@@ -655,10 +655,10 @@ define void @f13_i32(i64 %src, i64 %index) {
 define void @f13_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: oiy 4103(%r2), 127
+; CHECK: oiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4089
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %or = or i64 %val, 127
diff --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 50bc759837d79..52c7788b75509 100644
--- a/llvm/test/CodeGen/SystemZ/xor-05.ll
+++ b/llvm/test/CodeGen/SystemZ/xor-05.ll
@@ -209,9 +209,9 @@ define void @f5(ptr %src) {
 ; Check the high end of the XI range for i16.
 define void @f5_i16(ptr %src) {
 ; CHECK-LABEL: f5_i16:
-; CHECK: xi 4096(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4094
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -221,9 +221,9 @@ define void @f5_i16(ptr %src) {
 ; Check the high end of the XI range for i32.
 define void @f5_i32(ptr %src) {
 ; CHECK-LABEL: f5_i32:
-; CHECK: xi 4098(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4092
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -233,9 +233,9 @@ define void @f5_i32(ptr %src) {
 ; Check the high end of the XI range for i64.
 define void @f5_i64(ptr %src) {
 ; CHECK-LABEL: f5_i64:
-; CHECK: xi 4102(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4095
+  %ptr = getelementptr i8, ptr %src, i64 4088
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -257,9 +257,9 @@ define void @f6(ptr %src) {
 ; Check the next byte up for i16, which should use XIY instead of XI.
 define void @f6_i16(ptr %src) {
 ; CHECK-LABEL: f6_i16:
-; CHECK: xiy 4097(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4095
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -269,9 +269,9 @@ define void @f6_i16(ptr %src) {
 ; Check the next byte up for i32, which should use XIY instead of XI.
 define void @f6_i32(ptr %src) {
 ; CHECK-LABEL: f6_i32:
-; CHECK: xiy 4099(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4093
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -281,9 +281,9 @@ define void @f6_i32(ptr %src) {
 ; Check the next byte up for i64, which should use XIY instead of XI.
 define void @f6_i64(ptr %src) {
 ; CHECK-LABEL: f6_i64:
-; CHECK: xiy 4103(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 4096
+  %ptr = getelementptr i8, ptr %src, i64 4089
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -305,9 +305,9 @@ define void @f7(ptr %src) {
 ; Check the high end of the XIY range for i16.
 define void @f7_i16(ptr %src) {
 ; CHECK-LABEL: f7_i16:
-; CHECK: xiy 524288(%r2), 127
+; CHECK: xiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524286
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -317,9 +317,9 @@ define void @f7_i16(ptr %src) {
 ; Check the high end of the XIY range for i32.
 define void @f7_i32(ptr %src) {
 ; CHECK-LABEL: f7_i32:
-; CHECK: xiy 524290(%r2), 127
+; CHECK: xiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524284
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -329,9 +329,9 @@ define void @f7_i32(ptr %src) {
 ; Check the high end of the XIY range for i64.
 define void @f7_i64(ptr %src) {
 ; CHECK-LABEL: f7_i64:
-; CHECK: xiy 524294(%r2), 127
+; CHECK: xiy 524287(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 524287
+  %ptr = getelementptr i8, ptr %src, i64 524280
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -409,9 +409,9 @@ define void @f9(ptr %src) {
 ; Check the high end of the negative XIY range for i16.
 define void @f9_i16(ptr %src) {
 ; CHECK-LABEL: f9_i16:
-; CHECK: xiy 0(%r2), 127
+; CHECK: xiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -2
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -421,9 +421,9 @@ define void @f9_i16(ptr %src) {
 ; Check the high end of the negative XIY range for i32.
 define void @f9_i32(ptr %src) {
 ; CHECK-LABEL: f9_i32:
-; CHECK: xiy 2(%r2), 127
+; CHECK: xiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -4
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -433,9 +433,9 @@ define void @f9_i32(ptr %src) {
 ; Check the high end of the negative XIY range for i64.
 define void @f9_i64(ptr %src) {
 ; CHECK-LABEL: f9_i64:
-; CHECK: xiy 6(%r2), 127
+; CHECK: xiy -1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -1
+  %ptr = getelementptr i8, ptr %src, i64 -8
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -457,9 +457,9 @@ define void @f10(ptr %src) {
 ; Check the low end of the XIY range for i16.
 define void @f10_i16(ptr %src) {
 ; CHECK-LABEL: f10_i16:
-; CHECK: xiy -524287(%r2), 127
+; CHECK: xiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524289
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -469,9 +469,9 @@ define void @f10_i16(ptr %src) {
 ; Check the low end of the XIY range for i32.
 define void @f10_i32(ptr %src) {
 ; CHECK-LABEL: f10_i32:
-; CHECK: xiy -524285(%r2), 127
+; CHECK: xiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524291
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -481,9 +481,9 @@ define void @f10_i32(ptr %src) {
 ; Check the low end of the XIY range for i64.
 define void @f10_i64(ptr %src) {
 ; CHECK-LABEL: f10_i64:
-; CHECK: xiy -524281(%r2), 127
+; CHECK: xiy -524288(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524288
+  %ptr = getelementptr i8, ptr %src, i64 -524295
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -508,10 +508,10 @@ define void @f11(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i16(ptr %src) {
 ; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
 ; CHECK: xi 1(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524290
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
   store i16 %xor, ptr %ptr
@@ -522,10 +522,10 @@ define void @f11_i16(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i32(ptr %src) {
 ; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
 ; CHECK: xi 3(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524292
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
   store i32 %xor, ptr %ptr
@@ -536,10 +536,10 @@ define void @f11_i32(ptr %src) {
 ; Other sequences besides this one would be OK.
 define void @f11_i64(ptr %src) {
 ; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
 ; CHECK: xi 7(%r2), 127
 ; CHECK: br %r14
-  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %ptr = getelementptr i8, ptr %src, i64 -524296
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
   store i64 %xor, ptr %ptr
@@ -565,10 +565,10 @@ define void @f12(i64 %src, i64 %index) {
 define void @f12_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: xi 4096(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4094
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
@@ -580,10 +580,10 @@ define void @f12_i16(i64 %src, i64 %index) {
 define void @f12_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: xi 4098(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4092
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
@@ -595,10 +595,10 @@ define void @f12_i32(i64 %src, i64 %index) {
 define void @f12_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f12_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: xi 4102(%r2), 127
+; CHECK: xi 4095(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4095
+  %add2 = add i64 %add1, 4088
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127
@@ -625,10 +625,10 @@ define void @f13(i64 %src, i64 %index) {
 define void @f13_i16(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i16:
 ; CHECK: agr %r2, %r3
-; CHECK: xiy 4097(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4095
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i16, ptr %ptr
   %xor = xor i16 %val, 127
@@ -640,10 +640,10 @@ define void @f13_i16(i64 %src, i64 %index) {
 define void @f13_i32(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i32:
 ; CHECK: agr %r2, %r3
-; CHECK: xiy 4099(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4093
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i32, ptr %ptr
   %xor = xor i32 %val, 127
@@ -655,10 +655,10 @@ define void @f13_i32(i64 %src, i64 %index) {
 define void @f13_i64(i64 %src, i64 %index) {
 ; CHECK-LABEL: f13_i64:
 ; CHECK: agr %r2, %r3
-; CHECK: xiy 4103(%r2), 127
+; CHECK: xiy 4096(%r2), 127
 ; CHECK: br %r14
   %add1 = add i64 %src, %index
-  %add2 = add i64 %add1, 4096
+  %add2 = add i64 %add1, 4089
   %ptr = inttoptr i64 %add2 to ptr
   %val = load i64, ptr %ptr
   %xor = xor i64 %val, 127

>From 311b2a02c2fbe89bf5df8e2d69533288b8f749a2 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Sun, 7 Jun 2026 18:56:01 +0200
Subject: [PATCH 7/7] [SystemZ] Address code review feedback for folding
 i16/i32/i64           logical RMW operation.   - Fix a bug where 32-bit AND
 operations using a zero-extended i16     mask (e.g., 0x0000ff00) were matched
 against byte-level imm (NI).     Updated SystemZTTI CostModel with the same. 
  - Split the TableGen imm32ll8c predicate into separate imm32ll8c     and
 imm16ll8c variants.   - Restructured RMWIByte multiclass in terms of
 RMWIOffset helper     class.

---
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td   |  6 +-
 llvm/lib/Target/SystemZ/SystemZOperands.td    | 21 +++--
 llvm/lib/Target/SystemZ/SystemZPatterns.td    | 86 +++++++++----------
 .../SystemZ/SystemZTargetTransformInfo.cpp    |  3 +-
 .../CostModel/SystemZ/fold-rmw-cost.ll        | 16 ++--
 llvm/test/CodeGen/SystemZ/and-05.ll           |  2 +-
 6 files changed, 66 insertions(+), 68 deletions(-)

diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 2832dadbb585e..592324e9e748c 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1316,7 +1316,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm NC : MemorySS<"nc", 0xD4, z_nc>;
 }
-defm : RMWIByte<and, NI, NIY>;
+defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm64ll8c>;
 
 //===----------------------------------------------------------------------===//
 // OR
@@ -1372,7 +1372,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm OC : MemorySS<"oc", 0xD6, z_oc>;
 }
-defm : RMWIByte<or,  OI, OIY>;
+defm : RMWIByte<or,  OI, OIY, immLSB8, immLSB8, immLSB8>;
 
 //===----------------------------------------------------------------------===//
 // XOR
@@ -1411,7 +1411,7 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm XC : MemorySS<"xc", 0xD7, z_xc>;
 }
-defm : RMWIByte<xor, XI, XIY>;
+defm : RMWIByte<xor, XI, XIY, immLSB8, immLSB8, immLSB8>;
 
 //===----------------------------------------------------------------------===//
 // Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index c1f62c036b283..46d7b25c0209c 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -444,20 +444,19 @@ defm imm64hh16c : Immediate<i64, [{
          SystemZ::isImmHH(uint64_t(~Imm.getZExtValue()));
 }], HH16, "U16Imm">;
 
-// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one
-// or zero-extended from a 16-bit.
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one.
 defm imm32ll8c : Immediate<i32, [{
   if (!Imm.isIntN(32)) return false;
   uint64_t Val = Imm.getZExtValue();
-  // Case 1: Upper 24 bits are all ones.
-  if ((Val & 0xffffff00ULL) == 0xffffff00ULL)
-    return true;
-  // Case 2: Zero-extended i16 where the upper 16 bits are zero and the 
-  // second least significant byte is 0xff (0x0000ff00).
-  if ((Val & 0xffffff00ULL) == 0x0000ff00ULL) {
-    return true;
-  }
-  return false;
+  return (Val & 0xffffff00ULL) == 0xffffff00ULL;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the second 8-bit LSB chunk of an i32, with the other bits
+// being one.
+defm imm16ll8c : Immediate<i32, [{
+  if (!Imm.isIntN(32)) return false;
+  uint64_t Val = Imm.getZExtValue();
+  return (Val & 0xff00ULL) == 0xff00ULL;
 }], UIMM8, "U8Imm">;
 
 // Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 9d92e88113c07..3b115b1fbd690 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -45,52 +45,52 @@ class RMWI<SDPatternOperator load, SDPatternOperator operator,
   : Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
         (insn mode:$addr, (UIMM8 imm:$src))>;
 
+// Helpers for the ComplexPattern offset variants.
+// 16-bit truncation operations variant (offset 1).
+class RMWIOffset16<SDPatternOperator load, SDPatternOperator operator,
+                   ComplexPattern mode, PatFrag imm, Instruction insn>
+  : Pat<(truncstorei16 (i32 (operator (i32 (load mode:$addr)), imm:$src)),
+                       mode:$addr),
+        (insn mode:$addr, (UIMM8 $src))>;
+
+// 32-bit operations variant (offsets 3).
+class RMWIOffset32<SDPatternOperator load, SDPatternOperator operator,
+                   SDPatternOperator store, ComplexPattern mode,
+                   PatFrag imm, Instruction insn>
+  : Pat<(store (i32 (operator (i32 (load mode:$addr)), imm:$src)), mode:$addr),
+        (insn mode:$addr, (UIMM8 $src))>;
+
+// 64-bit operations variant (offset 7).
+class RMWIOffset64<SDPatternOperator load, SDPatternOperator operator,
+                   SDPatternOperator store, ComplexPattern mode,
+                   PatFrag imm, Instruction insn>
+  : Pat<(store (i64 (operator (i64 (load mode:$addr)), imm:$src)), mode:$addr),
+        (insn mode:$addr, (UIMM8 $src))>;
+
 // Record that INSN performs binary operation OPERATION on a byte
 // memory location.  IMM is the type of the second operand.
 multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
-                    Instruction insn20> {
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm32,
-             insn12>;
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm64,
-             insn12>;
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm32,
-             insn20>;
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
-             insn20>;
-
-  // 12-bit instruction variants.
-  def : Pat<(truncstorei16 (i32 (operator
-               (i32 (anyextloadi16 bdaddr12off1:$addr)),
-               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
-               bdaddr12off1:$addr),
-            (insn12 bdaddr12off1:$addr, (UIMM8 $imm))>;
-
-  def : Pat<(store (i32 (operator (i32 (load bdaddr12off3:$addr)),
-               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
-               bdaddr12off3:$addr),
-            (insn12 bdaddr12off3:$addr, (UIMM8 $imm))>;
-
-  def : Pat<(store (i64 (operator (i64 (load bdaddr12off7:$addr)),
-               (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
-               bdaddr12off7:$addr),
-            (insn12 bdaddr12off7:$addr, (UIMM8 $imm))>;
-
-  // 20-bit instruction variants.
-  def : Pat<(truncstorei16 (i32 (operator
-               (i32 (anyextloadi16 bdaddr20off1:$addr)),
-               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
-               bdaddr20off1:$addr),
-            (insn20 bdaddr20off1:$addr, (UIMM8 $imm))>;
-
-  def : Pat<(store (i32 (operator (i32 (load bdaddr20off3:$addr)),
-               (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
-               bdaddr20off3:$addr),
-            (insn20 bdaddr20off3:$addr, (UIMM8 $imm))>;
-
-  def : Pat<(store (i64 (operator (i64 (load bdaddr20off7:$addr)),
-               (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
-               bdaddr20off7:$addr),
-            (insn20 bdaddr20off7:$addr, (UIMM8 $imm))>;
+                    Instruction insn20, PatFrag imm16Leaf,
+                    PatFrag imm32Leaf, PatFrag imm64Leaf> {
+  // Base register-displacement pairs.
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+             imm32, insn12>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+             imm64, insn12>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+             imm32, insn20>;
+  def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+             imm64, insn20>;
+
+  // 12-bit instruction offset variants.
+  def : RMWIOffset16<anyextloadi16, operator, bdaddr12off1, imm16Leaf, insn12>;
+  def : RMWIOffset32<load, operator, store, bdaddr12off3, imm32Leaf, insn12>;
+  def : RMWIOffset64<load, operator, store, bdaddr12off7, imm64Leaf, insn12>;
+
+  // 20-bit instruction offset variants.
+  def : RMWIOffset16<anyextloadi16, operator, bdaddr20off1, imm16Leaf, insn20>;
+  def : RMWIOffset32<load, operator, store, bdaddr20off3, imm32Leaf, insn20>;
+  def : RMWIOffset64<load, operator, store, bdaddr20off7, imm64Leaf, insn20>;
 }
 
 // Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index 435180198b790..0c51f0e26b97d 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -568,8 +568,7 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
     if (Opcode == Instruction::And) {
       if (BitWidth == 16 && (Val & 0xff00ULL) != 0xff00ULL)
         return false;
-      if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL &&
-          (Val & 0xffffff00ULL) != 0x0000ff00ULL)
+      if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL)
         return false;
       if (BitWidth == 64 &&
           (Val & 0xffffffffffffff00ULL) != 0xffffffffffffff00ULL)
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index c05fb3cd12a17..c4ef9734814cc 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -203,10 +203,10 @@ define void @test_and_i32_valid(ptr %p) {
 }
 
 ; Zero-extended i16 context.
-define void @test_and_i32_zext_valid(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_zext_valid'
-; CHECK: cost of 0 {{.*}} and i32 %v, 65281
-; CHECK: cost of 0 {{.*}} store i32
+define void @test_and_i32_zext_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_invalid'
+; CHECK: cost of 1 {{.*}} and i32 %v, 65281
+; CHECK: cost of 1 {{.*}} store i32
   %v = load i32, ptr %p
   %res = and i32 %v, 65281 ; 0x0000ff01
   store i32 %res, ptr %p
@@ -214,8 +214,8 @@ define void @test_and_i32_zext_valid(ptr %p) {
 }
 
 ; One bit down from zero-extended threshold.
-define void @test_and_i32_invalid_zext_below(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_invalid_zext_below'
+define void @test_and_i32_zext_below_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_below_invalid'
 ; CHECK: cost of 1 {{.*}} and i32 %v, 65025
 ; CHECK: cost of 1 {{.*}} store i32
   %v = load i32, ptr %p
@@ -225,8 +225,8 @@ define void @test_and_i32_invalid_zext_below(ptr %p) {
 }
 
 ; One bit above zero-extended threshold.
-define void @test_and_i32_invalid_zext_above(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_invalid_zext_above'
+define void @test_and_i32_zext_above_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_above_invalid'
 ; CHECK: cost of 1 {{.*}} and i32 %v, 130817
 ; CHECK: cost of 1 {{.*}} store i32
   %v = load i32, ptr %p
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 56e3871683857..7468ac2ac8219 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -82,7 +82,7 @@ define void @f2_i32(ptr %ptr) {
 ; Check i32 with zero-extended i16 pattern (0x0000FFxx).
 define void @f2_i32_zext(ptr %ptr) {
 ; CHECK-LABEL: f2_i32_zext:
-; CHECK: ni 3(%r2), 254
+; CHECK: n %r0, 0(%r2)
 ; CHECK: br %r14
   %val = load i32, ptr %ptr
   %and = and i32 %val, 65534  ; 0x0000FFFE



More information about the llvm-commits mailing list