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