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

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 03:48:04 PDT 2026


Author: anoopkg6
Date: 2026-08-05T12:47:59+02:00
New Revision: 86bd12d5cb40103937484a43386801b4e5353da5

URL: https://github.com/llvm/llvm-project/commit/86bd12d5cb40103937484a43386801b4e5353da5
DIFF: https://github.com/llvm/llvm-project/commit/86bd12d5cb40103937484a43386801b4e5353da5.diff

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

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

---------

Co-authored-by: anoopkg6 <anoopkg6 at github.com>

Added: 
    

Modified: 
    llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
    llvm/lib/Target/SystemZ/SystemZInstrInfo.td
    llvm/lib/Target/SystemZ/SystemZOperands.td
    llvm/lib/Target/SystemZ/SystemZOperators.td
    llvm/lib/Target/SystemZ/SystemZPatterns.td
    llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
    llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
    llvm/test/CodeGen/SystemZ/and-05.ll
    llvm/test/CodeGen/SystemZ/or-05.ll
    llvm/test/CodeGen/SystemZ/xor-05.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp
index bb148c2dc2ebd..cb09611db7d16 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,8 +702,9 @@ 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);
+  AM.Disp += Offset;
   if (!selectAddress(Addr, AM))
     return false;
 
@@ -749,6 +759,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 693b171bcabf1..f53716a663ed8 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1329,8 +1329,8 @@ let Defs = [CC] in {
   let mayLoad = 1, mayStore = 1 in
     defm NC : MemorySS<"nc", 0xD4, z_nc>;
 }
-defm : RMWIByte<and, bdaddr12pair, NI>;
-defm : RMWIByte<and, bdaddr20pair, NIY>;
+defm : RMWIByte<and, NI, NIY, imm16ll8c, imm32ll8c,
+                imm16ll8c_i64, imm32ll8c_i64, imm64ll8c>;
 
 //===----------------------------------------------------------------------===//
 // OR
@@ -1386,8 +1386,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 : RMWIByte<or,  OI, OIY>;
 
 //===----------------------------------------------------------------------===//
 // XOR
@@ -1426,8 +1425,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 : RMWIByte<xor, XI, XIY>;
 
 //===----------------------------------------------------------------------===//
 // Combined logical operations

diff  --git a/llvm/lib/Target/SystemZ/SystemZOperands.td b/llvm/lib/Target/SystemZ/SystemZOperands.td
index 119bf4f7ca615..8a2049c518473 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperands.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperands.td
@@ -368,6 +368,19 @@ defm imm32zx8 : Immediate<i32, [{
 
 defm imm32zx8trunc : Immediate<i32, [{}], UIMM8, "U8Imm">;
 
+// Immediates for the 8-bit LSB chunk of an i32, with the other bits being one.
+defm imm32ll8c : Immediate<i32, [{
+  return Imm.isIntN(32) &&
+         (uint32_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i16 (evaluated as i32),
+// with the other bits being one.
+defm imm16ll8c : Immediate<i32, [{
+  return Imm.isIntN(16) &&
+         (uint16_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
 defm imm32zx12 : Immediate<i32, [{
   return Imm.isIntN(12);
 }], UIMM12, "U12Imm">;
@@ -486,6 +499,26 @@ defm imm64zx8 : Immediate<i64, [{
   return Imm.isIntN(8);;
 }], UIMM8, "U8Imm">;
 
+// Immediates for the 8-bit LSB chunk of an i64, with the other bits being one.
+defm imm64ll8c : Immediate<i64, [{
+  return Imm.isIntN(64) &&
+         (uint64_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i16, with the other i16 bits
+// being one.  The immediate is a 16-bit value held in an i64.
+defm imm16ll8c_i64 : Immediate<i64, [{
+  return Imm.isIntN(16) &&
+         (uint16_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
+// Immediates for the 8-bit LSB chunk of an i32, with the other i32 bits
+// being one.  The immediate is a 32-bit value held in an i64.
+defm imm32ll8c_i64 : Immediate<i64, [{
+  return Imm.isIntN(32) &&
+         (uint32_t(~Imm.getZExtValue()) & ~0xffULL) == 0;
+}], UIMM8, "U8Imm">;
+
 defm imm64sx16 : Immediate<i64, [{
   return Imm.isSignedIntN(16);
 }], SIMM16, "S16Imm">;
@@ -681,6 +714,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
 //===----------------------------------------------------------------------===//

diff  --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 739d9b2a7efb2..005b79638218a 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -1003,6 +1003,7 @@ class NonvolatileLoad<SDPatternOperator load>
 def nonvolatile_anyextloadi8  : NonvolatileLoad<anyextloadi8>;
 def nonvolatile_anyextloadi16 : NonvolatileLoad<anyextloadi16>;
 def nonvolatile_anyextloadi32 : NonvolatileLoad<anyextloadi32>;
+def nonvolatile_load : NonvolatileLoad<load>;
 
 // Non-volatile stores.
 class NonvolatileStore<SDPatternOperator store>
@@ -1013,6 +1014,7 @@ class NonvolatileStore<SDPatternOperator store>
 def nonvolatile_truncstorei8  : NonvolatileStore<truncstorei8>;
 def nonvolatile_truncstorei16 : NonvolatileStore<truncstorei16>;
 def nonvolatile_truncstorei32 : NonvolatileStore<truncstorei32>;
+def nonvolatile_store : NonvolatileStore<store>;
 
 // A store of a load that can be implemented using MVC.
 def mvc_store : PatFrag<(ops node:$value, node:$addr),

diff  --git a/llvm/lib/Target/SystemZ/SystemZPatterns.td b/llvm/lib/Target/SystemZ/SystemZPatterns.td
index 4d6bc68e9a7ed..6d6d48083dd18 100644
--- a/llvm/lib/Target/SystemZ/SystemZPatterns.td
+++ b/llvm/lib/Target/SystemZ/SystemZPatterns.td
@@ -39,18 +39,51 @@ multiclass ZXB<SDPatternOperator operator, RegisterOperand cls,
 // with LOAD, OPERATOR and STORE being the read, modify and write
 // respectively.  MODE is the addressing mode and IMM is the type
 // of the second operand.
-class RMWI<SDPatternOperator load, SDPatternOperator operator,
-           SDPatternOperator store, AddressingMode mode,
-           PatFrag imm, Instruction insn>
-  : Pat<(store (operator (load mode:$addr), imm:$src), mode:$addr),
-        (insn mode:$addr, (UIMM8 imm:$src))>;
+class RMWI<ValueType vt, SDPatternOperator load, SDPatternOperator op,
+           SDPatternOperator store, ComplexPattern mode, PatFrag imm,
+           Instruction insn>
+  : Pat<(store (op (vt (load mode:$addr)), imm:$src), mode:$addr),
+        (insn mode:$addr, (UIMM8 $src))>;
 
 // Record that INSN performs binary operation OPERATION on a byte
 // memory location.  IMM is the type of the second operand.
-multiclass RMWIByte<SDPatternOperator operator, AddressingMode mode,
-                    Instruction insn> {
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm32, insn>;
-  def : RMWI<z_anyextloadi8, operator, truncstorei8, mode, imm64, insn>;
+multiclass RMWIByte<SDPatternOperator operator, Instruction insn12,
+                    Instruction insn20, PatFrag imm16Leaf = imm32zx8,
+                    PatFrag imm32Leaf = imm32zx8,
+                    PatFrag imm16Leaf64 = imm64zx8,
+                    PatFrag imm32Leaf64 = imm64zx8,
+                    PatFrag imm64Leaf = imm64zx8> {
+  // 12-bit displacement variants.
+  def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+             imm32zx8, insn12>;
+  def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr12pair,
+             imm64zx8, insn12>;
+  def : RMWI<i32, nonvolatile_anyextloadi16, operator,
+             nonvolatile_truncstorei16, bdaddr12off1, imm16Leaf, insn12>;
+  def : RMWI<i64, nonvolatile_anyextloadi16, operator,
+             nonvolatile_truncstorei16, bdaddr12off1, imm16Leaf64, insn12>;
+  def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
+             bdaddr12off3, imm32Leaf, insn12>;
+  def : RMWI<i64, nonvolatile_anyextloadi32, operator,
+             nonvolatile_truncstorei32, bdaddr12off3, imm32Leaf64, insn12>;
+  def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
+             bdaddr12off7, imm64Leaf, insn12>;
+
+  // 20-bit displacement variants.
+  def : RMWI<i32, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+             imm32zx8, insn20>;
+  def : RMWI<i64, z_anyextloadi8, operator, truncstorei8, bdaddr20pair,
+             imm64zx8, insn20>;
+  def : RMWI<i32, nonvolatile_anyextloadi16, operator,
+             nonvolatile_truncstorei16, bdaddr20off1, imm16Leaf, insn20>;
+  def : RMWI<i64, nonvolatile_anyextloadi16, operator,
+             nonvolatile_truncstorei16, bdaddr20off1, imm16Leaf64, insn20>;
+  def : RMWI<i32, nonvolatile_load, operator, nonvolatile_store,
+             bdaddr20off3, imm32Leaf, insn20>;
+  def : RMWI<i64, nonvolatile_anyextloadi32, operator,
+             nonvolatile_truncstorei32, bdaddr20off3, imm32Leaf64, insn20>;
+  def : RMWI<i64, nonvolatile_load, operator, nonvolatile_store,
+             bdaddr20off7, imm64Leaf, insn20>;
 }
 
 // Record that INSN performs insertion TYPE into a register of class CLS.

diff  --git a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
index 5353c36fd9b97..a0d4f32198126 100644
--- a/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZTargetTransformInfo.cpp
@@ -556,10 +556,32 @@ static bool isFoldableRMW(const Instruction *I, Type *Ty) {
   switch (Opcode) {
   case Instruction::And:
   case Instruction::Or:
-  case Instruction::Xor:
-    if (BitWidth != 8)
+  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)
+        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 98aa0cda026d1..c4ef9734814cc 100644
--- a/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
+++ b/llvm/test/Analysis/CostModel/SystemZ/fold-rmw-cost.ll
@@ -145,3 +145,168 @@ define void @test_vector_no_fold(<4 x i32> %val, ptr %p) {
   ret void
 }
 
+; 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 = 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
+}
+
+; 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 = and i32 %v, 4294967041 ; 0xffffff01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; Zero-extended i16 context.
+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
+  ret void
+}
+
+; One bit down from zero-extended threshold.
+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
+  %res = and i32 %v, 65025 ; 0x0000fe01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; One bit above zero-extended threshold.
+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
+  %res = and i32 %v, 130817 ; 0x0001ff01
+  store i32 %res, ptr %p
+  ret void
+}
+
+; 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, -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 5f28480966e10..42fa69d8e6983 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,40 @@ 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: ni 1(%r2), 1
+; 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: ni 3(%r2), 1
+; 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: ni 7(%r2), 1
+; 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 +57,51 @@ 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: ni 1(%r2), 254
+; CHECK: br %r14
+  %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: ni 3(%r2), 254
+; CHECK: br %r14
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, -2
+  store i32 %and, 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: n %r0, 0(%r2)
+; 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: ni 7(%r2), 254
+; CHECK: br %r14
+  %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 +112,51 @@ define void @f3(ptr %ptr) {
   ret void
 }
 
+; 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-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
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; 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-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
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; 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-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
+  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 +168,53 @@ 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.
+; 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-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
+  store i16 %and, ptr %ptr
+  ret void
+}
+
+; 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-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
+  store i32 %and, ptr %ptr
+  ret void
+}
+
+; 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-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
+  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 +226,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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +274,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 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +322,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 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524286
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524284
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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 524287(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 524280
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +372,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, 65407
+  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, 4294967167
+  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, -129
+  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 +426,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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +474,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 -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524289
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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 -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524291
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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 -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524295
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +524,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, -524290
+; CHECK: ni 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524290
+  %val = load i16, ptr %ptr
+  %and = and i16 %val, 65407
+  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, -524292
+; CHECK: ni 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524292
+  %val = load i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  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, -524296
+; CHECK: ni 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524296
+  %val = load i64, ptr %ptr
+  %and = and i64 %val, -129
+  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 +581,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 4095(%r2), 127
+; 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, 65407
+  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 4095(%r2), 127
+; 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, 4294967167
+  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 4095(%r2), 127
+; 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, -129
+  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 +640,263 @@ 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 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, 65407
+  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 4096(%r2), 127
+; 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, 4294967167
+  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 4096(%r2), 127
+; 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, -129
+  store i64 %and, ptr %ptr
+  ret void
+}
+
+; Check NI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: ni 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %and = and i64 %ext, 65407
+  %tr  = trunc i64 %and to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check NI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: ni 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %and = and i64 %ext, 4294967167
+  %tr  = trunc i64 %and to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check NIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: niy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %and = and i64 %ext, 65407
+  %tr  = trunc i64 %and to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check NIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: niy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %and = and i64 %ext, 4294967167
+  %tr  = trunc i64 %and to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check volatile i16 load/store with AND is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK:       llh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT:  nill [[REG]], 65407
+; CHECK-NOT:   ni
+; CHECK-NOT:   niy
+; CHECK:       br %r14
+  %val = load volatile i16, ptr %ptr
+  %and = and i16 %val, 65407
+  store volatile i16 %and, ptr %ptr
+  ret void
+}
+
+; Check volatile i32 load/store with AND is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK:       n [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   ni
+; CHECK-NOT:   niy
+; CHECK:       br %r14
+  %val = load volatile i32, ptr %ptr
+  %and = and i32 %val, 4294967167
+  store volatile i32 %and, ptr %ptr
+  ret void
+}
+
+; Check volatile i64 load/store with AND is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK:       ng [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   ni
+; CHECK-NOT:   niy
+; CHECK:       br %r14
+  %val = load volatile i64, ptr %ptr
+  %and = and i64 %val, -129
+  store volatile i64 %and, ptr %ptr
+  ret void
+}
+
+; Check folding of multi-byte 'and' operations into byte-immediate memory
+; operation 'ni'.
+; Additional tests for immAndLSB8 PatLeaf logic - preservation of upper bytes
+; 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
+}

diff  --git a/llvm/test/CodeGen/SystemZ/or-05.ll b/llvm/test/CodeGen/SystemZ/or-05.ll
index 6ec352fa7b419..c1b43adb59a91 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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %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 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 next byte up for i32, which should use OIY instead of OI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: oiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %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 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %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 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 the high end of the OIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; 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 the high end of the OIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; 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 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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %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 -524288(%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 low end of the OIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: oiy -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524291
+  %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 -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524295
+  %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, -524290
+; CHECK: oi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524290
+  %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, -524292
+; CHECK: oi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524292
+  %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, -524296
+; CHECK: oi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524296
+  %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 4095(%r2), 127
+; 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, 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 4095(%r2), 127
+; 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, 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 4095(%r2), 127
+; 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, 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,144 @@ 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 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 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 4096(%r2), 127
+; 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, 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 4096(%r2), 127
+; 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, 127
+  store i64 %or, ptr %ptr
+  ret void
+}
+
+; Check OI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: oi 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %or  = or i64 %ext, 127
+  %tr  = trunc i64 %or to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check OI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: oi 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %or  = or i64 %ext, 127
+  %tr  = trunc i64 %or to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check OIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: oiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %or  = or i64 %ext, 127
+  %tr  = trunc i64 %or to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check OIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: oiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %or  = or i64 %ext, 127
+  %tr  = trunc i64 %or to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check volatile i16 load/store with OR is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK:       lh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT:  oill [[REG]], 128
+; CHECK-NOT:   oi
+; CHECK-NOT:   oiy
+; CHECK:       br %r14
+  %val = load volatile i16, ptr %ptr
+  %or = or i16 %val, 128
+  store volatile i16 %or, ptr %ptr
+  ret void
+}
+
+; Check volatile i32 load/store with OR is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK:       o [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   oi
+; CHECK-NOT:   oiy
+; CHECK:       br %r14
+  %val = load volatile i32, ptr %ptr
+  %or = or i32 %val, 128
+  store volatile i32 %or, ptr %ptr
+  ret void
+}
+
+; Check volatile i64 load/store with OR is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK:       og [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   oi
+; CHECK-NOT:   oiy
+; CHECK:       br %r14
+  %val = load volatile i64, ptr %ptr
+  %or = or i64 %val, 128
+  store volatile i64 %or, ptr %ptr
+  ret void
+}

diff  --git a/llvm/test/CodeGen/SystemZ/xor-05.ll b/llvm/test/CodeGen/SystemZ/xor-05.ll
index 5640e236793e5..aec375368799a 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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %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 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4088
+  %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 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 next byte up for i32, which should use XIY instead of XI.
+define void @f6_i32(ptr %src) {
+; CHECK-LABEL: f6_i32:
+; CHECK: xiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %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 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4089
+  %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 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 the high end of the XIY range for i32.
+define void @f7_i32(ptr %src) {
+; CHECK-LABEL: f7_i32:
+; 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 the high end of the XIY range for i64.
+define void @f7_i64(ptr %src) {
+; CHECK-LABEL: f7_i64:
+; 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 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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -2
+  %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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -4
+  %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 -1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -8
+  %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 -524288(%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 low end of the XIY range for i32.
+define void @f10_i32(ptr %src) {
+; CHECK-LABEL: f10_i32:
+; CHECK: xiy -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524291
+  %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 -524288(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524295
+  %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, -524290
+; CHECK: xi 1(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524290
+  %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, -524292
+; CHECK: xi 3(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524292
+  %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, -524296
+; CHECK: xi 7(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 -524296
+  %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 4095(%r2), 127
+; 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, 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 4095(%r2), 127
+; 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, 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 4095(%r2), 127
+; 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, 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,144 @@ 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 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 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 4096(%r2), 127
+; 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, 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 4096(%r2), 127
+; 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, 127
+  store i64 %xor, ptr %ptr
+  ret void
+}
+
+; Check XI with a i64 intermediate value for an i16.
+define void @f14_i16(ptr %src) {
+; CHECK-LABEL: f14_i16:
+; CHECK: xi 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4094
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %xor = xor i64 %ext, 127
+  %tr  = trunc i64 %xor to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check XI with a i64 intermediate value for an i32.
+define void @f14_i32(ptr %src) {
+; CHECK-LABEL: f14_i32:
+; CHECK: xi 4095(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4092
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %xor = xor i64 %ext, 127
+  %tr  = trunc i64 %xor to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check XIY with a i64 intermediate value for an i16.
+define void @f15_i16(ptr %src) {
+; CHECK-LABEL: f15_i16:
+; CHECK: xiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4095
+  %val = load i16, ptr %ptr
+  %ext = zext i16 %val to i64
+  %xor = xor i64 %ext, 127
+  %tr  = trunc i64 %xor to i16
+  store i16 %tr, ptr %ptr
+  ret void
+}
+
+; Check XIY with a i64 intermediate value for an i32.
+define void @f15_i32(ptr %src) {
+; CHECK-LABEL: f15_i32:
+; CHECK: xiy 4096(%r2), 127
+; CHECK: br %r14
+  %ptr = getelementptr i8, ptr %src, i64 4093
+  %val = load i32, ptr %ptr
+  %ext = zext i32 %val to i64
+  %xor = xor i64 %ext, 127
+  %tr  = trunc i64 %xor to i32
+  store i32 %tr, ptr %ptr
+  ret void
+}
+
+; Check volatile i16 load/store with XOR is not folded.
+define void @f16_i16(ptr %ptr) {
+; CHECK-LABEL: f16_i16:
+; CHECK:       lh [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NEXT:  xilf [[REG]], 128
+; CHECK-NOT:   xi
+; CHECK-NOT:   xiy
+; CHECK:       br %r14
+  %val = load volatile i16, ptr %ptr
+  %xor = xor i16 %val, 128
+  store volatile i16 %xor, ptr %ptr
+  ret void
+}
+
+; Check volatile i32 load/store with XOR is not folded.
+define void @f16_i32(ptr %ptr) {
+; CHECK-LABEL: f16_i32:
+; CHECK:       x [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   xi
+; CHECK-NOT:   xiy
+; CHECK:       br %r14
+  %val = load volatile i32, ptr %ptr
+  %xor = xor i32 %val, 128
+  store volatile i32 %xor, ptr %ptr
+  ret void
+}
+
+; Check volatile i64 load/store with XOR is not folded.
+define void @f16_i64(ptr %ptr) {
+; CHECK-LABEL: f16_i64:
+; CHECK:       xg [[REG:%r[0-5]]], 0(%r2)
+; CHECK-NOT:   xi
+; CHECK-NOT:   xiy
+; CHECK:       br %r14
+  %val = load volatile i64, ptr %ptr
+  %xor = xor i64 %val, 128
+  store volatile i64 %xor, ptr %ptr
+  ret void
+}


        


More information about the llvm-commits mailing list