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

via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 18 10:36:23 PDT 2026


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

Extend tryFoldLoadStoreIntoMemOperand to add support for folding logical operations with 8-bit immediates into memory operands for i16, i32, and i64 types to target LSB. It handles both 12-bit (NI, OI, XI) and 20-bit signed (NIY, OIY, XIY) displacements.


>From fc6426ca2b73d04e16fc823039301679ffa09e5f Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Sat, 18 Apr 2026 17:52:12 +0200
Subject: [PATCH] [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 a05fdc74e6366..af883ff9fb2f9 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -1323,17 +1323,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())
@@ -1410,7 +1414,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:
@@ -1420,9 +1424,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;
@@ -1431,12 +1435,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;
@@ -1452,22 +1465,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
+}



More information about the llvm-commits mailing list