[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