[llvm] [SystemZ] Fold i16/i32/i64 logical RMW operations into memory operands in TableGen (PR #192802)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 07:55:05 PDT 2026
https://github.com/anoopkg6 updated https://github.com/llvm/llvm-project/pull/192802
>From 9fdcccf5dcd06c0a0dab1eae003c2b45ea2535eb Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Sat, 18 Apr 2026 17:52:12 +0200
Subject: [PATCH 01/11] [SystemZ] Fold i16/i32/i64 logical RMW operations into
memory operands - Extend tryFoldLoadStoreIntoMemOperand to support folding
of AND, OR, and XOR operations for wider types. - Handles both 12-bit
(NI/OI/XI) and 20-bit (NIY/OIY/XIY) displacements. - Implements big-endian
adjustment to target the least significant byte. - Restricts folding to
8-bit immediates as required by the instructions.
---
.../Target/SystemZ/SystemZISelDAGToDAG.cpp | 100 +++-
llvm/test/CodeGen/SystemZ/and-05-a.ll | 563 ++++++++++++++++++
llvm/test/CodeGen/SystemZ/or-05-a.ll | 536 +++++++++++++++++
llvm/test/CodeGen/SystemZ/xor-05-a.ll | 536 +++++++++++++++++
4 files changed, 1716 insertions(+), 19 deletions(-)
create mode 100644 llvm/test/CodeGen/SystemZ/and-05-a.ll
create mode 100644 llvm/test/CodeGen/SystemZ/or-05-a.ll
create mode 100644 llvm/test/CodeGen/SystemZ/xor-05-a.ll
diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index bb148c2dc2ebd..16b1339f3b9a9 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -1328,17 +1328,21 @@ static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode,
if (!StoredVal.getNode()->hasNUsesOfValue(1, 0))
return false;
- // Is the store non-extending and non-indexed?
- if (!ISD::isNormalStore(StoreNode) || StoreNode->isNonTemporal())
+ // Allow truncating stores for sub-word types but still reject
+ // complex addressing (indexed) and special memory hints (non-temporal).
+ if (StoreNode->isIndexed() || StoreNode->isNonTemporal())
return false;
SDValue Load = StoredVal->getOperand(0);
- // Is the stored value a non-extending and non-indexed load?
- if (!ISD::isNormalLoad(Load.getNode()))
- return false;
- // Return LoadNode by reference.
- LoadNode = cast<LoadSDNode>(Load);
+ // Peeking through ISD::ANY_EXTEND and allowing non-normal
+ // (Extending/Truncating) Loads/Stores.
+ if (Load.getOpcode() == ISD::ANY_EXTEND)
+ Load = Load.getOperand(0);
+ // Allow truncating stores and extending loads for sub-word logical ops.
+ LoadNode = dyn_cast<LoadSDNode>(Load);
+ if (!LoadNode || LoadNode->isIndexed())
+ return false;
// Is store the only read of the loaded value?
if (!Load.hasOneUse())
@@ -1415,7 +1419,7 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
// and opcode we can handle. Note that this must match the code below that
// actually lowers the opcodes.
EVT MemVT = StoreNode->getMemoryVT();
- unsigned NewOpc = 0;
+ std::pair<unsigned, unsigned> LOpcs;
bool NegateOperand = false;
switch (Opc) {
default:
@@ -1425,9 +1429,9 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
[[fallthrough]];
case SystemZISD::SADDO:
if (MemVT == MVT::i32)
- NewOpc = SystemZ::ASI;
+ LOpcs.first = SystemZ::ASI;
else if (MemVT == MVT::i64)
- NewOpc = SystemZ::AGSI;
+ LOpcs.first = SystemZ::AGSI;
else
return false;
break;
@@ -1436,12 +1440,21 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
[[fallthrough]];
case SystemZISD::UADDO:
if (MemVT == MVT::i32)
- NewOpc = SystemZ::ALSI;
+ LOpcs.first = SystemZ::ALSI;
else if (MemVT == MVT::i64)
- NewOpc = SystemZ::ALGSI;
+ LOpcs.first = SystemZ::ALGSI;
else
return false;
break;
+ case ISD::AND:
+ LOpcs = {SystemZ::NI, SystemZ::NIY};
+ break;
+ case ISD::OR:
+ LOpcs = {SystemZ::OI, SystemZ::OIY};
+ break;
+ case ISD::XOR:
+ LOpcs = {SystemZ::XI, SystemZ::XIY};
+ break;
}
LoadSDNode *LoadNode = nullptr;
@@ -1457,22 +1470,71 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
auto OperandV = OperandC->getAPIntValue();
if (NegateOperand)
OperandV = -OperandV;
- if (OperandV.getSignificantBits() > 8)
- return false;
- Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
+
+ bool IsLogicalByteOp = (LOpcs.second != 0);
+ unsigned NewOpc = 0;
+ if (!IsLogicalByteOp) {
+ if (OperandV.getSignificantBits() > 8)
+ return false;
+ NewOpc = LOpcs.first;
+ Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
+ } else {
+ if (OperandV.getActiveBits() > 8)
+ return false;
+ // Fix for logical byte ops (XI/Y, NI/Y, OI/Y).
+ // Ensure the APInt width matches the requested MVT::i32 width
+ // exactly to satisfy SelectionDAG assertions.
+ Operand = CurDAG->getTargetConstant(OperandV.zextOrTrunc(32), DL, MVT::i32);
+ }
SDValue Base, Disp;
if (!selectBDAddr20Only(StoreNode->getBasePtr(), Base, Disp))
return false;
+ // Handle Big-Endian offset for any integer width (i16, i32, i64).
+ if (IsLogicalByteOp) {
+ auto *DispC = dyn_cast<ConstantSDNode>(Disp);
+ if (!DispC)
+ return false;
+
+ int64_t NewDisp = DispC->getSExtValue();
+ // Calculate offset to the least significant byte.
+ if (MemVT.getStoreSize() > 1)
+ NewDisp += (MemVT.getStoreSize() - 1);
+ // Select the opcode based on the final displacement.
+ if (isUInt<12>(NewDisp)) {
+ NewOpc = LOpcs.first;
+ } else if (isInt<20>(NewDisp)) {
+ NewOpc = LOpcs.second;
+ } else {
+ return false;
+ }
+ Disp = CurDAG->getTargetConstant(NewDisp, DL, DispC->getValueType(0));
+ }
+ // Arithmetic ops like ASI/AGSI produce a result/CC (i32) and a Chain.
+ // Logical ops like XI/NI/OI produce just a Chain (MVT::Other).
+ SmallVector<EVT, 2> RetVTs;
+ // For ASI/AGSI/ALSI/ALGSI.
+ if (!IsLogicalByteOp)
+ RetVTs.push_back(MVT::i32);
+ // For the Chain.
+ RetVTs.push_back(MVT::Other);
+
SDValue Ops[] = { Base, Disp, Operand, InputChain };
- MachineSDNode *Result =
- CurDAG->getMachineNode(NewOpc, DL, MVT::i32, MVT::Other, Ops);
+ MachineSDNode *Result = CurDAG->getMachineNode(NewOpc, DL, RetVTs, Ops);
+
CurDAG->setNodeMemRefs(
Result, {StoreNode->getMemOperand(), LoadNode->getMemOperand()});
- ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
- ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
+ if (IsLogicalByteOp) {
+ // XI/NI/OI only produce a Chain at index 0.
+ ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 0));
+ } else {
+ // ASI/AGSI produce CC/Result at index 0 and Chain at index 1.
+ ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
+ ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
+ }
+
CurDAG->RemoveDeadNode(Node);
return true;
}
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
new file mode 100644
index 0000000000000..d8f6914ebf95a
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/and-05-a.ll
@@ -0,0 +1,563 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; and-05.ll tests the multiclass RMWIByte for i8 types.
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
+; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
+; legalization in the address matcher before folding can occur.
+; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
+; be reached via a starting GEP offset of 524287, as the Big-Endian
+; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+; Negative constants fail to fold for multi-byte types because they are
+; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @and_i16_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i16_ni_low_unsigned:
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @and_i32_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_low_unsigned:
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @and_i64_ni_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: and_i64_ni_low_unsigned:
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @and_i32_ni_boundary_128(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_boundary_128:
+; CHECK: ni 3(%r2), 128
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 128
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
+define void @and_i32_ni_max_byte_minus_1(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_max_byte_minus_1:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 254
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+; Phase ordering, DAG combiner identifies 'and 255' on 32-bit load as
+; zero-extending byte load (llc).
+define void @and_i32_ni_max_byte(ptr %ptr) {
+; CHECK-LABEL: and_i32_ni_max_byte:
+; CHECK: llc [[REG:%r[0-9]+]], 3(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 255
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @and_i16_neg_2(ptr %src) {
+; CHECK-LABEL: and_i16_neg_2:
+; CHECK-NOT: ni {{.*}}, 254
+; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: nilf [[REG]], 65534
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %src
+ %and = and i16 %val, -2
+ store i16 %and, ptr %src
+ ret void
+}
+
+define void @and_i32_neg_2(ptr %src) {
+; CHECK-LABEL: and_i32_neg_2:
+; CHECK-NOT:niy {{.*}}, 254
+; CHECK: n [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %src
+ %and = and i32 %val, -2
+ store i32 %and, ptr %src
+ ret void
+}
+
+; DAG Combiner already combines it into a single And Memory (ng) instruction.
+define void @and_i64_neg_2(ptr %src) {
+; CHECK-LABEL: and_i64_neg_2:
+; CHECK: ng %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %src
+ %and = and i64 %val, -2
+ store i64 %and, ptr %src
+ ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to ni/niy.
+define void @and_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: and_i32_too_wide:
+; CHECK-NOT: ni{{y?}} {{.*}}, 256
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 256
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @and_i32_niy_boundary_128(ptr %src) {
+; CHECK-LABEL: and_i32_niy_boundary_128:
+; CHECK: niy 4096(%r2), 128
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 128
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
+define void @and_i32_niy_max_byte_minus_1(ptr %src) {
+; CHECK-LABEL: and_i32_niy_max_byte_minus_1:
+; CHECK: niy 4096(%r2), 254
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 254
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+; DAG Combiner transforms the 4-byte load into an 8-bit load logical (llc).
+define void @and_i32_niy_max_byte(ptr %src) {
+; CHECK-LABEL: and_i32_niy_max_byte:
+; CHECK: llc [[REG:%r[0-9]+]], 4096(%r2)
+; CHECK: st [[REG]], 4093(%r2)
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %xor = and i32 %val, 255
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
+define void @and_i16_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i16_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
+define void @and_i32_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i32_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
+define void @and_i64_ni_high_disp(ptr %src) {
+; CHECK-LABEL: and_i64_ni_high_disp:
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4088
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @and_i16_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @and_i32_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @and_i64_niy_transition_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_transition_disp:
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4089
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @and_i16_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524286
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @and_i32_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524284
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @and_i64_niy_high_end_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_high_end_disp:
+; CHECK:niy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524280
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i16_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i32_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i64_niy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @and_i16_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -2
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @and_i32_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -4
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @and_i64_niy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_max_disp:
+; CHECK:niy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -8
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i16_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_min_disp:
+; CHECK:niy -524287(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i32_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_min_disp:
+; CHECK:niy -524285(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @and_i64_niy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_min_disp:
+; CHECK:niy -524281(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i16_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i16_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i32_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i32_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: and_i64_niy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @and_i16_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i16_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4094
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @and_i32_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i32_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4092
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @and_i64_ni_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i64_ni_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4088
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @and_i16_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i16_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @and_i32_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i32_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4093
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @and_i64_niy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: and_i64_niy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK:niy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4089
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: ni {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %and = and i32 %val, 1
+ store volatile i32 %and, ptr %ptr
+ ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: nilf {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret i32 %val
+}
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
new file mode 100644
index 0000000000000..b345da8ed8293
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/or-05-a.ll
@@ -0,0 +1,536 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; or-05.ll tests the multiclass RMWIByte for i8 types.
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
+; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
+; legalization in the address matcher before folding can occur.
+; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
+; be reached via a starting GEP offset of 524287, as the Big-Endian
+; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+; Negative constants fail to fold for multi-byte types because they are
+; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @or_i16_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i16_oi_low_unsigned:
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @or_i32_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_low_unsigned:
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @or_i64_oi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: or_i64_oi_low_unsigned:
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @or_i32_oi_boundary_128(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_boundary_128:
+; CHECK: oi 3(%r2), 128
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 128
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @or_i32_oi_max_byte(ptr %ptr) {
+; CHECK-LABEL: or_i32_oi_max_byte:
+; CHECK: oi 3(%r2), 255
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 255
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @or_i16_neg_2(ptr %src) {
+; CHECK-LABEL: or_i16_neg_2:
+; CHECK-NOT: oi {{.*}}, 254
+; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oill [[REG]], 65534
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %src
+ %or = or i16 %val, -2
+ store i16 %or, ptr %src
+ ret void
+}
+
+define void @or_i32_neg_2(ptr %src) {
+; CHECK-LABEL: or_i32_neg_2:
+; CHECK-NOT: oiy {{.*}}, 254
+; CHECK: o [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %src
+ %or = or i32 %val, -2
+ store i32 %or, ptr %src
+ ret void
+}
+
+define void @or_i64_neg_2(ptr %src) {
+; CHECK-LABEL: or_i64_neg_2:
+; CHECK-NOT: oiy {{.*}}, 254
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %src
+ %or = or i64 %val, -2
+ store i64 %or, ptr %src
+ ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to oi/oiy.
+define void @or_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: or_i32_too_wide:
+; CHECK-NOT: oi{{y?}} {{.*}}, 256
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 256
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @or_i32_oiy_boundary_128(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_boundary_128:
+; CHECK: oiy 4096(%r2), 128
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 128
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @or_i32_oiy_max_byte(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_max_byte:
+; CHECK: oiy 4096(%r2), 255
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 255
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
+define void @or_i16_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
+define void @or_i32_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
+define void @or_i64_oi_high_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oi_high_disp:
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4088
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @or_i16_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @or_i32_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64 transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @or_i64_oiy_transition_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_transition_disp:
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4089
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @or_i16_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524286
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @or_i32_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524284
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @or_i64_oiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_high_end_disp:
+; CHECK: oiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524280
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i16_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i32_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i64_oiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @or_i16_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -2
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @or_i32_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -4
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @or_i64_oiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_max_disp:
+; CHECK: oiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -8
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i16_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_min_disp:
+; CHECK: oiy -524287(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i32_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_min_disp:
+; CHECK: oiy -524285(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @or_i64_oiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_min_disp:
+; CHECK: oiy -524281(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i16_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i16_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i32_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i32_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: or_i64_oiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @or_i16_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i16_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4094
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @or_i32_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i32_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4092
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @or_i64_oi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i64_oi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4088
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i16_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i32_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4093
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: or_i64_oiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4089
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: oi {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %or = or i32 %val, 1
+ store volatile i32 %or, ptr %ptr
+ ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oill {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret i32 %val
+}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05-a.ll b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
new file mode 100644
index 0000000000000..5b19d66ca50bf
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
@@ -0,0 +1,536 @@
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; xor-05.ll tests the multiclass RMWIByte for i8 types.
+; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
+; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+
+; There are certain liomitations:
+; 1. Displacement Range Limitations:
+; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
+; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
+; legalization in the address matcher before folding can occur.
+; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
+; be reached via a starting GEP offset of 524287, as the Big-Endian
+; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
+;
+; 2. Negative Constant Folding Failures:
+; Negative constants fail to fold for multi-byte types because they are
+; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
+; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
+
+; Check i16 (short): Offset + 1.
+define void @xor_i16_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i16_xi_low_unsigned:
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32 (int): Offset + 3.
+define void @xor_i32_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_low_unsigned:
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64 (int): Offset + 7.
+define void @xor_i64_xi_low_unsigned(ptr %ptr) {
+; CHECK-LABEL: xor_i64_xi_low_unsigned:
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check High: 128 (0x80) - The "signed bit" of a byte.
+; Verifies that patterns with the 8th bit set still fold.
+define void @xor_i32_xi_boundary_128(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_boundary_128:
+; CHECK: xi 3(%r2), 128
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 128
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @xor_i32_xi_max_byte(ptr %ptr) {
+; CHECK-LABEL: xor_i32_xi_max_byte:
+; CHECK: xi 3(%r2), 255
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 255
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check bit-width constraint with negative signed value.
+; This test case with -2 (0xFF...FE) fails to fold because the immediate value
+; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
+; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
+define void @xor_i16_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i16_neg_2:
+; CHECK-NOT: xi {{.*}}, 254
+; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf [[REG]], 65534
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %src
+ %xor = xor i16 %val, -2
+ store i16 %xor, ptr %src
+ ret void
+}
+
+define void @xor_i32_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i32_neg_2:
+; CHECK-NOT: xiy {{.*}}, 254
+; CHECK: x [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %src
+ %xor = xor i32 %val, -2
+ store i32 %xor, ptr %src
+ ret void
+}
+
+define void @xor_i64_neg_2(ptr %src) {
+; CHECK-LABEL: xor_i64_neg_2:
+; CHECK-NOT: xiy {{.*}}, 254
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %src
+ %xor = xor i64 %val, -2
+ store i64 %xor, ptr %src
+ ret void
+}
+
+; Check out of Range: 256 (0x100) - Requires 9 bits.
+; Should not fold to xi/xiy.
+define void @xor_i32_too_wide(ptr %ptr) {
+; CHECK-LABEL: xor_i32_too_wide:
+; CHECK-NOT: xi{{y?}} {{.*}}, 256
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 256
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check high: 128 (0x80) - The "signed bit" of a byte.
+define void @xor_i32_xiy_boundary_128(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_boundary_128:
+; CHECK: xiy 4096(%r2), 128
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 128
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check max: 255 (0xFF) - All bits set in the target byte.
+define void @xor_i32_xiy_max_byte(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_max_byte:
+; CHECK: xiy 4096(%r2), 255
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 255
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
+define void @xor_i16_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
+define void @xor_i32_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
+define void @xor_i64_xi_high_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xi_high_disp:
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4088
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16 transisition to xiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @xor_i16_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32 transisition to xiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @xor_i32_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64 transisition to xiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @xor_i64_xiy_transition_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_transition_disp:
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4089
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+define void @xor_i16_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524286
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+define void @xor_i32_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524284
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+define void @xor_i64_xiy_high_end_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_high_end_disp:
+; CHECK: xiy 524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524280
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i16_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i32_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i64_xiy_pos_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_pos_out_of_range_disp:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i16_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -2
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i32_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -4
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64: High end of the negative Displacement (Signed 20-bit).
+define void @xor_i64_xiy_neg_max_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_max_disp:
+; CHECK: xiy -1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -8
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check i16: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i16_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_min_disp:
+; CHECK: xiy -524287(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check i32: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i32_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_min_disp:
+; CHECK: xiy -524285(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check i64: Low end of the negative Displacement (Signed 20-bit).
+define void @xor_i64_xiy_neg_min_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_min_disp:
+; CHECK: xiy -524281(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i16_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i16_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i32_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i32_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down - i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @xor_i64_xiy_neg_out_of_range_disp(ptr %src) {
+; CHECK-LABEL: xor_i64_xiy_neg_out_of_range_disp:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+define void @xor_i16_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i16_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4094
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+define void @xor_i32_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i32_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4092
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+define void @xor_i64_xi_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i64_xi_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4095(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4088
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+define void @xor_i16_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i16_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+define void @xor_i32_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i32_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4093
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+define void @xor_i64_xiy_no_index(i64 %src, i64 %index) {
+; CHECK-LABEL: xor_i64_xiy_no_index:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4096(%r2), 1
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4089
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Volatile memory should not be folded into XI.
+define i32 @test_volatile(ptr %ptr) {
+; CHECK-LABEL: test_volatile:
+; CHECK-NOT: xi {{.*}}, 1
+; CHECK: l {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store volatile i32 %xor, ptr %ptr
+ ret i32 %val
+}
+
+; Multiple uses of the loaded value should not be folded.
+define i32 @test_multi_use(ptr %ptr) {
+; CHECK-LABEL: test_multi_use:
+; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf {{%r[0-9]+}}, 1
+; CHECK: st {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret i32 %val
+}
>From 6928e853f6281b97a426edb586d8bfe34e8ee64e Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Fri, 24 Apr 2026 01:23:20 +0200
Subject: [PATCH 02/11] [SystemZ] Update isFoldableRMW to support i16, i32, and
i64 logical ops. This aligns the TTI cost model with the corresponding
DAGToDAG folding support for these wider integer types.
---
.../SystemZ/SystemZTargetTransformInfo.cpp | 2 +-
.../CostModel/SystemZ/fold-rmw-cost.ll | 29 ++++++++++++
llvm/test/CodeGen/SystemZ/and-05-a.ll | 46 +++++++++----------
llvm/test/CodeGen/SystemZ/or-05-a.ll | 46 +++++++++----------
4 files changed, 76 insertions(+), 47 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index 5353c36fd9b97..e7081b335df07 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -557,7 +557,7 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
case Instruction::And:
case Instruction::Or:
case Instruction::Xor:
- if (BitWidth != 8)
+ if (BitWidth != 8 && BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
return false;
break;
case Instruction::Add:
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index 98aa0cda026d1..f82ac31797274 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -145,3 +145,32 @@ define void @test_vector_no_fold(<4 x i32> %val, ptr %p) {
ret void
}
+define void @test_xor_i16(ptr %p) {
+; CHECK-LABEL: 'test_xor_i16'
+; CHECK: cost of 0 {{.*}} xor i16
+; CHECK: cost of 0 {{.*}} store i16
+ %v = load i16, ptr %p
+ %res = xor i16 %v, 1
+ store i16 %res, ptr %p
+ ret void
+}
+
+define void @test_or_i32(ptr %p) {
+; CHECK-LABEL: 'test_or_i32'
+; CHECK: cost of 0 {{.*}} or i32
+; CHECK: cost of 0 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = or i32 %v, 1
+ store i32 %res, ptr %p
+ ret void
+}
+
+define void @test_and_i64(ptr %p) {
+; CHECK-LABEL: 'test_and_i64'
+; CHECK: cost of 0 {{.*}} and i64
+; CHECK: cost of 0 {{.*}} store i64
+ %v = load i64, ptr %p
+ %res = and i64 %v, 1
+ store i64 %res, ptr %p
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
index d8f6914ebf95a..31084f4f8a87c 100644
--- a/llvm/test/CodeGen/SystemZ/and-05-a.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05-a.ll
@@ -2,7 +2,7 @@
; and-05.ll tests the multiclass RMWIByte for i8 types.
; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+; covering both 12-bit unsigned (NI) and 20-bit signed (NIY) displacements.
; There are certain liomitations:
; 1. Displacement Range Limitations:
@@ -177,7 +177,7 @@ define void @and_i32_niy_max_byte(ptr %src) {
ret void
}
-; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
+; Check i16 high end of NI range: Original 4095 + 1 (LSB) = 4095.
define void @and_i16_ni_high_disp(ptr %src) {
; CHECK-LABEL: and_i16_ni_high_disp:
; CHECK: ni 4095(%r2), 1
@@ -189,7 +189,7 @@ define void @and_i16_ni_high_disp(ptr %src) {
ret void
}
-; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
+; Check i32 high end of NI range: Original 4092 + 3 (LSB) = 4095.
define void @and_i32_ni_high_disp(ptr %src) {
; CHECK-LABEL: and_i32_ni_high_disp:
; CHECK: ni 4095(%r2), 1
@@ -201,7 +201,7 @@ define void @and_i32_ni_high_disp(ptr %src) {
ret void
}
-; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
+; Check i64 high end of NI range: Original 4088 + 7 (LSB) = 4095.
define void @and_i64_ni_high_disp(ptr %src) {
; CHECK-LABEL: and_i64_ni_high_disp:
; CHECK: ni 4095(%r2), 1
@@ -213,7 +213,7 @@ define void @and_i64_ni_high_disp(ptr %src) {
ret void
}
-; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers NIY).
define void @and_i16_niy_transition_disp(ptr %src) {
; CHECK-LABEL: and_i16_niy_transition_disp:
; CHECK:niy 4096(%r2), 1
@@ -225,7 +225,7 @@ define void @and_i16_niy_transition_disp(ptr %src) {
ret void
}
-; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers NIY).
define void @and_i32_niy_transition_disp(ptr %src) {
; CHECK-LABEL: and_i32_niy_transition_disp:
; CHECK:niy 4096(%r2), 1
@@ -237,7 +237,7 @@ define void @and_i32_niy_transition_disp(ptr %src) {
ret void
}
-; Check i64 transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check i64 transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers NIY).
define void @and_i64_niy_transition_disp(ptr %src) {
; CHECK-LABEL: and_i64_niy_transition_disp:
; CHECK:niy 4096(%r2), 1
@@ -249,7 +249,7 @@ define void @and_i64_niy_transition_disp(ptr %src) {
ret void
}
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+; Check i16 high end of the NIY range: Original 524286 + 1 (LSB) = 524287.
define void @and_i16_niy_high_end_disp(ptr %src) {
; CHECK-LABEL: and_i16_niy_high_end_disp:
; CHECK:niy 524287(%r2), 127
@@ -261,7 +261,7 @@ define void @and_i16_niy_high_end_disp(ptr %src) {
ret void
}
-; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+; Check i32 high end of the NIY range: Original 524284 + 3 (LSB) = 524287.
define void @and_i32_niy_high_end_disp(ptr %src) {
; CHECK-LABEL: and_i32_niy_high_end_disp:
; CHECK:niy 524287(%r2), 127
@@ -273,7 +273,7 @@ define void @and_i32_niy_high_end_disp(ptr %src) {
ret void
}
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+; Check i64 high end of the NIY range: Original 524280 + 7 (LSB) = 524287.
define void @and_i64_niy_high_end_disp(ptr %src) {
; CHECK-LABEL: and_i64_niy_high_end_disp:
; CHECK:niy 524287(%r2), 127
@@ -441,8 +441,8 @@ define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
ret void
}
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for NI).
define void @and_i16_ni_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i16_ni_no_index:
; CHECK: agr %r2, %r3
@@ -457,8 +457,8 @@ define void @and_i16_ni_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for NI).
define void @and_i32_ni_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i32_ni_no_index:
; CHECK: agr %r2, %r3
@@ -473,8 +473,8 @@ define void @and_i32_ni_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+; Check that NI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for NI).
define void @and_i64_ni_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i64_ni_no_index:
; CHECK: agr %r2, %r3
@@ -489,8 +489,8 @@ define void @and_i64_ni_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers NIY).
define void @and_i16_niy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i16_niy_no_index:
; CHECK: agr %r2, %r3
@@ -505,8 +505,8 @@ define void @and_i16_niy_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers NIY).
define void @and_i32_niy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i32_niy_no_index:
; CHECK: agr %r2, %r3
@@ -521,8 +521,8 @@ define void @and_i32_niy_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check that NIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers NIY).
define void @and_i64_niy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: and_i64_niy_no_index:
; CHECK: agr %r2, %r3
@@ -537,7 +537,7 @@ define void @and_i64_niy_no_index(i64 %src, i64 %index) {
ret void
}
-; Volatile memory should not be folded into XI.
+; Volatile memory should not be folded into NI.
define i32 @test_volatile(ptr %ptr) {
; CHECK-LABEL: test_volatile:
; CHECK-NOT: ni {{.*}}, 1
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
index b345da8ed8293..e87e1deb3f6ea 100644
--- a/llvm/test/CodeGen/SystemZ/or-05-a.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05-a.ll
@@ -2,7 +2,7 @@
; or-05.ll tests the multiclass RMWIByte for i8 types.
; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
+; covering both 12-bit unsigned (OI) and 20-bit signed (OIY) displacements.
; There are certain liomitations:
; 1. Displacement Range Limitations:
@@ -150,7 +150,7 @@ define void @or_i32_oiy_max_byte(ptr %src) {
ret void
}
-; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
+; Check i16 high end of OI range: Original 4095 + 1 (LSB) = 4095.
define void @or_i16_oi_high_disp(ptr %src) {
; CHECK-LABEL: or_i16_oi_high_disp:
; CHECK: oi 4095(%r2), 1
@@ -162,7 +162,7 @@ define void @or_i16_oi_high_disp(ptr %src) {
ret void
}
-; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
+; Check i32 high end of OI range: Original 4092 + 3 (LSB) = 4095.
define void @or_i32_oi_high_disp(ptr %src) {
; CHECK-LABEL: or_i32_oi_high_disp:
; CHECK: oi 4095(%r2), 1
@@ -174,7 +174,7 @@ define void @or_i32_oi_high_disp(ptr %src) {
ret void
}
-; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
+; Check i64 high end of OI range: Original 4088 + 7 (LSB) = 4095.
define void @or_i64_oi_high_disp(ptr %src) {
; CHECK-LABEL: or_i64_oi_high_disp:
; CHECK: oi 4095(%r2), 1
@@ -186,7 +186,7 @@ define void @or_i64_oi_high_disp(ptr %src) {
ret void
}
-; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers OIY).
define void @or_i16_oiy_transition_disp(ptr %src) {
; CHECK-LABEL: or_i16_oiy_transition_disp:
; CHECK: oiy 4096(%r2), 1
@@ -198,7 +198,7 @@ define void @or_i16_oiy_transition_disp(ptr %src) {
ret void
}
-; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers OIY).
define void @or_i32_oiy_transition_disp(ptr %src) {
; CHECK-LABEL: or_i32_oiy_transition_disp:
; CHECK: oiy 4096(%r2), 1
@@ -210,7 +210,7 @@ define void @or_i32_oiy_transition_disp(ptr %src) {
ret void
}
-; Check i64 transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check i64 transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers OIY).
define void @or_i64_oiy_transition_disp(ptr %src) {
; CHECK-LABEL: or_i64_oiy_transition_disp:
; CHECK: oiy 4096(%r2), 1
@@ -222,7 +222,7 @@ define void @or_i64_oiy_transition_disp(ptr %src) {
ret void
}
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
+; Check i16 high end of the OIY range: Original 524286 + 1 (LSB) = 524287.
define void @or_i16_oiy_high_end_disp(ptr %src) {
; CHECK-LABEL: or_i16_oiy_high_end_disp:
; CHECK: oiy 524287(%r2), 127
@@ -234,7 +234,7 @@ define void @or_i16_oiy_high_end_disp(ptr %src) {
ret void
}
-; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
+; Check i32 high end of the OIY range: Original 524284 + 3 (LSB) = 524287.
define void @or_i32_oiy_high_end_disp(ptr %src) {
; CHECK-LABEL: or_i32_oiy_high_end_disp:
; CHECK: oiy 524287(%r2), 127
@@ -246,7 +246,7 @@ define void @or_i32_oiy_high_end_disp(ptr %src) {
ret void
}
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
+; Check i64 high end of the OIY range: Original 524280 + 7 (LSB) = 524287.
define void @or_i64_oiy_high_end_disp(ptr %src) {
; CHECK-LABEL: or_i64_oiy_high_end_disp:
; CHECK: oiy 524287(%r2), 127
@@ -414,8 +414,8 @@ define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
ret void
}
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i16.
+; Original 4094 + 1 (LSB) = 4095 (Max range for OI).
define void @or_i16_oi_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i16_oi_no_index:
; CHECK: agr %r2, %r3
@@ -430,8 +430,8 @@ define void @or_i16_oi_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i32.
+; Original 4092 + 3 (LSB) = 4095 (Max range for OI).
define void @or_i32_oi_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i32_oi_no_index:
; CHECK: agr %r2, %r3
@@ -446,8 +446,8 @@ define void @or_i32_oi_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
+; Check that OI does not allow an index for i64.
+; Original 4088 + 7 (LSB) = 4095 (Max range for OI).
define void @or_i64_oi_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i64_oi_no_index:
; CHECK: agr %r2, %r3
@@ -462,8 +462,8 @@ define void @or_i64_oi_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i16.
+; Original 4095 + 1 (LSB) = 4096 (triggers OIY).
define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i16_oiy_no_index:
; CHECK: agr %r2, %r3
@@ -478,8 +478,8 @@ define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i32.
+; Original 4093 + 3 (LSB) = 4096 (triggers OIY).
define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i32_oiy_no_index:
; CHECK: agr %r2, %r3
@@ -494,8 +494,8 @@ define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
+; Check that OIY does not allow an index for i64.
+; Original 4089 + 7 (LSB) = 4096 (triggers OIY).
define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
; CHECK-LABEL: or_i64_oiy_no_index:
; CHECK: agr %r2, %r3
@@ -510,7 +510,7 @@ define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
ret void
}
-; Volatile memory should not be folded into XI.
+; Volatile memory should not be folded into OI.
define i32 @test_volatile(ptr %ptr) {
; CHECK-LABEL: test_volatile:
; CHECK-NOT: oi {{.*}}, 1
>From dd5dcb0d1c1b605a6d29f67e7ffb5c79618b8497 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 27 Apr 2026 22:37:40 +0200
Subject: [PATCH 03/11] Revert changes to SystemZISelDAGToDAG.cpp as per code
review
---
.../Target/SystemZ/SystemZISelDAGToDAG.cpp | 100 ++++--------------
1 file changed, 19 insertions(+), 81 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index 16b1339f3b9a9..bb148c2dc2ebd 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -1328,22 +1328,18 @@ static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode,
if (!StoredVal.getNode()->hasNUsesOfValue(1, 0))
return false;
- // Allow truncating stores for sub-word types but still reject
- // complex addressing (indexed) and special memory hints (non-temporal).
- if (StoreNode->isIndexed() || StoreNode->isNonTemporal())
+ // Is the store non-extending and non-indexed?
+ if (!ISD::isNormalStore(StoreNode) || StoreNode->isNonTemporal())
return false;
SDValue Load = StoredVal->getOperand(0);
-
- // Peeking through ISD::ANY_EXTEND and allowing non-normal
- // (Extending/Truncating) Loads/Stores.
- if (Load.getOpcode() == ISD::ANY_EXTEND)
- Load = Load.getOperand(0);
- // Allow truncating stores and extending loads for sub-word logical ops.
- LoadNode = dyn_cast<LoadSDNode>(Load);
- if (!LoadNode || LoadNode->isIndexed())
+ // Is the stored value a non-extending and non-indexed load?
+ if (!ISD::isNormalLoad(Load.getNode()))
return false;
+ // Return LoadNode by reference.
+ LoadNode = cast<LoadSDNode>(Load);
+
// Is store the only read of the loaded value?
if (!Load.hasOneUse())
return false;
@@ -1419,7 +1415,7 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
// and opcode we can handle. Note that this must match the code below that
// actually lowers the opcodes.
EVT MemVT = StoreNode->getMemoryVT();
- std::pair<unsigned, unsigned> LOpcs;
+ unsigned NewOpc = 0;
bool NegateOperand = false;
switch (Opc) {
default:
@@ -1429,9 +1425,9 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
[[fallthrough]];
case SystemZISD::SADDO:
if (MemVT == MVT::i32)
- LOpcs.first = SystemZ::ASI;
+ NewOpc = SystemZ::ASI;
else if (MemVT == MVT::i64)
- LOpcs.first = SystemZ::AGSI;
+ NewOpc = SystemZ::AGSI;
else
return false;
break;
@@ -1440,21 +1436,12 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
[[fallthrough]];
case SystemZISD::UADDO:
if (MemVT == MVT::i32)
- LOpcs.first = SystemZ::ALSI;
+ NewOpc = SystemZ::ALSI;
else if (MemVT == MVT::i64)
- LOpcs.first = SystemZ::ALGSI;
+ NewOpc = SystemZ::ALGSI;
else
return false;
break;
- case ISD::AND:
- LOpcs = {SystemZ::NI, SystemZ::NIY};
- break;
- case ISD::OR:
- LOpcs = {SystemZ::OI, SystemZ::OIY};
- break;
- case ISD::XOR:
- LOpcs = {SystemZ::XI, SystemZ::XIY};
- break;
}
LoadSDNode *LoadNode = nullptr;
@@ -1470,71 +1457,22 @@ bool SystemZDAGToDAGISel::tryFoldLoadStoreIntoMemOperand(SDNode *Node) {
auto OperandV = OperandC->getAPIntValue();
if (NegateOperand)
OperandV = -OperandV;
-
- bool IsLogicalByteOp = (LOpcs.second != 0);
- unsigned NewOpc = 0;
- if (!IsLogicalByteOp) {
- if (OperandV.getSignificantBits() > 8)
- return false;
- NewOpc = LOpcs.first;
- Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
- } else {
- if (OperandV.getActiveBits() > 8)
- return false;
- // Fix for logical byte ops (XI/Y, NI/Y, OI/Y).
- // Ensure the APInt width matches the requested MVT::i32 width
- // exactly to satisfy SelectionDAG assertions.
- Operand = CurDAG->getTargetConstant(OperandV.zextOrTrunc(32), DL, MVT::i32);
- }
+ if (OperandV.getSignificantBits() > 8)
+ return false;
+ Operand = CurDAG->getTargetConstant(OperandV, DL, MemVT);
SDValue Base, Disp;
if (!selectBDAddr20Only(StoreNode->getBasePtr(), Base, Disp))
return false;
- // Handle Big-Endian offset for any integer width (i16, i32, i64).
- if (IsLogicalByteOp) {
- auto *DispC = dyn_cast<ConstantSDNode>(Disp);
- if (!DispC)
- return false;
-
- int64_t NewDisp = DispC->getSExtValue();
- // Calculate offset to the least significant byte.
- if (MemVT.getStoreSize() > 1)
- NewDisp += (MemVT.getStoreSize() - 1);
- // Select the opcode based on the final displacement.
- if (isUInt<12>(NewDisp)) {
- NewOpc = LOpcs.first;
- } else if (isInt<20>(NewDisp)) {
- NewOpc = LOpcs.second;
- } else {
- return false;
- }
- Disp = CurDAG->getTargetConstant(NewDisp, DL, DispC->getValueType(0));
- }
- // Arithmetic ops like ASI/AGSI produce a result/CC (i32) and a Chain.
- // Logical ops like XI/NI/OI produce just a Chain (MVT::Other).
- SmallVector<EVT, 2> RetVTs;
- // For ASI/AGSI/ALSI/ALGSI.
- if (!IsLogicalByteOp)
- RetVTs.push_back(MVT::i32);
- // For the Chain.
- RetVTs.push_back(MVT::Other);
-
SDValue Ops[] = { Base, Disp, Operand, InputChain };
- MachineSDNode *Result = CurDAG->getMachineNode(NewOpc, DL, RetVTs, Ops);
-
+ MachineSDNode *Result =
+ CurDAG->getMachineNode(NewOpc, DL, MVT::i32, MVT::Other, Ops);
CurDAG->setNodeMemRefs(
Result, {StoreNode->getMemOperand(), LoadNode->getMemOperand()});
- if (IsLogicalByteOp) {
- // XI/NI/OI only produce a Chain at index 0.
- ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 0));
- } else {
- // ASI/AGSI produce CC/Result at index 0 and Chain at index 1.
- ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
- ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
- }
-
+ ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1));
+ ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0));
CurDAG->RemoveDeadNode(Node);
return true;
}
>From f781a702fba96c27eaab4fd26dcad0ac4ae3304e Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 4 May 2026 01:38:01 +0200
Subject: [PATCH 04/11] [SystemZ] Fold i16/i32/i64 logical RMW operations into
memory operands - This implementation extends TableGen to support folding
of AND, OR, and XOR operations for wider types(i16/i32/i64) by using
RMWIByteLSB to adjust the displacement to target the LSB. - Handles
both 12-bit (NI/OI/XI) and 20-bit (NIY/OIY/XIY) displacements. Also,
optimizes out-of-range 20-bit addresses legalized into registers. -
Restricts folding to 8-bit positive immediates.
---
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 6 +
llvm/lib/Target/SystemZ/SystemZOperands.td | 43 ++
llvm/lib/Target/SystemZ/SystemZOperators.td | 41 ++
llvm/lib/Target/SystemZ/SystemZPatterns.td | 52 ++
llvm/test/CodeGen/SystemZ/and-05-a.ll | 563 --------------------
llvm/test/CodeGen/SystemZ/and-05.ll | 526 +++++++++++++++++-
llvm/test/CodeGen/SystemZ/or-05-a.ll | 536 -------------------
llvm/test/CodeGen/SystemZ/or-05.ll | 528 +++++++++++++++++-
llvm/test/CodeGen/SystemZ/xor-05-a.ll | 536 -------------------
llvm/test/CodeGen/SystemZ/xor-05.ll | 529 +++++++++++++++++-
10 files changed, 1687 insertions(+), 1673 deletions(-)
delete mode 100644 llvm/test/CodeGen/SystemZ/and-05-a.ll
delete mode 100644 llvm/test/CodeGen/SystemZ/or-05-a.ll
delete mode 100644 llvm/test/CodeGen/SystemZ/xor-05-a.ll
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 693b171bcabf1..41b1becda829b 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1331,6 +1331,8 @@ let Defs = [CC] in {
}
defm : RMWIByte<and, bdaddr12pair, NI>;
defm : RMWIByte<and, bdaddr20pair, NIY>;
+defm : RMWIByteLSB<and, bdaddr12pair, NI>;
+defm : RMWIByteLSB<and, bdaddr20pair, NIY>;
//===----------------------------------------------------------------------===//
// OR
@@ -1388,6 +1390,8 @@ let Defs = [CC] in {
}
defm : RMWIByte<or, bdaddr12pair, OI>;
defm : RMWIByte<or, bdaddr20pair, OIY>;
+defm : RMWIByteLSB<or, bdaddr12pair, OI>;
+defm : RMWIByteLSB<or, bdaddr20pair, OIY>;
//===----------------------------------------------------------------------===//
// XOR
@@ -1428,6 +1432,8 @@ let Defs = [CC] in {
}
defm : RMWIByte<xor, bdaddr12pair, XI>;
defm : RMWIByte<xor, bdaddr20pair, XIY>;
+defm : RMWIByteLSB<xor, bdaddr12pair, XI>;
+defm : RMWIByteLSB<xor, bdaddr20pair, XIY>;
//===----------------------------------------------------------------------===//
// Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 119bf4f7ca615..ceb5038d50b5b 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -694,3 +694,46 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
let IsAPInt = true;
let FastIselShouldIgnore = true;
}
+
+// Extracts the base register from the load node.
+def get_base_from_load : SDNodeXForm<ld, [{
+ SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+ // If the address is (Base + Disp), return Base.
+ // Otherwise, return the whole Addr.
+ if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+ return Addr.getOperand(0);
+ return Addr;
+}]>;
+
+// i16: Target the displacement of the load and add +1 for Big-Endian LSB.
+def get_disp_lsb1 : SDNodeXForm<ld, [{
+ SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+ int64_t Disp = 0;
+ if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+ Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+ return CurDAG->getTargetConstant(Disp + 1, SDLoc(N), MVT::i64);
+}]>;
+
+// i32: Target the displacement of the load and add +3 for Big-Endian LSB.
+def get_disp_lsb3 : SDNodeXForm<ld, [{
+ SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+ int64_t Disp = 0;
+ if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+ Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+ return CurDAG->getTargetConstant(Disp + 3, SDLoc(N), MVT::i64);
+}]>;
+
+// i64: Target the displacement of the load and add +7 for Big-Endian LSB.
+def get_disp_lsb7 : SDNodeXForm<ld, [{
+ SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
+ int64_t Disp = 0;
+ if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
+ Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
+ return CurDAG->getTargetConstant(Disp + 7, SDLoc(N), MVT::i64);
+}]>;
+
+// Normalizes the immediate to i32.
+def as_i32imm : SDNodeXForm<imm, [{
+ uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
+ return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 739d9b2a7efb2..ad1dbff133f5d 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1369,3 +1369,44 @@ class z_vse_by_parts<SDPatternOperator operator, int index1, int index2>
def z_vsei8_by_parts : z_vse_by_parts<sext8dbl, 7, 15>;
def z_vsei16_by_parts : z_vse_by_parts<sext16dbl, 3, 7>;
def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
+
+// Matches any integer constant where only the last byte is set.
+def immLSB8 : PatLeaf<(imm), [{
+ return N->getAPIntValue().getActiveBits() <= 8;
+}]>;
+
+// Fragment for i16 any-extending loads with a 20-bit signed displacement check.
+def anyextloadi16_20bit : PatFrag<(ops node:$addr), (anyextloadi16 $addr), [{
+ auto *Load = cast<MemSDNode>(N);
+ SDValue BaseAddr = Load->getBasePtr();
+ if (BaseAddr.getOpcode() == ISD::ADD &&
+ isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+ int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+ return Disp >= -524288 && Disp <= 524287;
+ }
+ return true;
+}]>;
+
+// Fragment for i32/i64 loads with a 20-bit signed displacement check.
+def load_20bit : PatFrag<(ops node:$addr), (load $addr), [{
+ auto *Load = cast<MemSDNode>(N);
+ SDValue BaseAddr = Load->getBasePtr();
+ if (BaseAddr.getOpcode() == ISD::ADD &&
+ isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+ int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+ return Disp >= -524288 && Disp <= 524287;
+ }
+ return true;
+}]>;
+
+// Check if displacement is safe for RMW operations on wider types.
+def load_RMW_safe : PatFrag<(ops node:$addr), (load $addr), [{
+ auto *Load = cast<MemSDNode>(N);
+ SDValue BaseAddr = Load->getBasePtr();
+ if (BaseAddr.getOpcode() == ISD::ADD &&
+ isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
+ int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
+ return Disp >= -524288 && Disp <= 524287;
+ }
+ return true;
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 4d6bc68e9a7ed..7cf4a932dc5b7 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -53,6 +53,58 @@ multiclass RMWIByte<SDPatternOperator operator, AddressingMode mode,
def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm64, insn>;
}
+// Handle RMW operations on the Least Significant Byte (LSB) of wider types.
+// This allows targeting byte-level instructions (NI, NIY, etc.) for i16, i32,
+// and i64 memory locations when only the last byte is modified.
+multiclass RMWIByteLSB<SDPatternOperator operator, AddressingMode mode,
+ Instruction insn> {
+
+ // Base + Displacement.
+ // Uses XForms to adjust the displacement to target the LSB.
+ let AddedComplexity = 20 in {
+ def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16_20bit:$src
+ mode:$addr)),
+ (i32 immLSB8:$imm))),
+ mode:$addr),
+ (insn (get_base_from_load $src),
+ (get_disp_lsb1 $src),
+ (as_i32imm $imm))>;
+
+ def : Pat<(store (i32 (operator (i32 (load_20bit:$src mode:$addr)),
+ (i32 immLSB8:$imm))),
+ mode:$addr),
+ (insn (get_base_from_load $src),
+ (get_disp_lsb3 $src),
+ (as_i32imm $imm))>;
+
+ def : Pat<(store (i64 (operator (i64 (load_20bit:$src mode:$addr)),
+ (i64 immLSB8:$imm))),
+ mode:$addr),
+ (insn (get_base_from_load $src),
+ (get_disp_lsb7 $src),
+ (as_i32imm $imm))>;
+ }
+
+ // Register-only fallback (for AGFI or legalized addresses).
+ // Targets out-of-range addresses legalized into registers.
+ let AddedComplexity = 15 in {
+ def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 ADDR64:$base)),
+ (i32 immLSB8:$imm))),
+ ADDR64:$base),
+ (insn ADDR64:$base, 1, (as_i32imm $imm))>;
+
+ def : Pat<(store (i32 (operator (i32 (load ADDR64:$base)),
+ (i32 immLSB8:$imm))),
+ ADDR64:$base),
+ (insn ADDR64:$base, 3, (as_i32imm $imm))>;
+
+ def : Pat<(store (i64 (operator (i64 (load ADDR64:$base)),
+ (i64 immLSB8:$imm))),
+ ADDR64:$base),
+ (insn ADDR64:$base, 7, (as_i32imm $imm))>;
+ }
+}
+
// Record that INSN performs insertion TYPE into a register of class CLS.
// The inserted operand is loaded using LOAD from an address of mode MODE.
multiclass InsertMem<string type, Instruction insn, RegisterOperand cls,
diff --git a/llvm/test/CodeGen/SystemZ/and-05-a.ll b/llvm/test/CodeGen/SystemZ/and-05-a.ll
deleted file mode 100644
index 31084f4f8a87c..0000000000000
--- a/llvm/test/CodeGen/SystemZ/and-05-a.ll
+++ /dev/null
@@ -1,563 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; and-05.ll tests the multiclass RMWIByte for i8 types.
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (NI) and 20-bit signed (NIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
-; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
-; legalization in the address matcher before folding can occur.
-; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
-; be reached via a starting GEP offset of 524287, as the Big-Endian
-; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-; Negative constants fail to fold for multi-byte types because they are
-; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @and_i16_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i16_ni_low_unsigned:
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @and_i32_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_low_unsigned:
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @and_i64_ni_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: and_i64_ni_low_unsigned:
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @and_i32_ni_boundary_128(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_boundary_128:
-; CHECK: ni 3(%r2), 128
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 128
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
-define void @and_i32_ni_max_byte_minus_1(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_max_byte_minus_1:
-; CHECK: ni 3(%r2), 254
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 254
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-; Phase ordering, DAG combiner identifies 'and 255' on 32-bit load as
-; zero-extending byte load (llc).
-define void @and_i32_ni_max_byte(ptr %ptr) {
-; CHECK-LABEL: and_i32_ni_max_byte:
-; CHECK: llc [[REG:%r[0-9]+]], 3(%r2)
-; CHECK: st [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 255
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @and_i16_neg_2(ptr %src) {
-; CHECK-LABEL: and_i16_neg_2:
-; CHECK-NOT: ni {{.*}}, 254
-; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: nilf [[REG]], 65534
-; CHECK: sth [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i16, ptr %src
- %and = and i16 %val, -2
- store i16 %and, ptr %src
- ret void
-}
-
-define void @and_i32_neg_2(ptr %src) {
-; CHECK-LABEL: and_i32_neg_2:
-; CHECK-NOT:niy {{.*}}, 254
-; CHECK: n [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %src
- %and = and i32 %val, -2
- store i32 %and, ptr %src
- ret void
-}
-
-; DAG Combiner already combines it into a single And Memory (ng) instruction.
-define void @and_i64_neg_2(ptr %src) {
-; CHECK-LABEL: and_i64_neg_2:
-; CHECK: ng %r0, 0(%r2)
-; CHECK: br %r14
- %val = load i64, ptr %src
- %and = and i64 %val, -2
- store i64 %and, ptr %src
- ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to ni/niy.
-define void @and_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: and_i32_too_wide:
-; CHECK-NOT: ni{{y?}} {{.*}}, 256
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 256
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @and_i32_niy_boundary_128(ptr %src) {
-; CHECK-LABEL: and_i32_niy_boundary_128:
-; CHECK: niy 4096(%r2), 128
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %and = and i32 %val, 128
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check (max - 1): 254 (0xFE) - All but one bit set in the target byte.
-define void @and_i32_niy_max_byte_minus_1(ptr %src) {
-; CHECK-LABEL: and_i32_niy_max_byte_minus_1:
-; CHECK: niy 4096(%r2), 254
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %and = and i32 %val, 254
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-; DAG Combiner transforms the 4-byte load into an 8-bit load logical (llc).
-define void @and_i32_niy_max_byte(ptr %src) {
-; CHECK-LABEL: and_i32_niy_max_byte:
-; CHECK: llc [[REG:%r[0-9]+]], 4096(%r2)
-; CHECK: st [[REG]], 4093(%r2)
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %xor = and i32 %val, 255
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i16 high end of NI range: Original 4095 + 1 (LSB) = 4095.
-define void @and_i16_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i16_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4094
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32 high end of NI range: Original 4092 + 3 (LSB) = 4095.
-define void @and_i32_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i32_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4092
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64 high end of NI range: Original 4088 + 7 (LSB) = 4095.
-define void @and_i64_ni_high_disp(ptr %src) {
-; CHECK-LABEL: and_i64_ni_high_disp:
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4088
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check i16 transisition to niy: Original 4095 + 1 (LSB) = 4096 (triggers NIY).
-define void @and_i16_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32 transisition to niy: Original 4093 + 3 (LSB) = 4096 (triggers NIY).
-define void @and_i32_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64 transisition to niy: Original 4089 + 7 (LSB) = 4096 (triggers NIY).
-define void @and_i64_niy_transition_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_transition_disp:
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4089
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check i16 high end of the NIY range: Original 524286 + 1 (LSB) = 524287.
-define void @and_i16_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524286
- %val = load i16, ptr %ptr
- %and = and i16 %val, 127
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32 high end of the NIY range: Original 524284 + 3 (LSB) = 524287.
-define void @and_i32_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524284
- %val = load i32, ptr %ptr
- %and = and i32 %val, 127
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64 high end of the NIY range: Original 524280 + 7 (LSB) = 524287.
-define void @and_i64_niy_high_end_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_high_end_disp:
-; CHECK:niy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524280
- %val = load i64, ptr %ptr
- %and = and i64 %val, 127
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i16_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i16, ptr %ptr
- %and = and i16 %val, 127
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i32_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i32, ptr %ptr
- %and = and i32 %val, 127
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i64_niy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i64, ptr %ptr
- %and = and i64 %val, 127
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @and_i16_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -2
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @and_i32_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -4
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @and_i64_niy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_max_disp:
-; CHECK:niy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -8
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i16_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_min_disp:
-; CHECK:niy -524287(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i32_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_min_disp:
-; CHECK:niy -524285(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @and_i64_niy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_min_disp:
-; CHECK:niy -524281(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i16_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i16_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i32_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i32_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @and_i64_niy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: and_i64_niy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: ni 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check that NI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for NI).
-define void @and_i16_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i16_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4094
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check that NI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for NI).
-define void @and_i32_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i32_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4092
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check that NI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for NI).
-define void @and_i64_ni_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i64_ni_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: ni 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4088
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Check that NIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers NIY).
-define void @and_i16_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i16_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %and = and i16 %val, 1
- store i16 %and, ptr %ptr
- ret void
-}
-
-; Check that NIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers NIY).
-define void @and_i32_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i32_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4093
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret void
-}
-
-; Check that NIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers NIY).
-define void @and_i64_niy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: and_i64_niy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK:niy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4089
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %and = and i64 %val, 1
- store i64 %and, ptr %ptr
- ret void
-}
-
-; Volatile memory should not be folded into NI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: ni {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load volatile i32, ptr %ptr
- %and = and i32 %val, 1
- store volatile i32 %and, ptr %ptr
- ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: nilf {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %and = and i32 %val, 1
- store i32 %and, ptr %ptr
- ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 5f28480966e10..22b73ff41ff39 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -2,7 +2,7 @@
;
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
define void @f1(ptr %ptr) {
; CHECK-LABEL: f1:
; CHECK: ni 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
ret void
}
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: nill %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, -255
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, -255
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi %r0, -255
+; CHECK: ng %r0, 0(%r2)
+; CHECK: stg %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, -255
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
define void @f2(ptr %ptr) {
; CHECK-LABEL: f2:
; CHECK: ni 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
ret void
}
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: ni {{[0-9]+}}(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: nilf %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, -2
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, -2
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi %r0, -2
+; CHECK: ng %r0, 0(%r2)
+; CHECK: stg %r0, 0(%r2)
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, -2
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
define void @f3(ptr %ptr) {
; CHECK-LABEL: f3:
; CHECK: ni 0(%r2), 1
@@ -35,6 +117,39 @@ define void @f3(ptr %ptr) {
ret void
}
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: ni 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 1
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: ni 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 1
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: ni 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 1
+ store i64 %and, ptr %ptr
+ ret void
+}
+
; Check the highest useful constant, expressed as a unsigned integer.
define void @f4(ptr %ptr) {
; CHECK-LABEL: f4:
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
ret void
}
-; Check the high end of the NI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: ni 1(%r2), 254
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 254
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 254
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: ni 7(%r2), 254
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 254
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NI range for i8.
define void @f5(ptr %src) {
; CHECK-LABEL: f5:
; CHECK: ni 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
ret void
}
-; Check the next byte up, which should use NIY instead of NI.
+; Check the high end of the NI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: ni 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: ni 4098(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: ni 4102(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which should use NIY instead of NI.
define void @f6(ptr %src) {
; CHECK-LABEL: f6:
; CHECK: niy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
ret void
}
-; Check the high end of the NIY range.
+; Check the next byte up for i16, which should use NIY instead of NI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: niy 4097(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which should use NIY instead of NI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: niy 4099(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which should use NIY instead of NI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: niy 4103(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NIY range for i8.
define void @f7(ptr %src) {
; CHECK-LABEL: f7:
; CHECK: niy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
ret void
}
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the NIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: niy 524288(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: niy 524290(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the NIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: niy 524294(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f8(ptr %src) {
; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
ret void
}
-; Check the high end of the negative NIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: ni 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative NIY range for i8.
define void @f9(ptr %src) {
; CHECK-LABEL: f9:
; CHECK: niy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
ret void
}
-; Check the low end of the NIY range.
+; Check the high end of the negative NIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: niy 0(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative NIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: niy 2(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative NIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: niy 6(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the low end of the NIY range for i8.
define void @f10(ptr %src) {
; CHECK-LABEL: f10:
; CHECK: niy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
ret void
}
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the NIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: niy -524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the low end of the NIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: niy -524285(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the low end of the NIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: niy -524281(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f11(ptr %src) {
; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
ret void
}
-; Check that NI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: ni 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check that NI does not allow an index for i8.
define void @f12(i64 %src, i64 %index) {
; CHECK-LABEL: f12:
; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
ret void
}
-; Check that NIY does not allow an index
+; Check that NI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4096(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the NI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4098(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the NI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: ni 4102(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check that NIY does not allow an index for i8.
define void @f13(i64 %src, i64 %index) {
; CHECK-LABEL: f13:
; CHECK: agr %r2, %r3
@@ -163,3 +620,48 @@ define void @f13(i64 %src, i64 %index) {
store i8 %and, ptr %ptr
ret void
}
+
+; Check the NIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4097(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 127
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check the NIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4099(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 127
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check the NIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: niy 4103(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, 127
+ store i64 %and, ptr %ptr
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/or-05-a.ll b/llvm/test/CodeGen/SystemZ/or-05-a.ll
deleted file mode 100644
index e87e1deb3f6ea..0000000000000
--- a/llvm/test/CodeGen/SystemZ/or-05-a.ll
+++ /dev/null
@@ -1,536 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; or-05.ll tests the multiclass RMWIByte for i8 types.
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (OI) and 20-bit signed (OIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
-; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
-; legalization in the address matcher before folding can occur.
-; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
-; be reached via a starting GEP offset of 524287, as the Big-Endian
-; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-; Negative constants fail to fold for multi-byte types because they are
-; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @or_i16_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i16_oi_low_unsigned:
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @or_i32_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_low_unsigned:
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @or_i64_oi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: or_i64_oi_low_unsigned:
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @or_i32_oi_boundary_128(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_boundary_128:
-; CHECK: oi 3(%r2), 128
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %or = or i32 %val, 128
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @or_i32_oi_max_byte(ptr %ptr) {
-; CHECK-LABEL: or_i32_oi_max_byte:
-; CHECK: oi 3(%r2), 255
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %or = or i32 %val, 255
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @or_i16_neg_2(ptr %src) {
-; CHECK-LABEL: or_i16_neg_2:
-; CHECK-NOT: oi {{.*}}, 254
-; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: oill [[REG]], 65534
-; CHECK: sth [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i16, ptr %src
- %or = or i16 %val, -2
- store i16 %or, ptr %src
- ret void
-}
-
-define void @or_i32_neg_2(ptr %src) {
-; CHECK-LABEL: or_i32_neg_2:
-; CHECK-NOT: oiy {{.*}}, 254
-; CHECK: o [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %src
- %or = or i32 %val, -2
- store i32 %or, ptr %src
- ret void
-}
-
-define void @or_i64_neg_2(ptr %src) {
-; CHECK-LABEL: or_i64_neg_2:
-; CHECK-NOT: oiy {{.*}}, 254
-; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: stg [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i64, ptr %src
- %or = or i64 %val, -2
- store i64 %or, ptr %src
- ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to oi/oiy.
-define void @or_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: or_i32_too_wide:
-; CHECK-NOT: oi{{y?}} {{.*}}, 256
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %or = or i32 %val, 256
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @or_i32_oiy_boundary_128(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_boundary_128:
-; CHECK: oiy 4096(%r2), 128
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %or = or i32 %val, 128
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @or_i32_oiy_max_byte(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_max_byte:
-; CHECK: oiy 4096(%r2), 255
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %or = or i32 %val, 255
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i16 high end of OI range: Original 4095 + 1 (LSB) = 4095.
-define void @or_i16_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4094
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32 high end of OI range: Original 4092 + 3 (LSB) = 4095.
-define void @or_i32_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4092
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64 high end of OI range: Original 4088 + 7 (LSB) = 4095.
-define void @or_i64_oi_high_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oi_high_disp:
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4088
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check i16 transisition to oiy: Original 4095 + 1 (LSB) = 4096 (triggers OIY).
-define void @or_i16_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32 transisition to oiy: Original 4093 + 3 (LSB) = 4096 (triggers OIY).
-define void @or_i32_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64 transisition to oiy: Original 4089 + 7 (LSB) = 4096 (triggers OIY).
-define void @or_i64_oiy_transition_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_transition_disp:
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4089
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check i16 high end of the OIY range: Original 524286 + 1 (LSB) = 524287.
-define void @or_i16_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524286
- %val = load i16, ptr %ptr
- %or = or i16 %val, 127
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32 high end of the OIY range: Original 524284 + 3 (LSB) = 524287.
-define void @or_i32_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524284
- %val = load i32, ptr %ptr
- %or = or i32 %val, 127
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64 high end of the OIY range: Original 524280 + 7 (LSB) = 524287.
-define void @or_i64_oiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_high_end_disp:
-; CHECK: oiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524280
- %val = load i64, ptr %ptr
- %or = or i64 %val, 127
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i16_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i16, ptr %ptr
- %or = or i16 %val, 127
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i32_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i32, ptr %ptr
- %or = or i32 %val, 127
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i64_oiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i64, ptr %ptr
- %or = or i64 %val, 127
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @or_i16_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -2
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @or_i32_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -4
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @or_i64_oiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_max_disp:
-; CHECK: oiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -8
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i16_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_min_disp:
-; CHECK: oiy -524287(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i32_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_min_disp:
-; CHECK: oiy -524285(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @or_i64_oiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_min_disp:
-; CHECK: oiy -524281(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i16_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i16_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i32_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i32_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @or_i64_oiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: or_i64_oiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: oi 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check that OI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for OI).
-define void @or_i16_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i16_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4094
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check that OI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for OI).
-define void @or_i32_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i32_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4092
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check that OI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for OI).
-define void @or_i64_oi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i64_oi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4088
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Check that OIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers OIY).
-define void @or_i16_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i16_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %or = or i16 %val, 1
- store i16 %or, ptr %ptr
- ret void
-}
-
-; Check that OIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers OIY).
-define void @or_i32_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i32_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4093
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret void
-}
-
-; Check that OIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers OIY).
-define void @or_i64_oiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: or_i64_oiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: oiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4089
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %or = or i64 %val, 1
- store i64 %or, ptr %ptr
- ret void
-}
-
-; Volatile memory should not be folded into OI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: oi {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load volatile i32, ptr %ptr
- %or = or i32 %val, 1
- store volatile i32 %or, ptr %ptr
- ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: oill {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %or = or i32 %val, 1
- store i32 %or, ptr %ptr
- ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index 6ec352fa7b419..e6b05a77f1f6f 100644
--- a/llvm/test/CodeGen/SystemZ/or-05.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05.ll
@@ -2,7 +2,7 @@
;
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
define void @f1(ptr %ptr) {
; CHECK-LABEL: f1:
; CHECK: oi 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
ret void
}
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: oi 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: oill %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, -255
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: oi 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: o %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, -255
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: oi 7(%r2)
+; CHECK: lg {{%r[0-9]+}}, 0(%r2)
+; CHECK: oihf %r0, 4294967295
+; CHECK: stg {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, -255
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
define void @f2(ptr %ptr) {
; CHECK-LABEL: f2:
; CHECK: oi 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
ret void
}
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: oi 1(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: oill %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, -2
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: oi 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: o %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, -2
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: oi 7(%r2)
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: oilf [[REG]], 4294967294
+; CHECK: stg [[REG]], 0(%r2)
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, -2
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
define void @f3(ptr %ptr) {
; CHECK-LABEL: f3:
; CHECK: oi 0(%r2), 1
@@ -35,7 +117,40 @@ define void @f3(ptr %ptr) {
ret void
}
-; Check the highest useful constant, expressed as a unsigned integer.
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: oi 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 1
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: oi 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 1
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: oi 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 1
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant i8, expressed as a unsigned integer.
define void @f4(ptr %ptr) {
; CHECK-LABEL: f4:
; CHECK: oi 0(%r2), 254
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
ret void
}
-; Check the high end of the OI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: oi 1(%r2), 254
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 254
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: oi 3(%r2), 254
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 254
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: oi 7(%r2), 254
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 254
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OI range for i8.
define void @f5(ptr %src) {
; CHECK-LABEL: f5:
; CHECK: oi 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
ret void
}
-; Check the next byte up, which should use OIY instead of OI.
+; Check the high end of the OI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: oi 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: oi 4098(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: oi 4102(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which should use OIY instead of OI.
define void @f6(ptr %src) {
; CHECK-LABEL: f6:
; CHECK: oiy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
ret void
}
-; Check the high end of the OIY range.
+; Check the next byte up for i16, which should use OIY instead of OI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: oiy 4097(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which should use OIY instead of OI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: oiy 4099(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which should use OIY instead of OI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: oiy 4103(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OIY range for i8.
define void @f7(ptr %src) {
; CHECK-LABEL: f7:
; CHECK: oiy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
ret void
}
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the OIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: oiy 524288(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: oiy 524290(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the OIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: oiy 524294(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f8(ptr %src) {
; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
ret void
}
-; Check the high end of the negative OIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: oi 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative OIY range for i8.
define void @f9(ptr %src) {
; CHECK-LABEL: f9:
; CHECK: oiy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
ret void
}
-; Check the low end of the OIY range.
+; Check the high end of the negative OIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: oiy 0(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative OIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: oiy 2(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative OIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: oiy 6(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the low end of the OIY range for i8.
define void @f10(ptr %src) {
; CHECK-LABEL: f10:
; CHECK: oiy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
ret void
}
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the OIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: oiy -524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the low end of the OIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: oiy -524285(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the low end of the OIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: oiy -524281(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f11(ptr %src) {
; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
ret void
}
-; Check that OI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: oi 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check that OI does not allow an index for i8.
define void @f12(i64 %src, i64 %index) {
; CHECK-LABEL: f12:
; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
ret void
}
-; Check that OIY does not allow an index
+; Check the OI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4096(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the OI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4098(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the OI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: oi 4102(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
+
+; Check that OIY does not allow an index for i8.
define void @f13(i64 %src, i64 %index) {
; CHECK-LABEL: f13:
; CHECK: agr %r2, %r3
@@ -163,3 +620,48 @@ define void @f13(i64 %src, i64 %index) {
store i8 %or, ptr %ptr
ret void
}
+
+; Check the OIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4097(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %or = or i16 %val, 127
+ store i16 %or, ptr %ptr
+ ret void
+}
+
+; Check the OIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4099(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %or = or i32 %val, 127
+ store i32 %or, ptr %ptr
+ ret void
+}
+
+; Check the OIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: oiy 4103(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %or = or i64 %val, 127
+ store i64 %or, ptr %ptr
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05-a.ll b/llvm/test/CodeGen/SystemZ/xor-05-a.ll
deleted file mode 100644
index 5b19d66ca50bf..0000000000000
--- a/llvm/test/CodeGen/SystemZ/xor-05-a.ll
+++ /dev/null
@@ -1,536 +0,0 @@
-; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-
-; xor-05.ll tests the multiclass RMWIByte for i8 types.
-; This test verifies the DAGToDag implementation for i16, i32, and i64 types,
-; covering both 12-bit unsigned (XI) and 20-bit signed (XIY) displacements.
-
-; There are certain liomitations:
-; 1. Displacement Range Limitations:
-; - Minimum: For multi-byte types, the absolute minimum 20-bit displacement
-; (-524288) cannot be folded. A GEP offset < -524288 triggers AGFI
-; legalization in the address matcher before folding can occur.
-; - Maximum: Similarly, the absolute maximum displacement (524287) cannot
-; be reached via a starting GEP offset of 524287, as the Big-Endian
-; adjustment (+1, +3, or +7) would overflow the 20-bit signed range.
-;
-; 2. Negative Constant Folding Failures:
-; Negative constants fail to fold for multi-byte types because they are
-; sign-extended (e.g., i16 -2 is 0xFFFE). These values have more than 8
-; active bits, whereas NI/OI/XI instructions only accept an 8-bit immediate.
-
-; Check i16 (short): Offset + 1.
-define void @xor_i16_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i16_xi_low_unsigned:
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32 (int): Offset + 3.
-define void @xor_i32_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_low_unsigned:
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64 (int): Offset + 7.
-define void @xor_i64_xi_low_unsigned(ptr %ptr) {
-; CHECK-LABEL: xor_i64_xi_low_unsigned:
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check High: 128 (0x80) - The "signed bit" of a byte.
-; Verifies that patterns with the 8th bit set still fold.
-define void @xor_i32_xi_boundary_128(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_boundary_128:
-; CHECK: xi 3(%r2), 128
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 128
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @xor_i32_xi_max_byte(ptr %ptr) {
-; CHECK-LABEL: xor_i32_xi_max_byte:
-; CHECK: xi 3(%r2), 255
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 255
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check bit-width constraint with negative signed value.
-; This test case with -2 (0xFF...FE) fails to fold because the immediate value
-; has more than 8 active bits (due to sign extension in i16/i32/i64). SystemZ
-; logical-to-memory instructions (NI, OI, XI) only operate on a single byte.
-define void @xor_i16_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i16_neg_2:
-; CHECK-NOT: xi {{.*}}, 254
-; CHECK: lh [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: xilf [[REG]], 65534
-; CHECK: sth [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i16, ptr %src
- %xor = xor i16 %val, -2
- store i16 %xor, ptr %src
- ret void
-}
-
-define void @xor_i32_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i32_neg_2:
-; CHECK-NOT: xiy {{.*}}, 254
-; CHECK: x [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %src
- %xor = xor i32 %val, -2
- store i32 %xor, ptr %src
- ret void
-}
-
-define void @xor_i64_neg_2(ptr %src) {
-; CHECK-LABEL: xor_i64_neg_2:
-; CHECK-NOT: xiy {{.*}}, 254
-; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: stg [[REG]], 0(%r2)
-; CHECK: br %r14
- %val = load i64, ptr %src
- %xor = xor i64 %val, -2
- store i64 %xor, ptr %src
- ret void
-}
-
-; Check out of Range: 256 (0x100) - Requires 9 bits.
-; Should not fold to xi/xiy.
-define void @xor_i32_too_wide(ptr %ptr) {
-; CHECK-LABEL: xor_i32_too_wide:
-; CHECK-NOT: xi{{y?}} {{.*}}, 256
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 256
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check high: 128 (0x80) - The "signed bit" of a byte.
-define void @xor_i32_xiy_boundary_128(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_boundary_128:
-; CHECK: xiy 4096(%r2), 128
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 128
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check max: 255 (0xFF) - All bits set in the target byte.
-define void @xor_i32_xiy_max_byte(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_max_byte:
-; CHECK: xiy 4096(%r2), 255
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 255
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i16 high end of XI range: Original 4095 + 1 (LSB) = 4095.
-define void @xor_i16_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4094
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32 high end of XI range: Original 4092 + 3 (LSB) = 4095.
-define void @xor_i32_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4092
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64 high end of XI range: Original 4088 + 7 (LSB) = 4095.
-define void @xor_i64_xi_high_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xi_high_disp:
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4088
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check i16 transisition to xiy: Original 4095 + 1 (LSB) = 4096 (triggers XIY).
-define void @xor_i16_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32 transisition to xiy: Original 4093 + 3 (LSB) = 4096 (triggers XIY).
-define void @xor_i32_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4093
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64 transisition to xiy: Original 4089 + 7 (LSB) = 4096 (triggers XIY).
-define void @xor_i64_xiy_transition_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_transition_disp:
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4089
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check i16 high end of the XIY range: Original 524286 + 1 (LSB) = 524287.
-define void @xor_i16_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524286
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 127
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32 high end of the XIY range: Original 524284 + 3 (LSB) = 524287.
-define void @xor_i32_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524284
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 127
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64 high end of the XIY range: Original 524280 + 7 (LSB) = 524287.
-define void @xor_i64_xiy_high_end_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_high_end_disp:
-; CHECK: xiy 524287(%r2), 127
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524280
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 127
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i16_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 127
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i32_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 127
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte up - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i64_xiy_pos_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_pos_out_of_range_disp:
-; CHECK: agfi %r2, 524288
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524288
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 127
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check i16: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i16_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -2
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i32_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -4
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64: High end of the negative Displacement (Signed 20-bit).
-define void @xor_i64_xiy_neg_max_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_max_disp:
-; CHECK: xiy -1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -8
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check i16: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i16_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_min_disp:
-; CHECK: xiy -524287(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check i32: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i32_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_min_disp:
-; CHECK: xiy -524285(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check i64: Low end of the negative Displacement (Signed 20-bit).
-define void @xor_i64_xiy_neg_min_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_min_disp:
-; CHECK: xiy -524281(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i16, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i16_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i16_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 1(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i32, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i32_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i32_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 3(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check the next byte down - i64, which needs separate address logic.
-; Other sequences besides this one would be OK.
-define void @xor_i64_xiy_neg_out_of_range_disp(ptr %src) {
-; CHECK-LABEL: xor_i64_xiy_neg_out_of_range_disp:
-; CHECK: agfi %r2, -524289
-; CHECK: xi 7(%r2), 1
-; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check that XI does not allow an index for i16.
-; Original 4094 + 1 (LSB) = 4095 (Max range for XI).
-define void @xor_i16_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i16_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4094
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check that XI does not allow an index for i32.
-; Original 4092 + 3 (LSB) = 4095 (Max range for XI).
-define void @xor_i32_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i32_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4092
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check that XI does not allow an index for i64.
-; Original 4088 + 7 (LSB) = 4095 (Max range for XI).
-define void @xor_i64_xi_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i64_xi_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xi 4095(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4088
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Check that XIY does not allow an index for i16.
-; Original 4095 + 1 (LSB) = 4096 (triggers XIY).
-define void @xor_i16_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i16_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i16, ptr %ptr
- %xor = xor i16 %val, 1
- store i16 %xor, ptr %ptr
- ret void
-}
-
-; Check that XIY does not allow an index for i32.
-; Original 4093 + 3 (LSB) = 4096 (triggers XIY).
-define void @xor_i32_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i32_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4093
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret void
-}
-
-; Check that XIY does not allow an index for i64.
-; Original 4089 + 7 (LSB) = 4096 (triggers XIY).
-define void @xor_i64_xiy_no_index(i64 %src, i64 %index) {
-; CHECK-LABEL: xor_i64_xiy_no_index:
-; CHECK: agr %r2, %r3
-; CHECK: xiy 4096(%r2), 1
-; CHECK: br %r14
- %add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4089
- %ptr = inttoptr i64 %add2 to ptr
- %val = load i64, ptr %ptr
- %xor = xor i64 %val, 1
- store i64 %xor, ptr %ptr
- ret void
-}
-
-; Volatile memory should not be folded into XI.
-define i32 @test_volatile(ptr %ptr) {
-; CHECK-LABEL: test_volatile:
-; CHECK-NOT: xi {{.*}}, 1
-; CHECK: l {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load volatile i32, ptr %ptr
- %xor = xor i32 %val, 1
- store volatile i32 %xor, ptr %ptr
- ret i32 %val
-}
-
-; Multiple uses of the loaded value should not be folded.
-define i32 @test_multi_use(ptr %ptr) {
-; CHECK-LABEL: test_multi_use:
-; CHECK: l [[REG:%r[0-9]+]], 0(%r2)
-; CHECK: xilf {{%r[0-9]+}}, 1
-; CHECK: st {{%r[0-9]+}}, 0(%r2)
-; CHECK: br %r14
- %val = load i32, ptr %ptr
- %xor = xor i32 %val, 1
- store i32 %xor, ptr %ptr
- ret i32 %val
-}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 5640e236793e5..50bc759837d79 100644
--- a/llvm/test/CodeGen/SystemZ/xor-05.ll
+++ b/llvm/test/CodeGen/SystemZ/xor-05.ll
@@ -2,7 +2,7 @@
;
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
-; Check the lowest useful constant, expressed as a signed integer.
+; Check the lowest useful constant for i8, expressed as a signed integer.
define void @f1(ptr %ptr) {
; CHECK-LABEL: f1:
; CHECK: xi 0(%r2), 1
@@ -13,7 +13,49 @@ define void @f1(ptr %ptr) {
ret void
}
-; Check the highest useful constant, expressed as a signed integer.
+; Check lowest useful constant for i16, expressed as a signed integer.
+define void @f1_i16(ptr %ptr) {
+; CHECK-LABEL: f1_i16:
+; CHECK-NOT: xi 1(%r2)
+; CHECK: lh %r0, 0(%r2)
+; CHECK: xilf %r0, 65281
+; CHECK: sth %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, -255
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i32, expressed as a signed integer.
+define void @f1_i32(ptr %ptr) {
+; CHECK-LABEL: f1_i32:
+; CHECK-NOT: xi 3(%r2)
+; CHECK: lhi %r0, -255
+; CHECK: x %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, -255
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check lowest useful constant for i64, expressed as a signed integer.
+define void @f1_i64(ptr %ptr) {
+; CHECK-LABEL: f1_i64:
+; CHECK-NOT: xi 7(%r2)
+; CHECK: lg {{%r[0-9]+}}, 0(%r2)
+; CHECK: xihf %r0, 4294967295
+; CHECK: stg {{%r[0-9]+}}, 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, -255
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i8, expressed as a signed integer.
define void @f2(ptr %ptr) {
; CHECK-LABEL: f2:
; CHECK: xi 0(%r2), 254
@@ -24,7 +66,47 @@ define void @f2(ptr %ptr) {
ret void
}
-; Check the lowest useful constant, expressed as an unsigned integer.
+; Check the highest useful constant for i16, expressed as a signed integer.
+define void @f2_i16(ptr %ptr) {
+; CHECK-LABEL: f2_i16:
+; CHECK-NOT: xi 1(%r2), 254
+; CHECK: lh %r0, 0(%r2)
+; CHECK: xilf %r0, 65534
+; CHECK: sth %r0, 0(%r2)
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, -2
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a signed integer.
+define void @f2_i32(ptr %ptr) {
+; CHECK-LABEL: f2_i32:
+; CHECK-NOT: xi 3(%r2)
+; CHECK: lhi %r0, -2
+; CHECK: x %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, -2
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a signed integer.
+define void @f2_i64(ptr %ptr) {
+; CHECK-LABEL: f2_i64:
+; CHECK-NOT: xi 7(%r2)
+; CHECK: lg [[REG:%r[0-9]+]], 0(%r2)
+; CHECK: xilf [[REG]], 4294967294
+; CHECK: stg [[REG]], 0(%r2)
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, -2
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i8, expressed as an unsigned integer.
define void @f3(ptr %ptr) {
; CHECK-LABEL: f3:
; CHECK: xi 0(%r2), 1
@@ -35,7 +117,40 @@ define void @f3(ptr %ptr) {
ret void
}
-; Check the highest useful constant, expressed as a unsigned integer.
+; Check the lowest useful constant for i16, expressed as an unsigned integer.
+define void @f3_i16(ptr %ptr) {
+; CHECK-LABEL: f3_i16:
+; CHECK: xi 1(%r2), 1
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 1
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i32, expressed as an unsigned integer.
+define void @f3_i32(ptr %ptr) {
+; CHECK-LABEL: f3_i32:
+; CHECK: xi 3(%r2), 1
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 1
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the lowest useful constant for i64, expressed as an unsigned integer.
+define void @f3_i64(ptr %ptr) {
+; CHECK-LABEL: f3_i64:
+; CHECK: xi 7(%r2), 1
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 1
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i8, expressed as a unsigned integer.
define void @f4(ptr %ptr) {
; CHECK-LABEL: f4:
; CHECK: xi 0(%r2), 254
@@ -46,7 +161,40 @@ define void @f4(ptr %ptr) {
ret void
}
-; Check the high end of the XI range.
+; Check the highest useful constant for i16, expressed as a unsigned integer.
+define void @f4_i16(ptr %ptr) {
+; CHECK-LABEL: f4_i16:
+; CHECK: xi 1(%r2), 254
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 254
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i32, expressed as a unsigned integer.
+define void @f4_i32(ptr %ptr) {
+; CHECK-LABEL: f4_i32:
+; CHECK: xi 3(%r2), 254
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 254
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the highest useful constant for i64, expressed as a unsigned integer.
+define void @f4_i64(ptr %ptr) {
+; CHECK-LABEL: f4_i64:
+; CHECK: xi 7(%r2), 254
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 254
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XI range for i8.
define void @f5(ptr %src) {
; CHECK-LABEL: f5:
; CHECK: xi 4095(%r2), 127
@@ -58,7 +206,43 @@ define void @f5(ptr %src) {
ret void
}
-; Check the next byte up, which should use XIY instead of XI.
+; Check the high end of the XI range for i16.
+define void @f5_i16(ptr %src) {
+; CHECK-LABEL: f5_i16:
+; CHECK: xi 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XI range for i32.
+define void @f5_i32(ptr %src) {
+; CHECK-LABEL: f5_i32:
+; CHECK: xi 4098(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XI range for i64.
+define void @f5_i64(ptr %src) {
+; CHECK-LABEL: f5_i64:
+; CHECK: xi 4102(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which should use XIY instead of XI.
define void @f6(ptr %src) {
; CHECK-LABEL: f6:
; CHECK: xiy 4096(%r2), 127
@@ -70,7 +254,43 @@ define void @f6(ptr %src) {
ret void
}
-; Check the high end of the XIY range.
+; Check the next byte up for i16, which should use XIY instead of XI.
+define void @f6_i16(ptr %src) {
+; CHECK-LABEL: f6_i16:
+; CHECK: xiy 4097(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which should use XIY instead of XI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: xiy 4099(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which should use XIY instead of XI.
+define void @f6_i64(ptr %src) {
+; CHECK-LABEL: f6_i64:
+; CHECK: xiy 4103(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4096
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XIY range for i8.
define void @f7(ptr %src) {
; CHECK-LABEL: f7:
; CHECK: xiy 524287(%r2), 127
@@ -82,7 +302,43 @@ define void @f7(ptr %src) {
ret void
}
-; Check the next byte up, which needs separate address logic.
+; Check the high end of the XIY range for i16.
+define void @f7_i16(ptr %src) {
+; CHECK-LABEL: f7_i16:
+; CHECK: xiy 524288(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; CHECK: xiy 524290(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the XIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; CHECK: xiy 524294(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524287
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f8(ptr %src) {
; CHECK-LABEL: f8:
@@ -96,7 +352,49 @@ define void @f8(ptr %src) {
ret void
}
-; Check the high end of the negative XIY range.
+; Check the next byte up for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i16(ptr %src) {
+; CHECK-LABEL: f8_i16:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i32(ptr %src) {
+; CHECK-LABEL: f8_i32:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte up for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f8_i64(ptr %src) {
+; CHECK-LABEL: f8_i64:
+; CHECK: agfi %r2, 524288
+; CHECK: xi 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 524288
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative XIY range for i8.
define void @f9(ptr %src) {
; CHECK-LABEL: f9:
; CHECK: xiy -1(%r2), 127
@@ -108,7 +406,43 @@ define void @f9(ptr %src) {
ret void
}
-; Check the low end of the XIY range.
+; Check the high end of the negative XIY range for i16.
+define void @f9_i16(ptr %src) {
+; CHECK-LABEL: f9_i16:
+; CHECK: xiy 0(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative XIY range for i32.
+define void @f9_i32(ptr %src) {
+; CHECK-LABEL: f9_i32:
+; CHECK: xiy 2(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the high end of the negative XIY range for i64.
+define void @f9_i64(ptr %src) {
+; CHECK-LABEL: f9_i64:
+; CHECK: xiy 6(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -1
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the low end of the XIY range for i8.
define void @f10(ptr %src) {
; CHECK-LABEL: f10:
; CHECK: xiy -524288(%r2), 127
@@ -120,7 +454,43 @@ define void @f10(ptr %src) {
ret void
}
-; Check the next byte down, which needs separate address logic.
+; Check the low end of the XIY range for i16.
+define void @f10_i16(ptr %src) {
+; CHECK-LABEL: f10_i16:
+; CHECK: xiy -524287(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the low end of the XIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: xiy -524285(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the low end of the XIY range for i64.
+define void @f10_i64(ptr %src) {
+; CHECK-LABEL: f10_i64:
+; CHECK: xiy -524281(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524288
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i8, which needs separate address logic.
; Other sequences besides this one would be OK.
define void @f11(ptr %src) {
; CHECK-LABEL: f11:
@@ -134,7 +504,49 @@ define void @f11(ptr %src) {
ret void
}
-; Check that XI does not allow an index
+; Check the next byte down for i16, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i16(ptr %src) {
+; CHECK-LABEL: f11_i16:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 1(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i32, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i32(ptr %src) {
+; CHECK-LABEL: f11_i32:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 3(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the next byte down for i64, which needs separate address logic.
+; Other sequences besides this one would be OK.
+define void @f11_i64(ptr %src) {
+; CHECK-LABEL: f11_i64:
+; CHECK: agfi %r2, -524289
+; CHECK: xi 7(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 -524289
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XI does not allow an index for i8.
define void @f12(i64 %src, i64 %index) {
; CHECK-LABEL: f12:
; CHECK: agr %r2, %r3
@@ -149,7 +561,52 @@ define void @f12(i64 %src, i64 %index) {
ret void
}
-; Check that XIY does not allow an index
+; Check the XI does not allow an index for i16.
+define void @f12_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i16:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4096(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the XI does not allow an index for i32.
+define void @f12_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i32:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4098(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the XI does not allow an index for i64.
+define void @f12_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f12_i64:
+; CHECK: agr %r2, %r3
+; CHECK: xi 4102(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4095
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
+; Check that XIY does not allow an index for i8.
define void @f13(i64 %src, i64 %index) {
; CHECK-LABEL: f13:
; CHECK: agr %r2, %r3
@@ -163,3 +620,49 @@ define void @f13(i64 %src, i64 %index) {
store i8 %xor, ptr %ptr
ret void
}
+
+; Check the XIY does not allow an index for i16.
+define void @f13_i16(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i16:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4097(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i16, ptr %ptr
+ %xor = xor i16 %val, 127
+ store i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check the XIY does not allow an index for i32.
+define void @f13_i32(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i32:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4099(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i32, ptr %ptr
+ %xor = xor i32 %val, 127
+ store i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check the XIY does not allow an index for i64.
+define void @f13_i64(i64 %src, i64 %index) {
+; CHECK-LABEL: f13_i64:
+; CHECK: agr %r2, %r3
+; CHECK: xiy 4103(%r2), 127
+; CHECK: br %r14
+ %add1 = add i64 %src, %index
+ %add2 = add i64 %add1, 4096
+ %ptr = inttoptr i64 %add2 to ptr
+ %val = load i64, ptr %ptr
+ %xor = xor i64 %val, 127
+ store i64 %xor, ptr %ptr
+ ret void
+}
+
>From 47c9a99466049668f4feb152274607eaaeac6270 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Fri, 22 May 2026 16:48:15 +0200
Subject: [PATCH 05/11] [SystemZ] Address code review feedback for folding
i16/i32/i64 logical RMW operations into memory operands - Consolidate
RMWIByte multiclass to handle both i8 and wider types. - RMWIByteLSB
multiclass for i16/i32/i63 with conditional selection based on operation
type. - Extended selectBDAddr to accept offest parameter for wrapper
functions selectBDAddr12/20off1/3/7 to apply fixed byte offsets for LSB
access (i16: +1, i32: +3, i64: +7). - Added immAndLSB8 pattern matching
for valid constants for 'and' operation.
---
.../Target/SystemZ/SystemZISelDAGToDAG.cpp | 41 ++-
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 15 +-
llvm/lib/Target/SystemZ/SystemZOperands.td | 58 ++--
llvm/lib/Target/SystemZ/SystemZOperators.td | 56 ++--
llvm/lib/Target/SystemZ/SystemZPatterns.td | 109 ++++----
llvm/test/CodeGen/SystemZ/and-05.ll | 253 ++++++++++++++----
6 files changed, 336 insertions(+), 196 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index bb148c2dc2ebd..992ad39690fc8 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -165,7 +165,7 @@ class SystemZDAGToDAGISel : public SelectionDAGISel {
// Return true on success, storing the base and displacement in
// Base and Disp respectively.
bool selectBDAddr(SystemZAddressingMode::DispRange DR, SDValue Addr,
- SDValue &Base, SDValue &Disp) const;
+ SDValue &Base, SDValue &Disp, int64_t Offset = 0) const;
// Try to match Addr as a FormBDX address with displacement type DR.
// Return true on success and if the result had no index. Store the
@@ -267,6 +267,15 @@ class SystemZDAGToDAGISel : public SelectionDAGISel {
// in Base, Disp and Index respectively.
bool selectBDVAddr12Only(SDValue Addr, SDValue Elem, SDValue &Base,
SDValue &Disp, SDValue &Index) const;
+ // Wrapper functions for LSB access on big-endian multi-byte types.
+ // Selects a Base + Displacement address and applies a fixed byte Offset.
+ // Offsets: i16 LSB = +1, i32 LSB = +3, i64 LSB = +7.
+ bool selectBDAddr12off1(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+ bool selectBDAddr12off3(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+ bool selectBDAddr12off7(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+ bool selectBDAddr20off1(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+ bool selectBDAddr20off3(SDValue Addr, SDValue &Base, SDValue &Disp) const;
+ bool selectBDAddr20off7(SDValue Addr, SDValue &Base, SDValue &Disp) const;
// Check whether (or Op (and X InsertMask)) is effectively an insertion
// of X into bits InsertMask of some Y != Op. Return true if so and
@@ -693,12 +702,14 @@ void SystemZDAGToDAGISel::getAddressOperands(const SystemZAddressingMode &AM,
bool SystemZDAGToDAGISel::selectBDAddr(SystemZAddressingMode::DispRange DR,
SDValue Addr, SDValue &Base,
- SDValue &Disp) const {
+ SDValue &Disp, int64_t Offset) const {
SystemZAddressingMode AM(SystemZAddressingMode::FormBD, DR);
if (!selectAddress(Addr, AM))
return false;
+ AM.Disp += Offset;
getAddressOperands(AM, Addr.getValueType(), Base, Disp);
+ Disp = CurDAG->getTargetConstant(AM.Disp, SDLoc(Addr), MVT::i64);
return true;
}
@@ -749,6 +760,32 @@ bool SystemZDAGToDAGISel::selectBDVAddr12Only(SDValue Addr, SDValue Elem,
return false;
}
+bool SystemZDAGToDAGISel::selectBDAddr12off1(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 1);
+}
+bool SystemZDAGToDAGISel::selectBDAddr12off3(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 3);
+}
+bool SystemZDAGToDAGISel::selectBDAddr12off7(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp12Pair, A, B, D, 7);
+}
+
+bool SystemZDAGToDAGISel::selectBDAddr20off1(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 1);
+}
+bool SystemZDAGToDAGISel::selectBDAddr20off3(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 3);
+}
+bool SystemZDAGToDAGISel::selectBDAddr20off7(SDValue A, SDValue &B,
+ SDValue &D) const {
+ return selectBDAddr(SystemZAddressingMode::Disp20Pair, A, B, D, 7);
+}
+
bool SystemZDAGToDAGISel::detectOrAndInsertion(SDValue &Op,
uint64_t InsertMask) const {
// We're only interested in cases where the insertion is into some operand
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 41b1becda829b..a7f008c785979 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1329,10 +1329,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm NC : MemorySS<"nc", 0xD4, z_nc>;
}
-defm : RMWIByte<and, bdaddr12pair, NI>;
-defm : RMWIByte<and, bdaddr20pair, NIY>;
-defm : RMWIByteLSB<and, bdaddr12pair, NI>;
-defm : RMWIByteLSB<and, bdaddr20pair, NIY>;
+defm : RMWIByte<and, NI, NIY>;
//===----------------------------------------------------------------------===//
// OR
@@ -1388,10 +1385,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm OC : MemorySS<"oc", 0xD6, z_oc>;
}
-defm : RMWIByte<or, bdaddr12pair, OI>;
-defm : RMWIByte<or, bdaddr20pair, OIY>;
-defm : RMWIByteLSB<or, bdaddr12pair, OI>;
-defm : RMWIByteLSB<or, bdaddr20pair, OIY>;
+defm : RMWIByte<or, OI, OIY>;
//===----------------------------------------------------------------------===//
// XOR
@@ -1430,10 +1424,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm XC : MemorySS<"xc", 0xD7, z_xc>;
}
-defm : RMWIByte<xor, bdaddr12pair, XI>;
-defm : RMWIByte<xor, bdaddr20pair, XIY>;
-defm : RMWIByteLSB<xor, bdaddr12pair, XI>;
-defm : RMWIByteLSB<xor, bdaddr20pair, XIY>;
+defm : RMWIByte<xor, XI, XIY>;
//===----------------------------------------------------------------------===//
// Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index ceb5038d50b5b..921b6ed5020fd 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -695,45 +695,31 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
let FastIselShouldIgnore = true;
}
-// Extracts the base register from the load node.
-def get_base_from_load : SDNodeXForm<ld, [{
- SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
- // If the address is (Base + Disp), return Base.
- // Otherwise, return the whole Addr.
- if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
- return Addr.getOperand(0);
- return Addr;
-}]>;
-
-// i16: Target the displacement of the load and add +1 for Big-Endian LSB.
-def get_disp_lsb1 : SDNodeXForm<ld, [{
- SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
- int64_t Disp = 0;
- if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
- Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
- return CurDAG->getTargetConstant(Disp + 1, SDLoc(N), MVT::i64);
-}]>;
-
-// i32: Target the displacement of the load and add +3 for Big-Endian LSB.
-def get_disp_lsb3 : SDNodeXForm<ld, [{
- SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
- int64_t Disp = 0;
- if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
- Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
- return CurDAG->getTargetConstant(Disp + 3, SDLoc(N), MVT::i64);
-}]>;
-
-// i64: Target the displacement of the load and add +7 for Big-Endian LSB.
-def get_disp_lsb7 : SDNodeXForm<ld, [{
- SDValue Addr = cast<MemSDNode>(N)->getBasePtr();
- int64_t Disp = 0;
- if (Addr.getOpcode() == ISD::ADD && isa<ConstantSDNode>(Addr.getOperand(1)))
- Disp = cast<ConstantSDNode>(Addr.getOperand(1))->getSExtValue();
- return CurDAG->getTargetConstant(Disp + 7, SDLoc(N), MVT::i64);
-}]>;
+// These patterns target the Least Significant Byte (LSB) of multi-byte
+// integers (i16, i32, i64). On SystemZ (Big-Endian), the LSB is located at
+// a fixed positive offset from the base address:
+// i16: offset +1 (byte 1 of 2)
+// i32: offset +3 (byte 3 of 4)
+// i64: offset +7 (byte 7 of 8)
+
+// 12-bit unsigned displacement variants (for NI, OI, XI)
+def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
+def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
+def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
+
+// 20-bit signed displacement variants (for NIY, OIY, XIY)
+def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
+def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
+def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
// Normalizes the immediate to i32.
def as_i32imm : SDNodeXForm<imm, [{
uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
}]>;
+
+// Extracts only the bottom 8 bits.
+def as_i8imm : SDNodeXForm<imm, [{
+ uint64_t Val = N->getZExtValue() & 0xFF;
+ return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
+}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index ad1dbff133f5d..a9e27851f0fdf 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1370,43 +1370,33 @@ def z_vsei8_by_parts : z_vse_by_parts<sext8dbl, 7, 15>;
def z_vsei16_by_parts : z_vse_by_parts<sext16dbl, 3, 7>;
def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
-// Matches any integer constant where only the last byte is set.
+// Matches any integer constant where only the last byte is set (0x00...XX).
+// Safe for OR/XOR because x | 0 == x.
def immLSB8 : PatLeaf<(imm), [{
return N->getAPIntValue().getActiveBits() <= 8;
}]>;
-// Fragment for i16 any-extending loads with a 20-bit signed displacement check.
-def anyextloadi16_20bit : PatFrag<(ops node:$addr), (anyextloadi16 $addr), [{
- auto *Load = cast<MemSDNode>(N);
- SDValue BaseAddr = Load->getBasePtr();
- if (BaseAddr.getOpcode() == ISD::ADD &&
- isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
- int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
- return Disp >= -524288 && Disp <= 524287;
+// Matches constants where all bits except the last byte are 1 (0xFF...XX).
+def immAndLSB8 : PatLeaf<(imm), [{
+ uint64_t Val = N->getZExtValue();
+ uint64_t Mask = 0;
+ uint64_t Size = N->getValueType(0).getSizeInBits();
+
+ switch (Size) {
+ case 16:
+ Mask = 0xff00ULL;
+ break;
+ case 32:
+ // Support both full i32 and zero-extended i16 constant.
+ Mask = ((Val & 0xffffff00ULL) == 0xffffff00ULL) ? 0xffffff00ULL
+ : 0x0000ff00ULL;
+ break;
+ case 64:
+ Mask = 0xffffffffffffff00ULL;
+ break;
+ default:
+ return false;
}
- return true;
-}]>;
-
-// Fragment for i32/i64 loads with a 20-bit signed displacement check.
-def load_20bit : PatFrag<(ops node:$addr), (load $addr), [{
- auto *Load = cast<MemSDNode>(N);
- SDValue BaseAddr = Load->getBasePtr();
- if (BaseAddr.getOpcode() == ISD::ADD &&
- isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
- int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
- return Disp >= -524288 && Disp <= 524287;
- }
- return true;
-}]>;
-// Check if displacement is safe for RMW operations on wider types.
-def load_RMW_safe : PatFrag<(ops node:$addr), (load $addr), [{
- auto *Load = cast<MemSDNode>(N);
- SDValue BaseAddr = Load->getBasePtr();
- if (BaseAddr.getOpcode() == ISD::ADD &&
- isa<ConstantSDNode>(BaseAddr.getOperand(1))) {
- int64_t Disp = cast<ConstantSDNode>(BaseAddr.getOperand(1))->getSExtValue();
- return Disp >= -524288 && Disp <= 524287;
- }
- return true;
+ return (Val & Mask) == Mask;
}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 7cf4a932dc5b7..bc733f8260bef 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -45,64 +45,61 @@ class RMWI<SDPatternOperator load, SDPatternOperator operator,
: Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
(insn mode:$addr, (UIMM8 imm:$src))>;
-// Record that INSN performs binary operation OPERATION on a byte
-// memory location. IMM is the type of the second operand.
-multiclass RMWIByte<SDPatternOperator operator, AddressingMode mode,
- Instruction insn> {
- def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm32, insn>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm64, insn>;
-}
-
// Handle RMW operations on the Least Significant Byte (LSB) of wider types.
-// This allows targeting byte-level instructions (NI, NIY, etc.) for i16, i32,
+// This allows targeting byte-level logical instructions for i16, i32,
// and i64 memory locations when only the last byte is modified.
-multiclass RMWIByteLSB<SDPatternOperator operator, AddressingMode mode,
- Instruction insn> {
-
- // Base + Displacement.
- // Uses XForms to adjust the displacement to target the LSB.
- let AddedComplexity = 20 in {
- def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16_20bit:$src
- mode:$addr)),
- (i32 immLSB8:$imm))),
- mode:$addr),
- (insn (get_base_from_load $src),
- (get_disp_lsb1 $src),
- (as_i32imm $imm))>;
-
- def : Pat<(store (i32 (operator (i32 (load_20bit:$src mode:$addr)),
- (i32 immLSB8:$imm))),
- mode:$addr),
- (insn (get_base_from_load $src),
- (get_disp_lsb3 $src),
- (as_i32imm $imm))>;
-
- def : Pat<(store (i64 (operator (i64 (load_20bit:$src mode:$addr)),
- (i64 immLSB8:$imm))),
- mode:$addr),
- (insn (get_base_from_load $src),
- (get_disp_lsb7 $src),
- (as_i32imm $imm))>;
- }
-
- // Register-only fallback (for AGFI or legalized addresses).
- // Targets out-of-range addresses legalized into registers.
- let AddedComplexity = 15 in {
- def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 ADDR64:$base)),
- (i32 immLSB8:$imm))),
- ADDR64:$base),
- (insn ADDR64:$base, 1, (as_i32imm $imm))>;
-
- def : Pat<(store (i32 (operator (i32 (load ADDR64:$base)),
- (i32 immLSB8:$imm))),
- ADDR64:$base),
- (insn ADDR64:$base, 3, (as_i32imm $imm))>;
-
- def : Pat<(store (i64 (operator (i64 (load ADDR64:$base)),
- (i64 immLSB8:$imm))),
- ADDR64:$base),
- (insn ADDR64:$base, 7, (as_i32imm $imm))>;
- }
+multiclass RMWIByteLSB<SDPatternOperator operator,
+ ComplexPattern mode12_1, ComplexPattern mode20_1,
+ ComplexPattern mode12_3, ComplexPattern mode20_3,
+ ComplexPattern mode12_7, ComplexPattern mode20_7,
+ Instruction insn12, Instruction insn20,
+ PatLeaf mask> {
+ // i16 LSB (+1 offset)
+ def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode12_1:$addr)),
+ (i32 mask:$imm))), mode12_1:$addr),
+ (insn12 mode12_1:$addr, (as_i8imm $imm))>;
+ def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode20_1:$addr)),
+ (i32 mask:$imm))), mode20_1:$addr),
+ (insn20 mode20_1:$addr, (as_i8imm $imm))>;
+
+
+ // i32 LSB (+3 offset)
+ def : Pat<(store (i32 (operator (i32 (load mode12_3:$addr)),
+ (i32 mask:$imm))), mode12_3:$addr),
+ (insn12 mode12_3:$addr, (as_i8imm $imm))>;
+ def : Pat<(store (i32 (operator (i32 (load mode20_3:$addr)),
+ (i32 mask:$imm))), mode20_3:$addr),
+ (insn20 mode20_3:$addr, (as_i8imm $imm))>;
+
+ // i64 LSB (+7 offset)
+ def : Pat<(store (i64 (operator (i64 (load mode12_7:$addr)),
+ (i64 mask:$imm))), mode12_7:$addr),
+ (insn12 mode12_7:$addr, (as_i8imm $imm))>;
+ def : Pat<(store (i64 (operator (i64 (load mode20_7:$addr)),
+ (i64 mask:$imm))), mode20_7:$addr),
+ (insn20 mode20_7:$addr, (as_i8imm $imm))>;
+}
+
+// Record that INSN performs binary operation OPERATION on a byte
+// memory location. IMM is the type of the second operand.
+multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
+ Instruction insn20> {
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm32,
+ insn12>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm64,
+ insn12>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm32,
+ insn20>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
+ insn20>;
+
+ // Conditional folding for wider types.
+ defm : RMWIByteLSB<operator,
+ bdaddr12off1, bdaddr20off1,
+ bdaddr12off3, bdaddr20off3,
+ bdaddr12off7, bdaddr20off7,
+ insn12, insn20,
+ !if(!eq(operator, and), immAndLSB8, immLSB8)>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 22b73ff41ff39..f181c6f9a9c4f 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -16,10 +16,7 @@ define void @f1(ptr %ptr) {
; Check lowest useful constant for i16, expressed as a signed integer.
define void @f1_i16(ptr %ptr) {
; CHECK-LABEL: f1_i16:
-; CHECK-NOT: ni 1(%r2)
-; CHECK: lh %r0, 0(%r2)
-; CHECK: nill %r0, 65281
-; CHECK: sth %r0, 0(%r2)
+; CHECK: ni 1(%r2), 1
; CHECK: br %r14
%val = load i16, ptr %ptr
%and = and i16 %val, -255
@@ -30,10 +27,7 @@ define void @f1_i16(ptr %ptr) {
; Check lowest useful constant for i32, expressed as a signed integer.
define void @f1_i32(ptr %ptr) {
; CHECK-LABEL: f1_i32:
-; CHECK-NOT: ni 3(%r2)
-; CHECK: lhi %r0, -255
-; CHECK: n %r0, 0(%r2)
-; CHECK: st %r0, 0(%r2)
+; CHECK: ni 3(%r2), 1
; CHECK: br %r14
%val = load i32, ptr %ptr
%and = and i32 %val, -255
@@ -44,10 +38,7 @@ define void @f1_i32(ptr %ptr) {
; Check lowest useful constant for i64, expressed as a signed integer.
define void @f1_i64(ptr %ptr) {
; CHECK-LABEL: f1_i64:
-; CHECK-NOT: ni 7(%r2)
-; CHECK: lghi %r0, -255
-; CHECK: ng %r0, 0(%r2)
-; CHECK: stg %r0, 0(%r2)
+; CHECK: ni 7(%r2), 1
; CHECK: br %r14
%val = load i64, ptr %ptr
%and = and i64 %val, -255
@@ -69,10 +60,8 @@ define void @f2(ptr %ptr) {
; Check the highest useful constant for i16, expressed as a signed integer.
define void @f2_i16(ptr %ptr) {
; CHECK-LABEL: f2_i16:
-; CHECK-NOT: ni {{[0-9]+}}(%r2), 254
-; CHECK: lh %r0, 0(%r2)
-; CHECK: nilf %r0, 65534
-; CHECK: sth %r0, 0(%r2)
+; CHECK: ni 1(%r2), 254
+; CHECK: br %r14
%val = load i16, ptr %ptr
%and = and i16 %val, -2
store i16 %and, ptr %ptr
@@ -82,10 +71,7 @@ define void @f2_i16(ptr %ptr) {
; Check the highest useful constant for i32, expressed as a signed integer.
define void @f2_i32(ptr %ptr) {
; CHECK-LABEL: f2_i32:
-; CHECK-NOT: ni 3(%r2)
-; CHECK: lhi %r0, -2
-; CHECK: n %r0, 0(%r2)
-; CHECK: st %r0, 0(%r2)
+; CHECK: ni 3(%r2), 254
; CHECK: br %r14
%val = load i32, ptr %ptr
%and = and i32 %val, -2
@@ -93,13 +79,22 @@ define void @f2_i32(ptr %ptr) {
ret void
}
+; Check i32 with zero-extended i16 pattern (0x0000FFxx).
+define void @f2_i32_zext(ptr %ptr) {
+; CHECK-LABEL: f2_i32_zext:
+; CHECK: ni 3(%r2), 254
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 65534 ; 0x0000FFFE
+ store i32 %and, ptr %ptr
+ ret void
+}
+
; Check the highest useful constant for i64, expressed as a signed integer.
define void @f2_i64(ptr %ptr) {
; CHECK-LABEL: f2_i64:
-; CHECK-NOT: ni 7(%r2)
-; CHECK: lghi %r0, -2
-; CHECK: ng %r0, 0(%r2)
-; CHECK: stg %r0, 0(%r2)
+; CHECK: ni 7(%r2), 254
+; CHECK: br %r14
%val = load i64, ptr %ptr
%and = and i64 %val, -2
store i64 %and, ptr %ptr
@@ -118,9 +113,13 @@ define void @f3(ptr %ptr) {
}
; Check the lowest useful constant for i16, expressed as an unsigned integer.
+; Constant 1 should not fold for i16, as it requires clearing the high byte.
define void @f3_i16(ptr %ptr) {
; CHECK-LABEL: f3_i16:
-; CHECK: ni 1(%r2), 1
+; CHECK-NOT: ni 1(%r2), 1
+; CHECK: llh %r0, 0(%r2)
+; CHECK: nilf %r0, 1
+; CHECK: sth %r0, 0(%r2)
; CHECK: br %r14
%val = load i16, ptr %ptr
%and = and i16 %val, 1
@@ -129,9 +128,13 @@ define void @f3_i16(ptr %ptr) {
}
; Check the lowest useful constant for i32, expressed as an unsigned integer.
+; Constant 1 should not fold for i32, as it requires clearing the high bytes.
define void @f3_i32(ptr %ptr) {
; CHECK-LABEL: f3_i32:
-; CHECK: ni 3(%r2), 1
+; CHECK-NOT: ni 3(%r2), 1
+; CHECK: lhi %r0, 1
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
; CHECK: br %r14
%val = load i32, ptr %ptr
%and = and i32 %val, 1
@@ -140,9 +143,13 @@ define void @f3_i32(ptr %ptr) {
}
; Check the lowest useful constant for i64, expressed as an unsigned integer.
+; Constant 1 should not fold for i64, as it requires clearing the high bytes.
define void @f3_i64(ptr %ptr) {
; CHECK-LABEL: f3_i64:
-; CHECK: ni 7(%r2), 1
+; CHECK-NOT: ni 7(%r2), 1
+; CHECK: lg %r0, 0(%r2)
+; CHECK: risbg %r0, %r0, 63, 191, 0
+; CHECK: stg %r0, 0(%r2)
; CHECK: br %r14
%val = load i64, ptr %ptr
%and = and i64 %val, 1
@@ -162,9 +169,14 @@ define void @f4(ptr %ptr) {
}
; Check the highest useful constant for i16, expressed as a unsigned integer.
+; Constant 254 (0x00FE) should not fold for i16 because the high byte must be
+; cleared.
define void @f4_i16(ptr %ptr) {
; CHECK-LABEL: f4_i16:
-; CHECK: ni 1(%r2), 254
+; CHECK-NOT: ni 1(%r2), 254
+; CHECK: llh %r0, 0(%r2)
+; CHECK: nilf %r0, 254
+; CHECK: sth %r0, 0(%r2)
; CHECK: br %r14
%val = load i16, ptr %ptr
%and = and i16 %val, 254
@@ -173,9 +185,13 @@ define void @f4_i16(ptr %ptr) {
}
; Check the highest useful constant for i32, expressed as a unsigned integer.
+; Constant 254 should not fold for i32 because the high byte must be cleared.
define void @f4_i32(ptr %ptr) {
; CHECK-LABEL: f4_i32:
-; CHECK: ni 3(%r2), 254
+; CHECK-NOT: ni 3(%r2), 254
+; CHECK: lhi %r0, 254
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
; CHECK: br %r14
%val = load i32, ptr %ptr
%and = and i32 %val, 254
@@ -184,9 +200,13 @@ define void @f4_i32(ptr %ptr) {
}
; Check the highest useful constant for i64, expressed as a unsigned integer.
+; Constant 254 should not fold for i64 because the high byte must be cleared.
define void @f4_i64(ptr %ptr) {
; CHECK-LABEL: f4_i64:
-; CHECK: ni 7(%r2), 254
+; CHECK-NOT: ni 7(%r2), 254
+; CHECK: lg %r0, 0(%r2)
+; CHECK: risbg %r0, %r0, 56, 190, 0
+; CHECK: stg %r0, 0(%r2)
; CHECK: br %r14
%val = load i64, ptr %ptr
%and = and i64 %val, 254
@@ -213,7 +233,7 @@ define void @f5_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4095
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -225,7 +245,7 @@ define void @f5_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4095
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -237,7 +257,7 @@ define void @f5_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4095
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -261,7 +281,7 @@ define void @f6_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4096
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -273,7 +293,7 @@ define void @f6_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4096
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -285,7 +305,7 @@ define void @f6_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 4096
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -309,7 +329,7 @@ define void @f7_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524287
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -321,7 +341,7 @@ define void @f7_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524287
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -333,7 +353,7 @@ define void @f7_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524287
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -361,7 +381,7 @@ define void @f8_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524288
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -375,7 +395,7 @@ define void @f8_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524288
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -389,7 +409,7 @@ define void @f8_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 524288
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -413,7 +433,7 @@ define void @f9_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -1
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -425,7 +445,7 @@ define void @f9_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -1
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -437,7 +457,7 @@ define void @f9_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -1
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -461,7 +481,7 @@ define void @f10_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524288
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -473,7 +493,7 @@ define void @f10_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524288
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -485,7 +505,7 @@ define void @f10_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524288
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -513,7 +533,7 @@ define void @f11_i16(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -527,7 +547,7 @@ define void @f11_i32(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -541,7 +561,7 @@ define void @f11_i64(ptr %src) {
; CHECK: br %r14
%ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -571,7 +591,7 @@ define void @f12_i16(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -586,7 +606,7 @@ define void @f12_i32(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -601,7 +621,7 @@ define void @f12_i64(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
store i64 %and, ptr %ptr
ret void
}
@@ -631,7 +651,7 @@ define void @f13_i16(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4096
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
- %and = and i16 %val, 127
+ %and = and i16 %val, 65407
store i16 %and, ptr %ptr
ret void
}
@@ -646,7 +666,7 @@ define void @f13_i32(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4096
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
- %and = and i32 %val, 127
+ %and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
ret void
}
@@ -661,7 +681,126 @@ define void @f13_i64(i64 %src, i64 %index) {
%add2 = add i64 %add1, 4096
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
- %and = and i64 %val, 127
+ %and = and i64 %val, -129
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check folding of multi-byte 'and' operations into byte-immediate memory
+; operation 'ni'.
+; Additional tests for immAndLSB8 PatLeaf logic - preservation of upper bytes
+; for i16, i32, and i64. Low/high constant (signed/unsigned) tests have already
+; been covered (f1 to f4).
+
+; Check i16 - should not fold. High byte has bit cleared.
+define void @f_const_i16_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i16_no_fold:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK: nilf [[REG]], 65278
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 65278 ; 0xFEFE
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 - should not fold. Upper bytes have bit cleared.
+define void @f_const_i32_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi [[REG:%r[0-5]]], -258
+; CHECK: n [[REG]], 0(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 4294967038 ; 0xFFFFFEFE
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 - should not fold. Upper bytes have bit cleared.
+define void @f_const_i64_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi [[REG:%r[0-5]]], -258
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, -258 ; 0xFFFFFFFFFFFFFFFE
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i16 - should not fold. Constant 0xFE00 affects more than just the LSB.
+define void @f_const_i16_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i16_multi_byte_no_fold:
+; CHECK-NOT: ni 1(%r2)
+; CHECK: llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK: nilf [[REG]], 65024
+; CHECK: sth [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i16, ptr %ptr
+ %and = and i16 %val, 65024 ; 0xFE00
+ store i16 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 - should not fold. Constant 0xFFFFFE00 affects more than just the
+; LSB.
+define void @f_const_i32_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_multi_byte_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: lhi %r0, -512
+; CHECK: n %r0, 0(%r2)
+; CHECK: st %r0, 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 4294966784 ; 0xFFFFFE00
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i32 - should not fold. Constant affects a non-LSB byte.
+define void @f_const_i32_wrong_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i32_wrong_byte_no_fold:
+; CHECK-NOT: ni 3(%r2)
+; CHECK: iilf [[REG:%r[0-5]]], 4294902015
+; CHECK: n [[REG]], 0(%r2)
+; CHECK: st [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i32, ptr %ptr
+ %and = and i32 %val, 4294902015 ; 0xFFFF00FF
+ store i32 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 - should not fold. Constant affects more than just the LSB.
+define void @f_const_i64_multi_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_multi_byte_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lghi [[REG:%r[0-5]]], -512
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, -512 ; 0xFFFFFFFFFFFFFE00
+ store i64 %and, ptr %ptr
+ ret void
+}
+
+; Check i64 - should not fold. Constant affects a non-LSB byte.
+define void @f_const_i64_wrong_byte_no_fold(ptr %ptr) {
+; CHECK-LABEL: f_const_i64_wrong_byte_no_fold:
+; CHECK-NOT: ni 7(%r2)
+; CHECK: lgfi [[REG:%r[0-5]]], -65281
+; CHECK: ng [[REG]], 0(%r2)
+; CHECK: stg [[REG]], 0(%r2)
+; CHECK: br %r14
+ %val = load i64, ptr %ptr
+ %and = and i64 %val, -65281 ; 0xFFFFFFFFFFFF00FF
store i64 %and, ptr %ptr
ret void
}
>From f73a306a41cfa6a7a0ff1dccacf3aa269ac61cb0 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Tue, 2 Jun 2026 16:25:06 +0200
Subject: [PATCH 06/11] [SystemZ] Address code review feedback for folding
i16/i32/i64 logical RMW operations into memory operands - Updated
isFoldableRMW() in SystemZTTI cost model to check constant immediate
masks for logical ops on wider types, as required by instruction hw
capability. - Implemented imm32ll8c and imm64ll8c in SystemZOperators.td.
- Added offset addition before selectAddress to avoid overflow, and
changes made to displacement tests, it might limit optimization close to
boundary edge displacement. - Eliminated RMWIByteLSB.
---
.../Target/SystemZ/SystemZISelDAGToDAG.cpp | 3 +-
llvm/lib/Target/SystemZ/SystemZOperands.td | 61 +++----
llvm/lib/Target/SystemZ/SystemZOperators.td | 25 ---
llvm/lib/Target/SystemZ/SystemZPatterns.td | 77 ++++-----
.../SystemZ/SystemZTargetTransformInfo.cpp | 27 ++-
.../CostModel/SystemZ/fold-rmw-cost.ll | 160 ++++++++++++++++--
llvm/test/CodeGen/SystemZ/and-05.ll | 96 +++++------
llvm/test/CodeGen/SystemZ/or-05.ll | 96 +++++------
llvm/test/CodeGen/SystemZ/xor-05.ll | 96 +++++------
9 files changed, 384 insertions(+), 257 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index 992ad39690fc8..cb09611db7d16 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
@@ -704,12 +704,11 @@ bool SystemZDAGToDAGISel::selectBDAddr(SystemZAddressingMode::DispRange DR,
SDValue Addr, SDValue &Base,
SDValue &Disp, int64_t Offset) const {
SystemZAddressingMode AM(SystemZAddressingMode::FormBD, DR);
+ AM.Disp += Offset;
if (!selectAddress(Addr, AM))
return false;
- AM.Disp += Offset;
getAddressOperands(AM, Addr.getValueType(), Base, Disp);
- Disp = CurDAG->getTargetConstant(AM.Disp, SDLoc(Addr), MVT::i64);
return true;
}
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 921b6ed5020fd..c1f62c036b283 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -444,6 +444,28 @@ defm imm64hh16c : Immediate<i64, [{
SystemZ::isImmHH(uint64_t(~Imm.getZExtValue()));
}], HH16, "U16Imm">;
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one
+// or zero-extended from a 16-bit.
+defm imm32ll8c : Immediate<i32, [{
+ if (!Imm.isIntN(32)) return false;
+ uint64_t Val = Imm.getZExtValue();
+ // Case 1: Upper 24 bits are all ones.
+ if ((Val & 0xffffff00ULL) == 0xffffff00ULL)
+ return true;
+ // Case 2: Zero-extended i16 where the upper 16 bits are zero and the
+ // second least significant byte is 0xff (0x0000ff00).
+ if ((Val & 0xffffff00ULL) == 0x0000ff00ULL) {
+ return true;
+ }
+ return false;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
+defm imm64ll8c : Immediate<i64, [{
+ return Imm.isIntN(64) &&
+ (uint64_t(~Imm.getZExtValue()) & 0xffffffffffffff00ULL) == 0;
+}], UIMM8, "U8Imm">;
+
// Immediates for the lower and upper 32 bits of an i64, with the other
// bits of the i32 being zero.
defm imm64lf32 : Immediate<i64, [{
@@ -681,6 +703,16 @@ def bdladdr12onlylen8 : BDLMode<"BDLAddr", "64", "12", "Only", "8">;
def bdraddr12only : BDRMode<"BDRAddr", "64", "12", "Only">;
def bdvaddr12only : BDVMode< "64", "12">;
+// 12-bit unsigned displacement variants (for NI, OI, XI)
+def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
+def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
+def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
+
+// 20-bit signed displacement variants (for NIY, OIY, XIY)
+def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
+def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
+def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
+
//===----------------------------------------------------------------------===//
// Miscellaneous
//===----------------------------------------------------------------------===//
@@ -694,32 +726,3 @@ def cond4 : ImmLeaf<i32, [{ return (Imm.getZExtValue() < 16); }],
let IsAPInt = true;
let FastIselShouldIgnore = true;
}
-
-// These patterns target the Least Significant Byte (LSB) of multi-byte
-// integers (i16, i32, i64). On SystemZ (Big-Endian), the LSB is located at
-// a fixed positive offset from the base address:
-// i16: offset +1 (byte 1 of 2)
-// i32: offset +3 (byte 3 of 4)
-// i64: offset +7 (byte 7 of 8)
-
-// 12-bit unsigned displacement variants (for NI, OI, XI)
-def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
-def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
-def bdaddr12off7 : ComplexPattern<i64, 2, "selectBDAddr12off7">;
-
-// 20-bit signed displacement variants (for NIY, OIY, XIY)
-def bdaddr20off1 : ComplexPattern<i64, 2, "selectBDAddr20off1">;
-def bdaddr20off3 : ComplexPattern<i64, 2, "selectBDAddr20off3">;
-def bdaddr20off7 : ComplexPattern<i64, 2, "selectBDAddr20off7">;
-
-// Normalizes the immediate to i32.
-def as_i32imm : SDNodeXForm<imm, [{
- uint64_t Val = N->getAPIntValue().zextOrTrunc(32).getZExtValue();
- return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
-}]>;
-
-// Extracts only the bottom 8 bits.
-def as_i8imm : SDNodeXForm<imm, [{
- uint64_t Val = N->getZExtValue() & 0xFF;
- return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i32);
-}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index a9e27851f0fdf..a6b97b82301ea 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1375,28 +1375,3 @@ def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
def immLSB8 : PatLeaf<(imm), [{
return N->getAPIntValue().getActiveBits() <= 8;
}]>;
-
-// Matches constants where all bits except the last byte are 1 (0xFF...XX).
-def immAndLSB8 : PatLeaf<(imm), [{
- uint64_t Val = N->getZExtValue();
- uint64_t Mask = 0;
- uint64_t Size = N->getValueType(0).getSizeInBits();
-
- switch (Size) {
- case 16:
- Mask = 0xff00ULL;
- break;
- case 32:
- // Support both full i32 and zero-extended i16 constant.
- Mask = ((Val & 0xffffff00ULL) == 0xffffff00ULL) ? 0xffffff00ULL
- : 0x0000ff00ULL;
- break;
- case 64:
- Mask = 0xffffffffffffff00ULL;
- break;
- default:
- return false;
- }
-
- return (Val & Mask) == Mask;
-}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index bc733f8260bef..9d92e88113c07 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -41,45 +41,10 @@ multiclass ZXB<SDPatternOperator operator, RegisterOperand cls,
// of the second operand.
class RMWI<SDPatternOperator load, SDPatternOperator operator,
SDPatternOperator store, AddressingMode mode,
- PatFrag imm, Instruction insn>
+ ImmLeaf imm, Instruction insn>
: Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
(insn mode:$addr, (UIMM8 imm:$src))>;
-// Handle RMW operations on the Least Significant Byte (LSB) of wider types.
-// This allows targeting byte-level logical instructions for i16, i32,
-// and i64 memory locations when only the last byte is modified.
-multiclass RMWIByteLSB<SDPatternOperator operator,
- ComplexPattern mode12_1, ComplexPattern mode20_1,
- ComplexPattern mode12_3, ComplexPattern mode20_3,
- ComplexPattern mode12_7, ComplexPattern mode20_7,
- Instruction insn12, Instruction insn20,
- PatLeaf mask> {
- // i16 LSB (+1 offset)
- def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode12_1:$addr)),
- (i32 mask:$imm))), mode12_1:$addr),
- (insn12 mode12_1:$addr, (as_i8imm $imm))>;
- def : Pat<(truncstorei16 (i32 (operator (i32 (anyextloadi16 mode20_1:$addr)),
- (i32 mask:$imm))), mode20_1:$addr),
- (insn20 mode20_1:$addr, (as_i8imm $imm))>;
-
-
- // i32 LSB (+3 offset)
- def : Pat<(store (i32 (operator (i32 (load mode12_3:$addr)),
- (i32 mask:$imm))), mode12_3:$addr),
- (insn12 mode12_3:$addr, (as_i8imm $imm))>;
- def : Pat<(store (i32 (operator (i32 (load mode20_3:$addr)),
- (i32 mask:$imm))), mode20_3:$addr),
- (insn20 mode20_3:$addr, (as_i8imm $imm))>;
-
- // i64 LSB (+7 offset)
- def : Pat<(store (i64 (operator (i64 (load mode12_7:$addr)),
- (i64 mask:$imm))), mode12_7:$addr),
- (insn12 mode12_7:$addr, (as_i8imm $imm))>;
- def : Pat<(store (i64 (operator (i64 (load mode20_7:$addr)),
- (i64 mask:$imm))), mode20_7:$addr),
- (insn20 mode20_7:$addr, (as_i8imm $imm))>;
-}
-
// Record that INSN performs binary operation OPERATION on a byte
// memory location. IMM is the type of the second operand.
multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
@@ -93,13 +58,39 @@ multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
insn20>;
- // Conditional folding for wider types.
- defm : RMWIByteLSB<operator,
- bdaddr12off1, bdaddr20off1,
- bdaddr12off3, bdaddr20off3,
- bdaddr12off7, bdaddr20off7,
- insn12, insn20,
- !if(!eq(operator, and), immAndLSB8, immLSB8)>;
+ // 12-bit instruction variants.
+ def : Pat<(truncstorei16 (i32 (operator
+ (i32 (anyextloadi16 bdaddr12off1:$addr)),
+ (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+ bdaddr12off1:$addr),
+ (insn12 bdaddr12off1:$addr, (UIMM8 $imm))>;
+
+ def : Pat<(store (i32 (operator (i32 (load bdaddr12off3:$addr)),
+ (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+ bdaddr12off3:$addr),
+ (insn12 bdaddr12off3:$addr, (UIMM8 $imm))>;
+
+ def : Pat<(store (i64 (operator (i64 (load bdaddr12off7:$addr)),
+ (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
+ bdaddr12off7:$addr),
+ (insn12 bdaddr12off7:$addr, (UIMM8 $imm))>;
+
+ // 20-bit instruction variants.
+ def : Pat<(truncstorei16 (i32 (operator
+ (i32 (anyextloadi16 bdaddr20off1:$addr)),
+ (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+ bdaddr20off1:$addr),
+ (insn20 bdaddr20off1:$addr, (UIMM8 $imm))>;
+
+ def : Pat<(store (i32 (operator (i32 (load bdaddr20off3:$addr)),
+ (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
+ bdaddr20off3:$addr),
+ (insn20 bdaddr20off3:$addr, (UIMM8 $imm))>;
+
+ def : Pat<(store (i64 (operator (i64 (load bdaddr20off7:$addr)),
+ (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
+ bdaddr20off7:$addr),
+ (insn20 bdaddr20off7:$addr, (UIMM8 $imm))>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index e7081b335df07..1f0a9fc89a0be 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -556,10 +556,33 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
switch (Opcode) {
case Instruction::And:
case Instruction::Or:
- case Instruction::Xor:
- if (BitWidth != 8 && BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
+ case Instruction::Xor: {
+ if (BitWidth == 8)
+ break;
+ if (BitWidth != 16 && BitWidth != 32 && BitWidth != 64)
+ return false;
+
+ auto *CI = dyn_cast<ConstantInt>(I->getOperand(1));
+ if (!CI)
return false;
+
+ uint64_t Val = CI->getZExtValue();
+ if (Opcode == Instruction::And) {
+ if (BitWidth == 16 && (Val & 0xff00ULL) != 0xff00ULL)
+ return false;
+ if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL &&
+ (Val & 0xffffff00ULL) != 0x0000ff00ULL)
+ return false;
+ if (BitWidth == 64 &&
+ (Val & 0xffffffffffffff00ULL) != 0xffffffffffffff00ULL)
+ return false;
+ } else {
+ if (CI->getValue().getActiveBits() > 8) {
+ return false;
+ }
+ }
break;
+ }
case Instruction::Add:
case Instruction::Sub:
if (BitWidth != 32 && BitWidth != 64)
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index f82ac31797274..c05fb3cd12a17 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -145,32 +145,168 @@ define void @test_vector_no_fold(<4 x i32> %val, ptr %p) {
ret void
}
-define void @test_xor_i16(ptr %p) {
-; CHECK-LABEL: 'test_xor_i16'
-; CHECK: cost of 0 {{.*}} xor i16
+; Logical operations for i16/i32/i64 types.
+
+; Modifies only LSB bits.
+define void @test_and_i16_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i16_valid'
+; CHECK: cost of 0 {{.*}} and i16 %v, -255
; CHECK: cost of 0 {{.*}} store i16
%v = load i16, ptr %p
- %res = xor i16 %v, 1
+ %res = and i16 %v, 65281 ; 0xff01
+ store i16 %res, ptr %p
+ ret void
+}
+
+; Clears bit 8, just above LSB window.
+define void @test_and_i16_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i16_invalid_above'
+; CHECK: cost of 1 {{.*}} and i16 %v, -511
+; CHECK: cost of 1 {{.*}} store i16
+ %v = load i16, ptr %p
+ %res = and i16 %v, 65025 ; 0xfe01
+ store i16 %res, ptr %p
+ ret void
+}
+
+; Active bits <=8.
+define void @test_or_i16_valid(ptr %p) {
+; CHECK-LABEL: 'test_or_i16_valid'
+; CHECK: cost of 0 {{.*}} or i16 %v, 255
+; CHECK: cost of 0 {{.*}} store i16
+ %v = load i16, ptr %p
+ %res = or i16 %v, 255 ; 0x00ff
+ store i16 %res, ptr %p
+ ret void
+}
+
+; Active bits > 8.
+define void @test_or_i16_invalid(ptr %p) {
+; CHECK-LABEL: 'test_or_i16_invalid'
+; CHECK: cost of 1 {{.*}} or i16 %v, 256
+; CHECK: cost of 1 {{.*}} store i16
+ %v = load i16, ptr %p
+ %res = or i16 %v, 256 ; 0x0100
store i16 %res, ptr %p
ret void
}
-define void @test_or_i32(ptr %p) {
-; CHECK-LABEL: 'test_or_i32'
-; CHECK: cost of 0 {{.*}} or i32
+; Modifies only LSB bits.
+define void @test_and_i32_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_valid'
+; CHECK: cost of 0 {{.*}} and i32 %v, -255
; CHECK: cost of 0 {{.*}} store i32
%v = load i32, ptr %p
- %res = or i32 %v, 1
+ %res = and i32 %v, 4294967041 ; 0xffffff01
+ store i32 %res, ptr %p
+ ret void
+}
+
+; Zero-extended i16 context.
+define void @test_and_i32_zext_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_valid'
+; CHECK: cost of 0 {{.*}} and i32 %v, 65281
+; CHECK: cost of 0 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = and i32 %v, 65281 ; 0x0000ff01
+ store i32 %res, ptr %p
+ ret void
+}
+
+; One bit down from zero-extended threshold.
+define void @test_and_i32_invalid_zext_below(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid_zext_below'
+; CHECK: cost of 1 {{.*}} and i32 %v, 65025
+; CHECK: cost of 1 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = and i32 %v, 65025 ; 0x0000fe01
+ store i32 %res, ptr %p
+ ret void
+}
+
+; One bit above zero-extended threshold.
+define void @test_and_i32_invalid_zext_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid_zext_above'
+; CHECK: cost of 1 {{.*}} and i32 %v, 130817
+; CHECK: cost of 1 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = and i32 %v, 130817 ; 0x0001ff01
store i32 %res, ptr %p
ret void
}
-define void @test_and_i64(ptr %p) {
-; CHECK-LABEL: 'test_and_i64'
-; CHECK: cost of 0 {{.*}} and i64
+; Modifies bits outside LSB.
+define void @test_and_i32_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_invalid'
+; CHECK: cost of 1 {{.*}} and i32 %v, 16
+; CHECK: cost of 1 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = and i32 %v, 16
+ store i32 %res, ptr %p
+ ret void
+}
+
+; Active bits <=8.
+define void @test_xor_i32_valid(ptr %p) {
+; CHECK-LABEL: 'test_xor_i32_valid'
+; CHECK: cost of 0 {{.*}} xor i32 %v, 255
+; CHECK: cost of 0 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = xor i32 %v, 255 ; 0x000000ff
+ store i32 %res, ptr %p
+ ret void
+}
+
+; Active bits > 8.
+define void @test_xor_i32_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_xor_i32_invalid_above'
+; CHECK: cost of 1 {{.*}} xor i32 %v, 256
+; CHECK: cost of 1 {{.*}} store i32
+ %v = load i32, ptr %p
+ %res = xor i32 %v, 256 ; 0x00000100
+ store i32 %res, ptr %p
+ ret void
+}
+
+; Modifies only LSB bits.
+define void @test_and_i64_valid(ptr %p) {
+; CHECK-LABEL: 'test_and_i64_valid'
+; CHECK: cost of 0 {{.*}} and i64 %v, -255
; CHECK: cost of 0 {{.*}} store i64
%v = load i64, ptr %p
- %res = and i64 %v, 1
+ %res = and i64 %v, -255 ; 0xffffffffffffff01
+ store i64 %res, ptr %p
+ ret void
+}
+
+; Clears bit 8.
+define void @test_and_i64_invalid_above(ptr %p) {
+; CHECK-LABEL: 'test_and_i64_invalid_above'
+; CHECK: cost of 1 {{.*}} and i64 %v, -511
+; CHECK: cost of 1 {{.*}} store i64
+ %v = load i64, ptr %p
+ %res = and i64 %v, -511 ; 0xfffffffffffffffe01
+ store i64 %res, ptr %p
+ ret void
+}
+
+; Active bits <=8.
+define void @test_or_i64_valid(ptr %p) {
+; CHECK-LABEL: 'test_or_i64_valid'
+; CHECK: cost of 0 {{.*}} or i64 %v, 255
+; CHECK: cost of 0 {{.*}} store i64
+ %v = load i64, ptr %p
+ %res = or i64 %v, 255 ; 0x00000000000000ff
+ store i64 %res, ptr %p
+ ret void
+}
+
+; Active bits > 8.
+define void @test_or_i64_invalid(ptr %p) {
+; CHECK-LABEL: 'test_or_i64_invalid'
+; CHECK: cost of 1 {{.*}} or i64 %v, 256
+ %v = load i64, ptr %p
+ %res = or i64 %v, 256 ; 0x0000000000000100
store i64 %res, ptr %p
ret void
}
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index f181c6f9a9c4f..56e3871683857 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -229,9 +229,9 @@ define void @f5(ptr %src) {
; Check the high end of the NI range for i16.
define void @f5_i16(ptr %src) {
; CHECK-LABEL: f5_i16:
-; CHECK: ni 4096(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4094
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -241,9 +241,9 @@ define void @f5_i16(ptr %src) {
; Check the high end of the NI range for i32.
define void @f5_i32(ptr %src) {
; CHECK-LABEL: f5_i32:
-; CHECK: ni 4098(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4092
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -253,9 +253,9 @@ define void @f5_i32(ptr %src) {
; Check the high end of the NI range for i64.
define void @f5_i64(ptr %src) {
; CHECK-LABEL: f5_i64:
-; CHECK: ni 4102(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4088
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -277,9 +277,9 @@ define void @f6(ptr %src) {
; Check the next byte up for i16, which should use NIY instead of NI.
define void @f6_i16(ptr %src) {
; CHECK-LABEL: f6_i16:
-; CHECK: niy 4097(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4095
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -289,9 +289,9 @@ define void @f6_i16(ptr %src) {
; Check the next byte up for i32, which should use NIY instead of NI.
define void @f6_i32(ptr %src) {
; CHECK-LABEL: f6_i32:
-; CHECK: niy 4099(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4093
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -301,9 +301,9 @@ define void @f6_i32(ptr %src) {
; Check the next byte up for i64, which should use NIY instead of NI.
define void @f6_i64(ptr %src) {
; CHECK-LABEL: f6_i64:
-; CHECK: niy 4103(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4089
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -325,9 +325,9 @@ define void @f7(ptr %src) {
; Check the high end of the NIY range for i16.
define void @f7_i16(ptr %src) {
; CHECK-LABEL: f7_i16:
-; CHECK: niy 524288(%r2), 127
+; CHECK: niy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524286
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -337,9 +337,9 @@ define void @f7_i16(ptr %src) {
; Check the high end of the NIY range for i32.
define void @f7_i32(ptr %src) {
; CHECK-LABEL: f7_i32:
-; CHECK: niy 524290(%r2), 127
+; CHECK: niy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524284
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -349,9 +349,9 @@ define void @f7_i32(ptr %src) {
; Check the high end of the NIY range for i64.
define void @f7_i64(ptr %src) {
; CHECK-LABEL: f7_i64:
-; CHECK: niy 524294(%r2), 127
+; CHECK: niy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524280
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -429,9 +429,9 @@ define void @f9(ptr %src) {
; Check the high end of the negative NIY range for i16.
define void @f9_i16(ptr %src) {
; CHECK-LABEL: f9_i16:
-; CHECK: niy 0(%r2), 127
+; CHECK: niy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -2
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -441,9 +441,9 @@ define void @f9_i16(ptr %src) {
; Check the high end of the negative NIY range for i32.
define void @f9_i32(ptr %src) {
; CHECK-LABEL: f9_i32:
-; CHECK: niy 2(%r2), 127
+; CHECK: niy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -4
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -453,9 +453,9 @@ define void @f9_i32(ptr %src) {
; Check the high end of the negative NIY range for i64.
define void @f9_i64(ptr %src) {
; CHECK-LABEL: f9_i64:
-; CHECK: niy 6(%r2), 127
+; CHECK: niy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -8
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -477,9 +477,9 @@ define void @f10(ptr %src) {
; Check the low end of the NIY range for i16.
define void @f10_i16(ptr %src) {
; CHECK-LABEL: f10_i16:
-; CHECK: niy -524287(%r2), 127
+; CHECK: niy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -489,9 +489,9 @@ define void @f10_i16(ptr %src) {
; Check the low end of the NIY range for i32.
define void @f10_i32(ptr %src) {
; CHECK-LABEL: f10_i32:
-; CHECK: niy -524285(%r2), 127
+; CHECK: niy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524291
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -501,9 +501,9 @@ define void @f10_i32(ptr %src) {
; Check the low end of the NIY range for i64.
define void @f10_i64(ptr %src) {
; CHECK-LABEL: f10_i64:
-; CHECK: niy -524281(%r2), 127
+; CHECK: niy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524295
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -528,10 +528,10 @@ define void @f11(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i16(ptr %src) {
; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
; CHECK: ni 1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524290
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
store i16 %and, ptr %ptr
@@ -542,10 +542,10 @@ define void @f11_i16(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i32(ptr %src) {
; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
; CHECK: ni 3(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524292
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
store i32 %and, ptr %ptr
@@ -556,10 +556,10 @@ define void @f11_i32(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i64(ptr %src) {
; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
; CHECK: ni 7(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524296
%val = load i64, ptr %ptr
%and = and i64 %val, -129
store i64 %and, ptr %ptr
@@ -585,10 +585,10 @@ define void @f12(i64 %src, i64 %index) {
define void @f12_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i16:
; CHECK: agr %r2, %r3
-; CHECK: ni 4096(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4094
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
@@ -600,10 +600,10 @@ define void @f12_i16(i64 %src, i64 %index) {
define void @f12_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i32:
; CHECK: agr %r2, %r3
-; CHECK: ni 4098(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4092
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
@@ -615,10 +615,10 @@ define void @f12_i32(i64 %src, i64 %index) {
define void @f12_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i64:
; CHECK: agr %r2, %r3
-; CHECK: ni 4102(%r2), 127
+; CHECK: ni 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4088
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%and = and i64 %val, -129
@@ -645,10 +645,10 @@ define void @f13(i64 %src, i64 %index) {
define void @f13_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i16:
; CHECK: agr %r2, %r3
-; CHECK: niy 4097(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%and = and i16 %val, 65407
@@ -660,10 +660,10 @@ define void @f13_i16(i64 %src, i64 %index) {
define void @f13_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i32:
; CHECK: agr %r2, %r3
-; CHECK: niy 4099(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4093
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%and = and i32 %val, 4294967167
@@ -675,10 +675,10 @@ define void @f13_i32(i64 %src, i64 %index) {
define void @f13_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i64:
; CHECK: agr %r2, %r3
-; CHECK: niy 4103(%r2), 127
+; CHECK: niy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4089
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%and = and i64 %val, -129
diff --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index e6b05a77f1f6f..84dfab8a2ef56 100644
--- a/llvm/test/CodeGen/SystemZ/or-05.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05.ll
@@ -209,9 +209,9 @@ define void @f5(ptr %src) {
; Check the high end of the OI range for i16.
define void @f5_i16(ptr %src) {
; CHECK-LABEL: f5_i16:
-; CHECK: oi 4096(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4094
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -221,9 +221,9 @@ define void @f5_i16(ptr %src) {
; Check the high end of the OI range for i32.
define void @f5_i32(ptr %src) {
; CHECK-LABEL: f5_i32:
-; CHECK: oi 4098(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4092
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -233,9 +233,9 @@ define void @f5_i32(ptr %src) {
; Check the high end of the OI range for i64.
define void @f5_i64(ptr %src) {
; CHECK-LABEL: f5_i64:
-; CHECK: oi 4102(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4088
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -257,9 +257,9 @@ define void @f6(ptr %src) {
; Check the next byte up for i16, which should use OIY instead of OI.
define void @f6_i16(ptr %src) {
; CHECK-LABEL: f6_i16:
-; CHECK: oiy 4097(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4095
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -269,9 +269,9 @@ define void @f6_i16(ptr %src) {
; Check the next byte up for i32, which should use OIY instead of OI.
define void @f6_i32(ptr %src) {
; CHECK-LABEL: f6_i32:
-; CHECK: oiy 4099(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4093
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -281,9 +281,9 @@ define void @f6_i32(ptr %src) {
; Check the next byte up for i64, which should use OIY instead of OI.
define void @f6_i64(ptr %src) {
; CHECK-LABEL: f6_i64:
-; CHECK: oiy 4103(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4089
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -305,9 +305,9 @@ define void @f7(ptr %src) {
; Check the high end of the OIY range for i16.
define void @f7_i16(ptr %src) {
; CHECK-LABEL: f7_i16:
-; CHECK: oiy 524288(%r2), 127
+; CHECK: oiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524286
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -317,9 +317,9 @@ define void @f7_i16(ptr %src) {
; Check the high end of the OIY range for i32.
define void @f7_i32(ptr %src) {
; CHECK-LABEL: f7_i32:
-; CHECK: oiy 524290(%r2), 127
+; CHECK: oiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524284
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -329,9 +329,9 @@ define void @f7_i32(ptr %src) {
; Check the high end of the OIY range for i64.
define void @f7_i64(ptr %src) {
; CHECK-LABEL: f7_i64:
-; CHECK: oiy 524294(%r2), 127
+; CHECK: oiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524280
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -409,9 +409,9 @@ define void @f9(ptr %src) {
; Check the high end of the negative OIY range for i16.
define void @f9_i16(ptr %src) {
; CHECK-LABEL: f9_i16:
-; CHECK: oiy 0(%r2), 127
+; CHECK: oiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -2
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -421,9 +421,9 @@ define void @f9_i16(ptr %src) {
; Check the high end of the negative OIY range for i32.
define void @f9_i32(ptr %src) {
; CHECK-LABEL: f9_i32:
-; CHECK: oiy 2(%r2), 127
+; CHECK: oiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -4
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -433,9 +433,9 @@ define void @f9_i32(ptr %src) {
; Check the high end of the negative OIY range for i64.
define void @f9_i64(ptr %src) {
; CHECK-LABEL: f9_i64:
-; CHECK: oiy 6(%r2), 127
+; CHECK: oiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -8
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -457,9 +457,9 @@ define void @f10(ptr %src) {
; Check the low end of the OIY range for i16.
define void @f10_i16(ptr %src) {
; CHECK-LABEL: f10_i16:
-; CHECK: oiy -524287(%r2), 127
+; CHECK: oiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -469,9 +469,9 @@ define void @f10_i16(ptr %src) {
; Check the low end of the OIY range for i32.
define void @f10_i32(ptr %src) {
; CHECK-LABEL: f10_i32:
-; CHECK: oiy -524285(%r2), 127
+; CHECK: oiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524291
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -481,9 +481,9 @@ define void @f10_i32(ptr %src) {
; Check the low end of the OIY range for i64.
define void @f10_i64(ptr %src) {
; CHECK-LABEL: f10_i64:
-; CHECK: oiy -524281(%r2), 127
+; CHECK: oiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524295
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -508,10 +508,10 @@ define void @f11(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i16(ptr %src) {
; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
; CHECK: oi 1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524290
%val = load i16, ptr %ptr
%or = or i16 %val, 127
store i16 %or, ptr %ptr
@@ -522,10 +522,10 @@ define void @f11_i16(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i32(ptr %src) {
; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
; CHECK: oi 3(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524292
%val = load i32, ptr %ptr
%or = or i32 %val, 127
store i32 %or, ptr %ptr
@@ -536,10 +536,10 @@ define void @f11_i32(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i64(ptr %src) {
; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
; CHECK: oi 7(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524296
%val = load i64, ptr %ptr
%or = or i64 %val, 127
store i64 %or, ptr %ptr
@@ -565,10 +565,10 @@ define void @f12(i64 %src, i64 %index) {
define void @f12_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i16:
; CHECK: agr %r2, %r3
-; CHECK: oi 4096(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4094
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%or = or i16 %val, 127
@@ -580,10 +580,10 @@ define void @f12_i16(i64 %src, i64 %index) {
define void @f12_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i32:
; CHECK: agr %r2, %r3
-; CHECK: oi 4098(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4092
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%or = or i32 %val, 127
@@ -595,10 +595,10 @@ define void @f12_i32(i64 %src, i64 %index) {
define void @f12_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i64:
; CHECK: agr %r2, %r3
-; CHECK: oi 4102(%r2), 127
+; CHECK: oi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4088
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%or = or i64 %val, 127
@@ -625,10 +625,10 @@ define void @f13(i64 %src, i64 %index) {
define void @f13_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i16:
; CHECK: agr %r2, %r3
-; CHECK: oiy 4097(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%or = or i16 %val, 127
@@ -640,10 +640,10 @@ define void @f13_i16(i64 %src, i64 %index) {
define void @f13_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i32:
; CHECK: agr %r2, %r3
-; CHECK: oiy 4099(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4093
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%or = or i32 %val, 127
@@ -655,10 +655,10 @@ define void @f13_i32(i64 %src, i64 %index) {
define void @f13_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i64:
; CHECK: agr %r2, %r3
-; CHECK: oiy 4103(%r2), 127
+; CHECK: oiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4089
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%or = or i64 %val, 127
diff --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 50bc759837d79..52c7788b75509 100644
--- a/llvm/test/CodeGen/SystemZ/xor-05.ll
+++ b/llvm/test/CodeGen/SystemZ/xor-05.ll
@@ -209,9 +209,9 @@ define void @f5(ptr %src) {
; Check the high end of the XI range for i16.
define void @f5_i16(ptr %src) {
; CHECK-LABEL: f5_i16:
-; CHECK: xi 4096(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4094
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -221,9 +221,9 @@ define void @f5_i16(ptr %src) {
; Check the high end of the XI range for i32.
define void @f5_i32(ptr %src) {
; CHECK-LABEL: f5_i32:
-; CHECK: xi 4098(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4092
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -233,9 +233,9 @@ define void @f5_i32(ptr %src) {
; Check the high end of the XI range for i64.
define void @f5_i64(ptr %src) {
; CHECK-LABEL: f5_i64:
-; CHECK: xi 4102(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4095
+ %ptr = getelementptr i8, ptr %src, i64 4088
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -257,9 +257,9 @@ define void @f6(ptr %src) {
; Check the next byte up for i16, which should use XIY instead of XI.
define void @f6_i16(ptr %src) {
; CHECK-LABEL: f6_i16:
-; CHECK: xiy 4097(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4095
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -269,9 +269,9 @@ define void @f6_i16(ptr %src) {
; Check the next byte up for i32, which should use XIY instead of XI.
define void @f6_i32(ptr %src) {
; CHECK-LABEL: f6_i32:
-; CHECK: xiy 4099(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4093
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -281,9 +281,9 @@ define void @f6_i32(ptr %src) {
; Check the next byte up for i64, which should use XIY instead of XI.
define void @f6_i64(ptr %src) {
; CHECK-LABEL: f6_i64:
-; CHECK: xiy 4103(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 4096
+ %ptr = getelementptr i8, ptr %src, i64 4089
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -305,9 +305,9 @@ define void @f7(ptr %src) {
; Check the high end of the XIY range for i16.
define void @f7_i16(ptr %src) {
; CHECK-LABEL: f7_i16:
-; CHECK: xiy 524288(%r2), 127
+; CHECK: xiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524286
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -317,9 +317,9 @@ define void @f7_i16(ptr %src) {
; Check the high end of the XIY range for i32.
define void @f7_i32(ptr %src) {
; CHECK-LABEL: f7_i32:
-; CHECK: xiy 524290(%r2), 127
+; CHECK: xiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524284
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -329,9 +329,9 @@ define void @f7_i32(ptr %src) {
; Check the high end of the XIY range for i64.
define void @f7_i64(ptr %src) {
; CHECK-LABEL: f7_i64:
-; CHECK: xiy 524294(%r2), 127
+; CHECK: xiy 524287(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 524287
+ %ptr = getelementptr i8, ptr %src, i64 524280
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -409,9 +409,9 @@ define void @f9(ptr %src) {
; Check the high end of the negative XIY range for i16.
define void @f9_i16(ptr %src) {
; CHECK-LABEL: f9_i16:
-; CHECK: xiy 0(%r2), 127
+; CHECK: xiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -2
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -421,9 +421,9 @@ define void @f9_i16(ptr %src) {
; Check the high end of the negative XIY range for i32.
define void @f9_i32(ptr %src) {
; CHECK-LABEL: f9_i32:
-; CHECK: xiy 2(%r2), 127
+; CHECK: xiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -4
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -433,9 +433,9 @@ define void @f9_i32(ptr %src) {
; Check the high end of the negative XIY range for i64.
define void @f9_i64(ptr %src) {
; CHECK-LABEL: f9_i64:
-; CHECK: xiy 6(%r2), 127
+; CHECK: xiy -1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -1
+ %ptr = getelementptr i8, ptr %src, i64 -8
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -457,9 +457,9 @@ define void @f10(ptr %src) {
; Check the low end of the XIY range for i16.
define void @f10_i16(ptr %src) {
; CHECK-LABEL: f10_i16:
-; CHECK: xiy -524287(%r2), 127
+; CHECK: xiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524289
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -469,9 +469,9 @@ define void @f10_i16(ptr %src) {
; Check the low end of the XIY range for i32.
define void @f10_i32(ptr %src) {
; CHECK-LABEL: f10_i32:
-; CHECK: xiy -524285(%r2), 127
+; CHECK: xiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524291
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -481,9 +481,9 @@ define void @f10_i32(ptr %src) {
; Check the low end of the XIY range for i64.
define void @f10_i64(ptr %src) {
; CHECK-LABEL: f10_i64:
-; CHECK: xiy -524281(%r2), 127
+; CHECK: xiy -524288(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524288
+ %ptr = getelementptr i8, ptr %src, i64 -524295
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -508,10 +508,10 @@ define void @f11(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i16(ptr %src) {
; CHECK-LABEL: f11_i16:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524290
; CHECK: xi 1(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524290
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
store i16 %xor, ptr %ptr
@@ -522,10 +522,10 @@ define void @f11_i16(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i32(ptr %src) {
; CHECK-LABEL: f11_i32:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524292
; CHECK: xi 3(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524292
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
store i32 %xor, ptr %ptr
@@ -536,10 +536,10 @@ define void @f11_i32(ptr %src) {
; Other sequences besides this one would be OK.
define void @f11_i64(ptr %src) {
; CHECK-LABEL: f11_i64:
-; CHECK: agfi %r2, -524289
+; CHECK: agfi %r2, -524296
; CHECK: xi 7(%r2), 127
; CHECK: br %r14
- %ptr = getelementptr i8, ptr %src, i64 -524289
+ %ptr = getelementptr i8, ptr %src, i64 -524296
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
store i64 %xor, ptr %ptr
@@ -565,10 +565,10 @@ define void @f12(i64 %src, i64 %index) {
define void @f12_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i16:
; CHECK: agr %r2, %r3
-; CHECK: xi 4096(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4094
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
@@ -580,10 +580,10 @@ define void @f12_i16(i64 %src, i64 %index) {
define void @f12_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i32:
; CHECK: agr %r2, %r3
-; CHECK: xi 4098(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4092
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
@@ -595,10 +595,10 @@ define void @f12_i32(i64 %src, i64 %index) {
define void @f12_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f12_i64:
; CHECK: agr %r2, %r3
-; CHECK: xi 4102(%r2), 127
+; CHECK: xi 4095(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4095
+ %add2 = add i64 %add1, 4088
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
@@ -625,10 +625,10 @@ define void @f13(i64 %src, i64 %index) {
define void @f13_i16(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i16:
; CHECK: agr %r2, %r3
-; CHECK: xiy 4097(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4095
%ptr = inttoptr i64 %add2 to ptr
%val = load i16, ptr %ptr
%xor = xor i16 %val, 127
@@ -640,10 +640,10 @@ define void @f13_i16(i64 %src, i64 %index) {
define void @f13_i32(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i32:
; CHECK: agr %r2, %r3
-; CHECK: xiy 4099(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4093
%ptr = inttoptr i64 %add2 to ptr
%val = load i32, ptr %ptr
%xor = xor i32 %val, 127
@@ -655,10 +655,10 @@ define void @f13_i32(i64 %src, i64 %index) {
define void @f13_i64(i64 %src, i64 %index) {
; CHECK-LABEL: f13_i64:
; CHECK: agr %r2, %r3
-; CHECK: xiy 4103(%r2), 127
+; CHECK: xiy 4096(%r2), 127
; CHECK: br %r14
%add1 = add i64 %src, %index
- %add2 = add i64 %add1, 4096
+ %add2 = add i64 %add1, 4089
%ptr = inttoptr i64 %add2 to ptr
%val = load i64, ptr %ptr
%xor = xor i64 %val, 127
>From d00ff19eadbd7713b8f3ac02d1aa6012e0d1fdb6 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Sun, 7 Jun 2026 18:56:01 +0200
Subject: [PATCH 07/11] [SystemZ] Address code review feedback for folding
i16/i32/i64 logical RMW operation. - Fix a bug where 32-bit AND
operations using a zero-extended i16 mask (e.g., 0x0000ff00) were matched
against byte-level imm (NI). Updated SystemZTTI CostModel with the same.
- Split the TableGen imm32ll8c predicate into separate imm32ll8c and
imm16ll8c variants. - Restructured RMWIByte multiclass in terms of
RMWIOffset helper class.
---
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 6 +-
llvm/lib/Target/SystemZ/SystemZOperands.td | 21 +++--
llvm/lib/Target/SystemZ/SystemZPatterns.td | 86 +++++++++----------
.../SystemZ/SystemZTargetTransformInfo.cpp | 3 +-
.../CostModel/SystemZ/fold-rmw-cost.ll | 16 ++--
llvm/test/CodeGen/SystemZ/and-05.ll | 2 +-
6 files changed, 66 insertions(+), 68 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index a7f008c785979..0549124f57373 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1329,7 +1329,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm NC : MemorySS<"nc", 0xD4, z_nc>;
}
-defm : RMWIByte<and, NI, NIY>;
+defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm64ll8c>;
//===----------------------------------------------------------------------===//
// OR
@@ -1385,7 +1385,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm OC : MemorySS<"oc", 0xD6, z_oc>;
}
-defm : RMWIByte<or, OI, OIY>;
+defm : RMWIByte<or, OI, OIY, immLSB8, immLSB8, immLSB8>;
//===----------------------------------------------------------------------===//
// XOR
@@ -1424,7 +1424,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm XC : MemorySS<"xc", 0xD7, z_xc>;
}
-defm : RMWIByte<xor, XI, XIY>;
+defm : RMWIByte<xor, XI, XIY, immLSB8, immLSB8, immLSB8>;
//===----------------------------------------------------------------------===//
// Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index c1f62c036b283..46d7b25c0209c 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -444,20 +444,19 @@ defm imm64hh16c : Immediate<i64, [{
SystemZ::isImmHH(uint64_t(~Imm.getZExtValue()));
}], HH16, "U16Imm">;
-// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one
-// or zero-extended from a 16-bit.
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one.
defm imm32ll8c : Immediate<i32, [{
if (!Imm.isIntN(32)) return false;
uint64_t Val = Imm.getZExtValue();
- // Case 1: Upper 24 bits are all ones.
- if ((Val & 0xffffff00ULL) == 0xffffff00ULL)
- return true;
- // Case 2: Zero-extended i16 where the upper 16 bits are zero and the
- // second least significant byte is 0xff (0x0000ff00).
- if ((Val & 0xffffff00ULL) == 0x0000ff00ULL) {
- return true;
- }
- return false;
+ return (Val & 0xffffff00ULL) == 0xffffff00ULL;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the second 8-bit LSB chunk of an i32, with the other bits
+// being one.
+defm imm16ll8c : Immediate<i32, [{
+ if (!Imm.isIntN(32)) return false;
+ uint64_t Val = Imm.getZExtValue();
+ return (Val & 0xff00ULL) == 0xff00ULL;
}], UIMM8, "U8Imm">;
// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 9d92e88113c07..3b115b1fbd690 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -45,52 +45,52 @@ class RMWI<SDPatternOperator load, SDPatternOperator operator,
: Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
(insn mode:$addr, (UIMM8 imm:$src))>;
+// Helpers for the ComplexPattern offset variants.
+// 16-bit truncation operations variant (offset 1).
+class RMWIOffset16<SDPatternOperator load, SDPatternOperator operator,
+ ComplexPattern mode, PatFrag imm, Instruction insn>
+ : Pat<(truncstorei16 (i32 (operator (i32 (load mode:$addr)), imm:$src)),
+ mode:$addr),
+ (insn mode:$addr, (UIMM8 $src))>;
+
+// 32-bit operations variant (offsets 3).
+class RMWIOffset32<SDPatternOperator load, SDPatternOperator operator,
+ SDPatternOperator store, ComplexPattern mode,
+ PatFrag imm, Instruction insn>
+ : Pat<(store (i32 (operator (i32 (load mode:$addr)), imm:$src)), mode:$addr),
+ (insn mode:$addr, (UIMM8 $src))>;
+
+// 64-bit operations variant (offset 7).
+class RMWIOffset64<SDPatternOperator load, SDPatternOperator operator,
+ SDPatternOperator store, ComplexPattern mode,
+ PatFrag imm, Instruction insn>
+ : Pat<(store (i64 (operator (i64 (load mode:$addr)), imm:$src)), mode:$addr),
+ (insn mode:$addr, (UIMM8 $src))>;
+
// Record that INSN performs binary operation OPERATION on a byte
// memory location. IMM is the type of the second operand.
multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
- Instruction insn20> {
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm32,
- insn12>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair, imm64,
- insn12>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm32,
- insn20>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair, imm64,
- insn20>;
-
- // 12-bit instruction variants.
- def : Pat<(truncstorei16 (i32 (operator
- (i32 (anyextloadi16 bdaddr12off1:$addr)),
- (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
- bdaddr12off1:$addr),
- (insn12 bdaddr12off1:$addr, (UIMM8 $imm))>;
-
- def : Pat<(store (i32 (operator (i32 (load bdaddr12off3:$addr)),
- (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
- bdaddr12off3:$addr),
- (insn12 bdaddr12off3:$addr, (UIMM8 $imm))>;
-
- def : Pat<(store (i64 (operator (i64 (load bdaddr12off7:$addr)),
- (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
- bdaddr12off7:$addr),
- (insn12 bdaddr12off7:$addr, (UIMM8 $imm))>;
-
- // 20-bit instruction variants.
- def : Pat<(truncstorei16 (i32 (operator
- (i32 (anyextloadi16 bdaddr20off1:$addr)),
- (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
- bdaddr20off1:$addr),
- (insn20 bdaddr20off1:$addr, (UIMM8 $imm))>;
-
- def : Pat<(store (i32 (operator (i32 (load bdaddr20off3:$addr)),
- (i32 !if(!eq(operator, and), imm32ll8c, immLSB8):$imm))),
- bdaddr20off3:$addr),
- (insn20 bdaddr20off3:$addr, (UIMM8 $imm))>;
-
- def : Pat<(store (i64 (operator (i64 (load bdaddr20off7:$addr)),
- (i64 !if(!eq(operator, and), imm64ll8c, immLSB8):$imm))),
- bdaddr20off7:$addr),
- (insn20 bdaddr20off7:$addr, (UIMM8 $imm))>;
+ Instruction insn20, PatFrag imm16Leaf,
+ PatFrag imm32Leaf, PatFrag imm64Leaf> {
+ // Base register-displacement pairs.
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+ imm32, insn12>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+ imm64, insn12>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+ imm32, insn20>;
+ def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+ imm64, insn20>;
+
+ // 12-bit instruction offset variants.
+ def : RMWIOffset16<anyextloadi16, operator, bdaddr12off1, imm16Leaf, insn12>;
+ def : RMWIOffset32<load, operator, store, bdaddr12off3, imm32Leaf, insn12>;
+ def : RMWIOffset64<load, operator, store, bdaddr12off7, imm64Leaf, insn12>;
+
+ // 20-bit instruction offset variants.
+ def : RMWIOffset16<anyextloadi16, operator, bdaddr20off1, imm16Leaf, insn20>;
+ def : RMWIOffset32<load, operator, store, bdaddr20off3, imm32Leaf, insn20>;
+ def : RMWIOffset64<load, operator, store, bdaddr20off7, imm64Leaf, insn20>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index 1f0a9fc89a0be..a0d4f32198126 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -570,8 +570,7 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
if (Opcode == Instruction::And) {
if (BitWidth == 16 && (Val & 0xff00ULL) != 0xff00ULL)
return false;
- if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL &&
- (Val & 0xffffff00ULL) != 0x0000ff00ULL)
+ if (BitWidth == 32 && (Val & 0xffffff00ULL) != 0xffffff00ULL)
return false;
if (BitWidth == 64 &&
(Val & 0xffffffffffffff00ULL) != 0xffffffffffffff00ULL)
diff --git a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
index c05fb3cd12a17..c4ef9734814cc 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -203,10 +203,10 @@ define void @test_and_i32_valid(ptr %p) {
}
; Zero-extended i16 context.
-define void @test_and_i32_zext_valid(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_zext_valid'
-; CHECK: cost of 0 {{.*}} and i32 %v, 65281
-; CHECK: cost of 0 {{.*}} store i32
+define void @test_and_i32_zext_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_invalid'
+; CHECK: cost of 1 {{.*}} and i32 %v, 65281
+; CHECK: cost of 1 {{.*}} store i32
%v = load i32, ptr %p
%res = and i32 %v, 65281 ; 0x0000ff01
store i32 %res, ptr %p
@@ -214,8 +214,8 @@ define void @test_and_i32_zext_valid(ptr %p) {
}
; One bit down from zero-extended threshold.
-define void @test_and_i32_invalid_zext_below(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_invalid_zext_below'
+define void @test_and_i32_zext_below_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_below_invalid'
; CHECK: cost of 1 {{.*}} and i32 %v, 65025
; CHECK: cost of 1 {{.*}} store i32
%v = load i32, ptr %p
@@ -225,8 +225,8 @@ define void @test_and_i32_invalid_zext_below(ptr %p) {
}
; One bit above zero-extended threshold.
-define void @test_and_i32_invalid_zext_above(ptr %p) {
-; CHECK-LABEL: 'test_and_i32_invalid_zext_above'
+define void @test_and_i32_zext_above_invalid(ptr %p) {
+; CHECK-LABEL: 'test_and_i32_zext_above_invalid'
; CHECK: cost of 1 {{.*}} and i32 %v, 130817
; CHECK: cost of 1 {{.*}} store i32
%v = load i32, ptr %p
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 56e3871683857..7468ac2ac8219 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -82,7 +82,7 @@ define void @f2_i32(ptr %ptr) {
; Check i32 with zero-extended i16 pattern (0x0000FFxx).
define void @f2_i32_zext(ptr %ptr) {
; CHECK-LABEL: f2_i32_zext:
-; CHECK: ni 3(%r2), 254
+; CHECK: n %r0, 0(%r2)
; CHECK: br %r14
%val = load i32, ptr %ptr
%and = and i32 %val, 65534 ; 0x0000FFFE
>From 30cd8bbb1cc5dfeafeccfbd777e0bde460009cb9 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Fri, 12 Jun 2026 21:06:58 +0200
Subject: [PATCH 08/11] [SystemZ] Address code review feedback for folding
i16/i32/i64 logical RMW operation. - Restructured RMWIByte
multiclass in terms of RMWI with bdaddr12pairCmplx and bdaddr20pairCmplx.
- Cleaning up - imm16ll8c, imm32ll8c and imm64ll8c. - Using existing
imm32zx8 and imm64zx8 instead of PatLead immLSB8.
---
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 4 +-
llvm/lib/Target/SystemZ/SystemZOperands.td | 44 +++++++------
llvm/lib/Target/SystemZ/SystemZOperators.td | 6 --
llvm/lib/Target/SystemZ/SystemZPatterns.td | 71 +++++++++------------
4 files changed, 54 insertions(+), 71 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 0549124f57373..f492c234d89a6 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1385,7 +1385,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm OC : MemorySS<"oc", 0xD6, z_oc>;
}
-defm : RMWIByte<or, OI, OIY, immLSB8, immLSB8, immLSB8>;
+defm : RMWIByte<or, OI, OIY>;
//===----------------------------------------------------------------------===//
// XOR
@@ -1424,7 +1424,7 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm XC : MemorySS<"xc", 0xD7, z_xc>;
}
-defm : RMWIByte<xor, XI, XIY, immLSB8, immLSB8, immLSB8>;
+defm : RMWIByte<xor, XI, XIY>;
//===----------------------------------------------------------------------===//
// Combined logical operations
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 46d7b25c0209c..2b470027aa8ff 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -368,6 +368,19 @@ defm imm32zx8 : Immediate<i32, [{
defm imm32zx8trunc : Immediate<i32, [{}], UIMM8, "U8Imm">;
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one.
+defm imm32ll8c : Immediate<i32, [{
+ return Imm.isIntN(32) &&
+ (uint32_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i16 (evaluated as i32),
+// with the other bits being one.
+defm imm16ll8c : Immediate<i32, [{
+ return Imm.isIntN(16) &&
+ (uint16_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
defm imm32zx12 : Immediate<i32, [{
return Imm.isIntN(12);
}], UIMM12, "U12Imm">;
@@ -444,27 +457,6 @@ defm imm64hh16c : Immediate<i64, [{
SystemZ::isImmHH(uint64_t(~Imm.getZExtValue()));
}], HH16, "U16Imm">;
-// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one.
-defm imm32ll8c : Immediate<i32, [{
- if (!Imm.isIntN(32)) return false;
- uint64_t Val = Imm.getZExtValue();
- return (Val & 0xffffff00ULL) == 0xffffff00ULL;
-}], UIMM8, "U8Imm">;
-
-// Immediates for the second 8-bit LSB chunk of an i32, with the other bits
-// being one.
-defm imm16ll8c : Immediate<i32, [{
- if (!Imm.isIntN(32)) return false;
- uint64_t Val = Imm.getZExtValue();
- return (Val & 0xff00ULL) == 0xff00ULL;
-}], UIMM8, "U8Imm">;
-
-// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
-defm imm64ll8c : Immediate<i64, [{
- return Imm.isIntN(64) &&
- (uint64_t(~Imm.getZExtValue()) & 0xffffffffffffff00ULL) == 0;
-}], UIMM8, "U8Imm">;
-
// Immediates for the lower and upper 32 bits of an i64, with the other
// bits of the i32 being zero.
defm imm64lf32 : Immediate<i64, [{
@@ -507,6 +499,12 @@ defm imm64zx8 : Immediate<i64, [{
return Imm.isIntN(8);;
}], UIMM8, "U8Imm">;
+// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
+defm imm64ll8c : Immediate<i64, [{
+ return Imm.isIntN(64) &&
+ (uint64_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
defm imm64sx16 : Immediate<i64, [{
return Imm.isSignedIntN(16);
}], SIMM16, "S16Imm">;
@@ -702,6 +700,10 @@ def bdladdr12onlylen8 : BDLMode<"BDLAddr", "64", "12", "Only", "8">;
def bdraddr12only : BDRMode<"BDRAddr", "64", "12", "Only">;
def bdvaddr12only : BDVMode< "64", "12">;
+// Complex patterns for base register-displacement pairs.
+def bdaddr12pairCmplx : ComplexPattern<i64, 2, "selectBDAddr12Pair">;
+def bdaddr20pairCmplx : ComplexPattern<i64, 2, "selectBDAddr20Pair">;
+
// 12-bit unsigned displacement variants (for NI, OI, XI)
def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index a6b97b82301ea..739d9b2a7efb2 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1369,9 +1369,3 @@ class z_vse_by_parts<SDPatternOperator operator, int index1, int index2>
def z_vsei8_by_parts : z_vse_by_parts<sext8dbl, 7, 15>;
def z_vsei16_by_parts : z_vse_by_parts<sext16dbl, 3, 7>;
def z_vsei32_by_parts : z_vse_by_parts<sext32, 1, 3>;
-
-// Matches any integer constant where only the last byte is set (0x00...XX).
-// Safe for OR/XOR because x | 0 == x.
-def immLSB8 : PatLeaf<(imm), [{
- return N->getAPIntValue().getActiveBits() <= 8;
-}]>;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 3b115b1fbd690..a64109e9d2e92 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -39,58 +39,45 @@ multiclass ZXB<SDPatternOperator operator, RegisterOperand cls,
// with LOAD, OPERATOR and STORE being the read, modify and write
// respectively. MODE is the addressing mode and IMM is the type
// of the second operand.
-class RMWI<SDPatternOperator load, SDPatternOperator operator,
- SDPatternOperator store, AddressingMode mode,
- ImmLeaf imm, Instruction insn>
- : Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
- (insn mode:$addr, (UIMM8 imm:$src))>;
-
-// Helpers for the ComplexPattern offset variants.
-// 16-bit truncation operations variant (offset 1).
-class RMWIOffset16<SDPatternOperator load, SDPatternOperator operator,
- ComplexPattern mode, PatFrag imm, Instruction insn>
- : Pat<(truncstorei16 (i32 (operator (i32 (load mode:$addr)), imm:$src)),
- mode:$addr),
- (insn mode:$addr, (UIMM8 $src))>;
-
-// 32-bit operations variant (offsets 3).
-class RMWIOffset32<SDPatternOperator load, SDPatternOperator operator,
- SDPatternOperator store, ComplexPattern mode,
- PatFrag imm, Instruction insn>
- : Pat<(store (i32 (operator (i32 (load mode:$addr)), imm:$src)), mode:$addr),
- (insn mode:$addr, (UIMM8 $src))>;
-
-// 64-bit operations variant (offset 7).
-class RMWIOffset64<SDPatternOperator load, SDPatternOperator operator,
- SDPatternOperator store, ComplexPattern mode,
- PatFrag imm, Instruction insn>
- : Pat<(store (i64 (operator (i64 (load mode:$addr)), imm:$src)), mode:$addr),
+class RMWI<ValueType vt, SDPatternOperator load, SDPatternOperator op,
+ SDPatternOperator store, ComplexPattern mode, PatFrag imm,
+ Instruction insn>
+ : Pat<(store (op (vt (load mode:$addr)), imm:$src), mode:$addr),
(insn mode:$addr, (UIMM8 $src))>;
// Record that INSN performs binary operation OPERATION on a byte
// memory location. IMM is the type of the second operand.
multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
- Instruction insn20, PatFrag imm16Leaf,
- PatFrag imm32Leaf, PatFrag imm64Leaf> {
+ Instruction insn20, PatFrag imm16Leaf = imm32zx8,
+ PatFrag imm32Leaf = imm32zx8,
+ PatFrag imm64Leaf = imm64zx8,
+ PatFrag imm8Leaf32 = imm32zx8,
+ PatFrag imm8Leaf64 = imm64zx8> {
// Base register-displacement pairs.
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
- imm32, insn12>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
- imm64, insn12>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
- imm32, insn20>;
- def : RMWI<z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
- imm64, insn20>;
+ def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pairCmplx,
+ imm8Leaf32, insn12>;
+ def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr12pairCmplx,
+ imm8Leaf64, insn12>;
+ def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pairCmplx,
+ imm8Leaf32, insn20>;
+ def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pairCmplx,
+ imm8Leaf64, insn20>;
// 12-bit instruction offset variants.
- def : RMWIOffset16<anyextloadi16, operator, bdaddr12off1, imm16Leaf, insn12>;
- def : RMWIOffset32<load, operator, store, bdaddr12off3, imm32Leaf, insn12>;
- def : RMWIOffset64<load, operator, store, bdaddr12off7, imm64Leaf, insn12>;
+ def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr12off1,
+ imm16Leaf, insn12>;
+ def : RMWI<i32, load, operator, store, bdaddr12off3,
+ imm32Leaf, insn12>;
+ def : RMWI<i64, load, operator, store, bdaddr12off7,
+ imm64Leaf, insn12>;
// 20-bit instruction offset variants.
- def : RMWIOffset16<anyextloadi16, operator, bdaddr20off1, imm16Leaf, insn20>;
- def : RMWIOffset32<load, operator, store, bdaddr20off3, imm32Leaf, insn20>;
- def : RMWIOffset64<load, operator, store, bdaddr20off7, imm64Leaf, insn20>;
+ def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr20off1,
+ imm16Leaf, insn20>;
+ def : RMWI<i32, load, operator, store, bdaddr20off3,
+ imm32Leaf, insn20>;
+ def : RMWI<i64, load, operator, store, bdaddr20off7,
+ imm64Leaf, insn20>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
>From e86d3c393b311849c42f4599472f882837e0ad86 Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 15 Jun 2026 22:08:18 +0200
Subject: [PATCH 09/11] [SystemZ] Incorporate code review feedback for
folding i16/i32/i64 logical RMW operations.
---
llvm/lib/Target/SystemZ/SystemZOperands.td | 4 ----
llvm/lib/Target/SystemZ/SystemZPatterns.td | 20 ++++++++------------
2 files changed, 8 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 2b470027aa8ff..23234b169d09a 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -700,10 +700,6 @@ def bdladdr12onlylen8 : BDLMode<"BDLAddr", "64", "12", "Only", "8">;
def bdraddr12only : BDRMode<"BDRAddr", "64", "12", "Only">;
def bdvaddr12only : BDVMode< "64", "12">;
-// Complex patterns for base register-displacement pairs.
-def bdaddr12pairCmplx : ComplexPattern<i64, 2, "selectBDAddr12Pair">;
-def bdaddr20pairCmplx : ComplexPattern<i64, 2, "selectBDAddr20Pair">;
-
// 12-bit unsigned displacement variants (for NI, OI, XI)
def bdaddr12off1 : ComplexPattern<i64, 2, "selectBDAddr12off1">;
def bdaddr12off3 : ComplexPattern<i64, 2, "selectBDAddr12off3">;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index a64109e9d2e92..79e72d4869175 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -54,30 +54,26 @@ multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
PatFrag imm8Leaf32 = imm32zx8,
PatFrag imm8Leaf64 = imm64zx8> {
// Base register-displacement pairs.
- def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pairCmplx,
+ def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
imm8Leaf32, insn12>;
- def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr12pairCmplx,
+ def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
imm8Leaf64, insn12>;
- def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pairCmplx,
+ def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
imm8Leaf32, insn20>;
- def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pairCmplx,
+ def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
imm8Leaf64, insn20>;
// 12-bit instruction offset variants.
def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr12off1,
imm16Leaf, insn12>;
- def : RMWI<i32, load, operator, store, bdaddr12off3,
- imm32Leaf, insn12>;
- def : RMWI<i64, load, operator, store, bdaddr12off7,
- imm64Leaf, insn12>;
+ def : RMWI<i32, load, operator, store, bdaddr12off3, imm32Leaf, insn12>;
+ def : RMWI<i64, load, operator, store, bdaddr12off7, imm64Leaf, insn12>;
// 20-bit instruction offset variants.
def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr20off1,
imm16Leaf, insn20>;
- def : RMWI<i32, load, operator, store, bdaddr20off3,
- imm32Leaf, insn20>;
- def : RMWI<i64, load, operator, store, bdaddr20off7,
- imm64Leaf, insn20>;
+ def : RMWI<i32, load, operator, store, bdaddr20off3, imm32Leaf, insn20>;
+ def : RMWI<i64, load, operator, store, bdaddr20off7, imm64Leaf, insn20>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
>From bf8d14b8a8840388d56f25804f65302f2e576dbc Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Mon, 6 Jul 2026 01:59:23 +0200
Subject: [PATCH 10/11] [SystemZ] Incorporate code review feedback for
folding i16/i32/i64 logical RMW operations. - Restrict this transfomation
to non-volatile memory operation. - Extend RMW folding framework to handle
64-bit intermediate values for i16/i32 inputs.
---
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 3 +-
llvm/lib/Target/SystemZ/SystemZOperands.td | 14 +++
llvm/lib/Target/SystemZ/SystemZOperators.td | 2 +
llvm/lib/Target/SystemZ/SystemZPatterns.td | 44 ++++++++--
llvm/test/CodeGen/SystemZ/and-05.ll | 96 +++++++++++++++++++++
llvm/test/CodeGen/SystemZ/or-05.ll | 96 +++++++++++++++++++++
llvm/test/CodeGen/SystemZ/xor-05.ll | 95 ++++++++++++++++++++
7 files changed, 340 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index f492c234d89a6..c77f3e03b46cd 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1329,7 +1329,8 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm NC : MemorySS<"nc", 0xD4, z_nc>;
}
-defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm64ll8c>;
+defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm64ll8c, imm32zx8,
+ imm64zx8, imm64ll8c_i16, imm64ll8c_i32>;
//===----------------------------------------------------------------------===//
// OR
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 23234b169d09a..f6cd7cd663a06 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -505,6 +505,20 @@ defm imm64ll8c : Immediate<i64, [{
(uint64_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
}], UIMM8, "U8Imm">;
+// Immediates for the 8-bit LSB chunk of an i16, with the other i16 bits
+// being one.
+defm imm64ll8c_i16 : Immediate<i64, [{
+ return Imm.isIntN(64) &&
+ (uint16_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i32, with the other i32 bits
+// being one.
+defm imm64ll8c_i32 : Immediate<i64, [{
+ return Imm.isIntN(64) &&
+ (uint32_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
defm imm64sx16 : Immediate<i64, [{
return Imm.isSignedIntN(16);
}], SIMM16, "S16Imm">;
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 739d9b2a7efb2..005b79638218a 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1003,6 +1003,7 @@ class NonvolatileLoad<SDPatternOperator load>
def nonvolatile_anyextloadi8 : NonvolatileLoad<anyextloadi8>;
def nonvolatile_anyextloadi16 : NonvolatileLoad<anyextloadi16>;
def nonvolatile_anyextloadi32 : NonvolatileLoad<anyextloadi32>;
+def nonvolatile_load : NonvolatileLoad<load>;
// Non-volatile stores.
class NonvolatileStore<SDPatternOperator store>
@@ -1013,6 +1014,7 @@ class NonvolatileStore<SDPatternOperator store>
def nonvolatile_truncstorei8 : NonvolatileStore<truncstorei8>;
def nonvolatile_truncstorei16 : NonvolatileStore<truncstorei16>;
def nonvolatile_truncstorei32 : NonvolatileStore<truncstorei32>;
+def nonvolatile_store : NonvolatileStore<store>;
// A store of a load that can be implemented using MVC.
def mvc_store : PatFrag<(ops node:$value, node:$addr),
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 79e72d4869175..1b592a91e983d 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -52,7 +52,9 @@ multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
PatFrag imm32Leaf = imm32zx8,
PatFrag imm64Leaf = imm64zx8,
PatFrag imm8Leaf32 = imm32zx8,
- PatFrag imm8Leaf64 = imm64zx8> {
+ PatFrag imm8Leaf64 = imm64zx8,
+ PatFrag imm64Leaf16 = imm64zx8,
+ PatFrag imm64Leaf32 = imm64zx8> {
// Base register-displacement pairs.
def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
imm8Leaf32, insn12>;
@@ -64,16 +66,40 @@ multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
imm8Leaf64, insn20>;
// 12-bit instruction offset variants.
- def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr12off1,
- imm16Leaf, insn12>;
- def : RMWI<i32, load, operator, store, bdaddr12off3, imm32Leaf, insn12>;
- def : RMWI<i64, load, operator, store, bdaddr12off7, imm64Leaf, insn12>;
+ def : RMWI<i32, nonvolatile_anyextloadi16, operator,
+ nonvolatile_truncstorei16, bdaddr12off1, imm16Leaf, insn12>;
+ def : RMWI<i64, nonvolatile_anyextloadi16, operator,
+ nonvolatile_truncstorei16, bdaddr12off1, imm64Leaf16, insn12>;
+ def : RMWI<i64, sextloadi16, operator, nonvolatile_truncstorei16,
+ bdaddr12off1, imm64Leaf, insn12>;
+
+ def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
+ bdaddr12off3, imm32Leaf, insn12>;
+ def : RMWI<i64, nonvolatile_anyextloadi32, operator,
+ nonvolatile_truncstorei32, bdaddr12off3, imm64Leaf32, insn12>;
+ def : RMWI<i64, sextloadi32, operator, nonvolatile_truncstorei32,
+ bdaddr12off3, imm64Leaf, insn12>;
+
+ def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
+ bdaddr12off7, imm64Leaf, insn12>;
// 20-bit instruction offset variants.
- def : RMWI<i32, anyextloadi16, operator, truncstorei16, bdaddr20off1,
- imm16Leaf, insn20>;
- def : RMWI<i32, load, operator, store, bdaddr20off3, imm32Leaf, insn20>;
- def : RMWI<i64, load, operator, store, bdaddr20off7, imm64Leaf, insn20>;
+ def : RMWI<i32, nonvolatile_anyextloadi16, operator,
+ nonvolatile_truncstorei16, bdaddr20off1, imm16Leaf, insn20>;
+ def : RMWI<i64, nonvolatile_anyextloadi16, operator,
+ nonvolatile_truncstorei16, bdaddr20off1, imm64Leaf16, insn20>;
+ def : RMWI<i64, sextloadi16, operator, nonvolatile_truncstorei16,
+ bdaddr20off1, imm64Leaf, insn20>;
+
+ def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
+ bdaddr20off3, imm32Leaf, insn20>;
+ def : RMWI<i64, nonvolatile_anyextloadi32, operator,
+ nonvolatile_truncstorei32, bdaddr20off3, imm64Leaf32, insn20>;
+ def : RMWI<i64, sextloadi32, operator, nonvolatile_truncstorei32,
+ bdaddr20off3, imm64Leaf, insn20>;
+
+ def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
+ bdaddr20off7, imm64Leaf, insn20>;
}
// Record that INSN performs insertion TYPE into a register of class CLS.
diff --git a/llvm/test/CodeGen/SystemZ/and-05.ll b/llvm/test/CodeGen/SystemZ/and-05.ll
index 7468ac2ac8219..42fa69d8e6983 100644
--- a/llvm/test/CodeGen/SystemZ/and-05.ll
+++ b/llvm/test/CodeGen/SystemZ/and-05.ll
@@ -686,6 +686,102 @@ define void @f13_i64(i64 %src, i64 %index) {
ret void
}
+; Check NI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: ni 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %and = and i64 %ext, 65407
+ %tr = trunc i64 %and to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check NI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: ni 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %and = and i64 %ext, 4294967167
+ %tr = trunc i64 %and to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check NIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: niy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %and = and i64 %ext, 65407
+ %tr = trunc i64 %and to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check NIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: niy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %and = and i64 %ext, 4294967167
+ %tr = trunc i64 %and to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check volatile i16 load/store with AND is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK: llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT: nill [[REG]], 65407
+; CHECK-NOT: ni
+; CHECK-NOT: niy
+; CHECK: br %r14
+ %val = load volatile i16, ptr %ptr
+ %and = and i16 %val, 65407
+ store volatile i16 %and, ptr %ptr
+ ret void
+}
+
+; Check volatile i32 load/store with AND is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK: n [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: ni
+; CHECK-NOT: niy
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %and = and i32 %val, 4294967167
+ store volatile i32 %and, ptr %ptr
+ ret void
+}
+
+; Check volatile i64 load/store with AND is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK: ng [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: ni
+; CHECK-NOT: niy
+; CHECK: br %r14
+ %val = load volatile i64, ptr %ptr
+ %and = and i64 %val, -129
+ store volatile i64 %and, ptr %ptr
+ ret void
+}
+
; Check folding of multi-byte 'and' operations into byte-immediate memory
; operation 'ni'.
; Additional tests for immAndLSB8 PatLeaf logic - preservation of upper bytes
diff --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index 84dfab8a2ef56..c1b43adb59a91 100644
--- a/llvm/test/CodeGen/SystemZ/or-05.ll
+++ b/llvm/test/CodeGen/SystemZ/or-05.ll
@@ -665,3 +665,99 @@ define void @f13_i64(i64 %src, i64 %index) {
store i64 %or, ptr %ptr
ret void
}
+
+; Check OI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: oi 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %or = or i64 %ext, 127
+ %tr = trunc i64 %or to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check OI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: oi 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %or = or i64 %ext, 127
+ %tr = trunc i64 %or to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check OIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: oiy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %or = or i64 %ext, 127
+ %tr = trunc i64 %or to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check OIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: oiy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %or = or i64 %ext, 127
+ %tr = trunc i64 %or to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check volatile i16 load/store with OR is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK: lh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT: oill [[REG]], 128
+; CHECK-NOT: oi
+; CHECK-NOT: oiy
+; CHECK: br %r14
+ %val = load volatile i16, ptr %ptr
+ %or = or i16 %val, 128
+ store volatile i16 %or, ptr %ptr
+ ret void
+}
+
+; Check volatile i32 load/store with OR is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK: o [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: oi
+; CHECK-NOT: oiy
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %or = or i32 %val, 128
+ store volatile i32 %or, ptr %ptr
+ ret void
+}
+
+; Check volatile i64 load/store with OR is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK: og [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: oi
+; CHECK-NOT: oiy
+; CHECK: br %r14
+ %val = load volatile i64, ptr %ptr
+ %or = or i64 %val, 128
+ store volatile i64 %or, ptr %ptr
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 52c7788b75509..aec375368799a 100644
--- a/llvm/test/CodeGen/SystemZ/xor-05.ll
+++ b/llvm/test/CodeGen/SystemZ/xor-05.ll
@@ -666,3 +666,98 @@ define void @f13_i64(i64 %src, i64 %index) {
ret void
}
+; Check XI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: xi 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4094
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %xor = xor i64 %ext, 127
+ %tr = trunc i64 %xor to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check XI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: xi 4095(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4092
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %xor = xor i64 %ext, 127
+ %tr = trunc i64 %xor to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check XIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: xiy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4095
+ %val = load i16, ptr %ptr
+ %ext = zext i16 %val to i64
+ %xor = xor i64 %ext, 127
+ %tr = trunc i64 %xor to i16
+ store i16 %tr, ptr %ptr
+ ret void
+}
+
+; Check XIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: xiy 4096(%r2), 127
+; CHECK: br %r14
+ %ptr = getelementptr i8, ptr %src, i64 4093
+ %val = load i32, ptr %ptr
+ %ext = zext i32 %val to i64
+ %xor = xor i64 %ext, 127
+ %tr = trunc i64 %xor to i32
+ store i32 %tr, ptr %ptr
+ ret void
+}
+
+; Check volatile i16 load/store with XOR is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK: lh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT: xilf [[REG]], 128
+; CHECK-NOT: xi
+; CHECK-NOT: xiy
+; CHECK: br %r14
+ %val = load volatile i16, ptr %ptr
+ %xor = xor i16 %val, 128
+ store volatile i16 %xor, ptr %ptr
+ ret void
+}
+
+; Check volatile i32 load/store with XOR is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK: x [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: xi
+; CHECK-NOT: xiy
+; CHECK: br %r14
+ %val = load volatile i32, ptr %ptr
+ %xor = xor i32 %val, 128
+ store volatile i32 %xor, ptr %ptr
+ ret void
+}
+
+; Check volatile i64 load/store with XOR is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK: xg [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT: xi
+; CHECK-NOT: xiy
+; CHECK: br %r14
+ %val = load volatile i64, ptr %ptr
+ %xor = xor i64 %val, 128
+ store volatile i64 %xor, ptr %ptr
+ ret void
+}
>From a8522e9fab362e841ddec7cbf54905ff5d07591b Mon Sep 17 00:00:00 2001
From: anoopkg6 <anoopkg6 at github.com>
Date: Tue, 4 Aug 2026 02:05:37 +0200
Subject: [PATCH 11/11] [SystemZ] Address review comments for RMW fold patch
- Rename imm64ll8c_i16 to imm16ll8c_i64 and fix its predicate to use
isIntN(16) instead of isIntN(64), since the immediate is a 16-bit
value held in an i64.
- Rename imm64ll8c_i32 to imm32ll8c_i64 and fix its predicate to use
isIntN(32) instead of isIntN(64), since the immediate is a 32-bit
value held in an i64.
- Remove redundant sextloadi16 and sextloadi32 rules from RMWIByte,
as they are already covered by nonvolatile_anyextloadi16/32.
- Reorder RMWIByte patterns consistently: all 12-bit displacement
variants first, then all 20-bit, with offsets in ascending order
within each group.
---
llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 4 +--
llvm/lib/Target/SystemZ/SystemZOperands.td | 12 +++----
llvm/lib/Target/SystemZ/SystemZPatterns.td | 40 +++++++--------------
3 files changed, 21 insertions(+), 35 deletions(-)
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index c77f3e03b46cd..b08d40796fd00 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1329,8 +1329,8 @@ let Defs = [CC] in {
let mayLoad = 1, mayStore = 1 in
defm NC : MemorySS<"nc", 0xD4, z_nc>;
}
-defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm64ll8c, imm32zx8,
- imm64zx8, imm64ll8c_i16, imm64ll8c_i32>;
+defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c, imm32zx8,
+ imm64zx8, imm16ll8c_i64, imm32ll8c_i64, imm64ll8c>;
//===----------------------------------------------------------------------===//
// OR
diff --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index f6cd7cd663a06..8a2049c518473 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -506,16 +506,16 @@ defm imm64ll8c : Immediate<i64, [{
}], UIMM8, "U8Imm">;
// Immediates for the 8-bit LSB chunk of an i16, with the other i16 bits
-// being one.
-defm imm64ll8c_i16 : Immediate<i64, [{
- return Imm.isIntN(64) &&
+// being one. The immediate is a 16-bit value held in an i64.
+defm imm16ll8c_i64 : Immediate<i64, [{
+ return Imm.isIntN(16) &&
(uint16_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
}], UIMM8, "U8Imm">;
// Immediates for the 8-bit LSB chunk of an i32, with the other i32 bits
-// being one.
-defm imm64ll8c_i32 : Immediate<i64, [{
- return Imm.isIntN(64) &&
+// being one. The immediate is a 32-bit value held in an i64.
+defm imm32ll8c_i64 : Immediate<i64, [{
+ return Imm.isIntN(32) &&
(uint32_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
}], UIMM8, "U8Imm">;
diff --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 1b592a91e983d..a9e62cbe620a4 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -50,54 +50,40 @@ class RMWI<ValueType vt, SDPatternOperator load, SDPatternOperator op,
multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
Instruction insn20, PatFrag imm16Leaf = imm32zx8,
PatFrag imm32Leaf = imm32zx8,
- PatFrag imm64Leaf = imm64zx8,
PatFrag imm8Leaf32 = imm32zx8,
PatFrag imm8Leaf64 = imm64zx8,
- PatFrag imm64Leaf16 = imm64zx8,
- PatFrag imm64Leaf32 = imm64zx8> {
- // Base register-displacement pairs.
+ PatFrag imm16Leaf64 = imm64zx8,
+ PatFrag imm32Leaf64 = imm64zx8,
+ PatFrag imm64Leaf = imm64zx8> {
+ // 12-bit displacement variants.
def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
imm8Leaf32, insn12>;
def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
imm8Leaf64, insn12>;
- def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
- imm8Leaf32, insn20>;
- def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
- imm8Leaf64, insn20>;
-
- // 12-bit instruction offset variants.
def : RMWI<i32, nonvolatile_anyextloadi16, operator,
nonvolatile_truncstorei16, bdaddr12off1, imm16Leaf, insn12>;
def : RMWI<i64, nonvolatile_anyextloadi16, operator,
- nonvolatile_truncstorei16, bdaddr12off1, imm64Leaf16, insn12>;
- def : RMWI<i64, sextloadi16, operator, nonvolatile_truncstorei16,
- bdaddr12off1, imm64Leaf, insn12>;
-
+ nonvolatile_truncstorei16, bdaddr12off1, imm16Leaf64, insn12>;
def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
bdaddr12off3, imm32Leaf, insn12>;
def : RMWI<i64, nonvolatile_anyextloadi32, operator,
- nonvolatile_truncstorei32, bdaddr12off3, imm64Leaf32, insn12>;
- def : RMWI<i64, sextloadi32, operator, nonvolatile_truncstorei32,
- bdaddr12off3, imm64Leaf, insn12>;
-
+ nonvolatile_truncstorei32, bdaddr12off3, imm32Leaf64, insn12>;
def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
bdaddr12off7, imm64Leaf, insn12>;
- // 20-bit instruction offset variants.
+ // 20-bit displacement variants.
+ def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+ imm8Leaf32, insn20>;
+ def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+ imm8Leaf64, insn20>;
def : RMWI<i32, nonvolatile_anyextloadi16, operator,
nonvolatile_truncstorei16, bdaddr20off1, imm16Leaf, insn20>;
def : RMWI<i64, nonvolatile_anyextloadi16, operator,
- nonvolatile_truncstorei16, bdaddr20off1, imm64Leaf16, insn20>;
- def : RMWI<i64, sextloadi16, operator, nonvolatile_truncstorei16,
- bdaddr20off1, imm64Leaf, insn20>;
-
+ nonvolatile_truncstorei16, bdaddr20off1, imm16Leaf64, insn20>;
def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
bdaddr20off3, imm32Leaf, insn20>;
def : RMWI<i64, nonvolatile_anyextloadi32, operator,
- nonvolatile_truncstorei32, bdaddr20off3, imm64Leaf32, insn20>;
- def : RMWI<i64, sextloadi32, operator, nonvolatile_truncstorei32,
- bdaddr20off3, imm64Leaf, insn20>;
-
+ nonvolatile_truncstorei32, bdaddr20off3, imm32Leaf64, insn20>;
def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
bdaddr20off7, imm64Leaf, insn20>;
}
More information about the llvm-commits
mailing list