[llvm] e80f132 - [SeparateConstOffsetFromGEP] Decompose xor constant operand when possible (#195830)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 4 23:59:34 PDT 2026


Author: Antonio Frighetto
Date: 2026-06-05T08:59:29+02:00
New Revision: e80f1329ce2ba3fb775e6ca0f9b69f4ab3f3866a

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

LOG: [SeparateConstOffsetFromGEP] Decompose xor constant operand when possible (#195830)

It may be desirable to fold constants directly into the addressing mode
when computing an address. While lowering GEPs and looking for a
constant to extract among the indexes, take into account constants which
are xor expressions as well. When some bits of the constant operand of
the xor are known-zero in the base operand, then, for those specific
bits (disjoint bits), xor and additions behave alike. Such bits may be
extracted from the xor, and are those that can contribute to the final
GEP offset.

Proofs: https://alive2.llvm.org/ce/z/JtmXsu.

Co-authored-by: Sumanth Gundapaneni <sumanth.gundapaneni at amd.com>

Added: 
    llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/xor-decompose.ll
    llvm/test/Transforms/SeparateConstOffsetFromGEP/xor-decompose.ll

Modified: 
    llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
index 7bd8902266a64..4870b8c888279 100644
--- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
+++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
@@ -161,6 +161,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/KnownBits.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Utils/Local.h"
@@ -220,7 +221,7 @@ class ConstantOffsetExtractor {
 
 private:
   ConstantOffsetExtractor(BasicBlock::iterator InsertionPt)
-      : IP(InsertionPt), DL(InsertionPt->getDataLayout()) {}
+      : IP(InsertionPt), DL(InsertionPt->getDataLayout()), SQ(DL) {}
 
   /// Searches the expression that computes V for a non-zero constant C s.t.
   /// V can be reassociated into the form V' + C. If the searching is
@@ -297,6 +298,33 @@ class ConstantOffsetExtractor {
   bool canTraceInto(bool SignExtended, bool ZeroExtended, BinaryOperator *BO,
                     GetElementPtrInst *GEP, Value *Idx);
 
+  /// Analyze a xor expression, and identify the bits in the constant operand
+  /// that are disjoint from the base operand's known set bits. For these
+  /// disjoint bits, a xor is equivalent to an addition, which allows us to
+  /// extract them as constant offsets that can be folded into the immediate
+  /// field of addressing operations. The transformation is the following one:
+  ///
+  ///   Base ^ Const  becomes  (Base ^ NonDisjointBits) + DisjointBits
+  ///
+  /// where DisjointBits = Const & KnownZeros(Base) and
+  ///       NonDisjointBits = Const & ~DisjointBits.
+  ///
+  /// Example with ptr having known-zero low bit:
+  ///   Original: `xor %ptr, 3`    ; 3 = 0b11
+  ///   Analysis: DisjointBits = 3 & KnownZeros(%ptr) = 0b11 & 0b01 = 0b01
+  ///   Result:   `(xor %ptr, 2) + 1` where 1 can be folded into address mode
+  ///
+  /// \param XorInst The XOR binary operator to analyze
+  /// \return Returns the disjoint bits (the extractable offset), or zero if
+  /// none exist. On success, stores NonDisjointBits in
+  /// NonDisjointXorConstantBits.
+  APInt extractDisjointBitsFromXor(BinaryOperator *XorInst);
+
+  /// The non-disjoint bits remaining after xor decomposition in
+  /// `extractDisjointBitsFromXor`, which are later used while replacing the
+  /// original xor constant operand.
+  ConstantInt *NonDisjointXorConstantBits = nullptr;
+
   /// The path from the constant offset to the old GEP index. e.g., if the GEP
   /// index is "a * b + (c + 5)". After running function find, UserChain[0] will
   /// be the constant 5, UserChain[1] will be the subexpression "c + 5", and
@@ -313,6 +341,7 @@ class ConstantOffsetExtractor {
   BasicBlock::iterator IP;
 
   const DataLayout &DL;
+  const SimplifyQuery SQ;
 };
 
 /// A pass that tries to split every GEP in the function into a variadic
@@ -709,6 +738,8 @@ APInt ConstantOffsetExtractor::find(Value *V, GetElementPtrInst *GEP,
     // Trace into subexpressions for more hoisting opportunities.
     if (canTraceInto(SignExtended, ZeroExtended, BO, GEP, Idx))
       ConstantOffset = findInEitherOperand(BO, SignExtended, ZeroExtended);
+    else if (BO->getOpcode() == Instruction::Xor)
+      ConstantOffset = extractDisjointBitsFromXor(BO);
   } else if (isa<TruncInst>(V)) {
     ConstantOffset =
         find(U->getOperand(0), GEP, Idx, SignExtended, ZeroExtended)
@@ -827,6 +858,19 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
   Value *NextInChain = removeConstOffset(ChainIndex - 1);
   Value *TheOther = BO->getOperand(1 - OpNo);
 
+  // When rewriting xor(TheOther, NextInChain) expressions, the original
+  // constant operand is replaced with the non-disjoints bits, which are the
+  // non-extractable bits, i.e., those that must remain in the xor (the other
+  // bits have already compounded the GEP offset).
+  if (BO->getOpcode() == Instruction::Xor) {
+    // The non-disjoint bits are cached in NonDisjointXorConstantBits, which is
+    // always up-to-date.
+    assert(NonDisjointXorConstantBits &&
+           "XOR in UserChain without recorded non-disjoint bits");
+    // Only casts can happen to be distributed among the xor operands.
+    NextInChain = applyCasts(NonDisjointXorConstantBits);
+  }
+
   // If NextInChain is 0 and not the LHS of a sub, we can simplify the
   // sub-expression to be just TheOther.
   if (ConstantInt *CI = dyn_cast<ConstantInt>(NextInChain)) {
@@ -862,6 +906,49 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
   return NewBO;
 }
 
+APInt ConstantOffsetExtractor::extractDisjointBitsFromXor(
+    BinaryOperator *XorInst) {
+  assert(XorInst && XorInst->getOpcode() == Instruction::Xor &&
+         "Expected XOR instruction");
+
+  unsigned BitWidth = XorInst->getType()->getScalarSizeInBits();
+  Value *BaseOp;
+  ConstantInt *XorConstantOp;
+
+  if (!match(XorInst, m_Xor(m_Value(BaseOp), m_ConstantInt(XorConstantOp))))
+    return APInt::getZero(BitWidth);
+
+  const KnownBits BaseKnownBits = computeKnownBits(BaseOp, SQ);
+  const APInt &ConstantValue = XorConstantOp->getValue();
+
+  // Compute the disjoint bits, i.e., those bits of the constant operand that
+  // are known-zero in the base. These disjoint bits will contribute to the
+  // final GEP offset. If there are no disjoint bits, there isn't any offset to
+  // extract from the xor.
+  const APInt DisjointBits = ConstantValue & BaseKnownBits.Zero;
+  if (DisjointBits.isZero())
+    return DisjointBits;
+
+  // Avoid a pessimizing rewrite if the disjoint bits include the sign bit.
+  if (DisjointBits.isSignBitSet())
+    return APInt::getZero(BitWidth);
+
+  // Compute the remaining bits, i.e., the non-disjoint ones, which are those
+  // that must be preserved in the xor.
+  const APInt NonDisjointBits = ConstantValue & ~DisjointBits;
+  NonDisjointXorConstantBits =
+      ConstantInt::get(XorInst->getContext(), NonDisjointBits);
+
+  // UserChain maintains a path from the constant up to the GEP index. Push the
+  // xor constant operand, which is the constant leaf of the chain (which is
+  // also what `distributeCastsAndCloneChain` expects). Such a chained operand
+  // is the one to be replaced with the non-disjoint bits, while rebuilding the
+  // xor afterwards. The xor instruction itself is pushed upon returning.
+  UserChain.push_back(XorConstantOp);
+
+  return DisjointBits;
+}
+
 /// A helper function to check if reassociating through an entry in the user
 /// chain would invalidate the GEP's nuw flag.
 static bool allowsPreservingNUW(const User *U) {

diff  --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/xor-decompose.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/xor-decompose.ll
new file mode 100644
index 0000000000000..e8d09c518e6b9
--- /dev/null
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/xor-decompose.ll
@@ -0,0 +1,420 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; Test XOR disjoint bits decomposition into GEP constant offsets.
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=separate-const-offset-from-gep \
+; RUN: -S < %s | FileCheck %s
+; Test GVN eliminates the redundant xor instructions from decomposition.
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=separate-const-offset-from-gep,gvn \
+; RUN: -S < %s | FileCheck --check-prefix=GVN %s
+
+; Check that disjoint constants are properly extracted and folded into GEP
+; addressing modes and GVN to eliminate redundant computations.
+define amdgpu_kernel void @test1(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test1(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[XOR11:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR11]]
+; CHECK-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; CHECK-NEXT:    [[XOR23:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR23]]
+; CHECK-NEXT:    [[GEP24:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP1]], i32 16384
+; CHECK-NEXT:    [[XOR35:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR35]]
+; CHECK-NEXT:    [[GEP36:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP2]], i32 24576
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; CHECK-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP24]], align 16
+; CHECK-NEXT:    [[V3:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP36]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[V3]]
+; CHECK-NEXT:    [[ADD2:%.*]] = fadd <8 x half> [[ADD0]], [[ADD1]]
+; CHECK-NEXT:    store <8 x half> [[ADD2]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test1(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[GEP0]], i32 8192
+; GVN-NEXT:    [[GEP24:%.*]] = getelementptr i8, ptr addrspace(3) [[GEP0]], i32 16384
+; GVN-NEXT:    [[GEP36:%.*]] = getelementptr i8, ptr addrspace(3) [[GEP0]], i32 24576
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; GVN-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP24]], align 16
+; GVN-NEXT:    [[V3:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP36]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[V3]]
+; GVN-NEXT:    [[ADD2:%.*]] = fadd <8 x half> [[ADD0]], [[ADD1]]
+; GVN-NEXT:    store <8 x half> [[ADD2]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %xor1 = xor i32 %sel, 4128
+  %xor2 = xor i32 %sel, 8224
+  %xor3 = xor i32 %sel, 12320
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %gep2 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor2
+  %gep3 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor3
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %v2 = load <8 x half>, ptr addrspace(3) %gep2, align 16
+  %v3 = load <8 x half>, ptr addrspace(3) %gep3, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  %add1 = fadd <8 x half> %v2, %v3
+  %add2 = fadd <8 x half> %add0, %add1
+  store <8 x half> %add2, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Check that disjoint constants are properly extracted and folded into GEP
+; addressing modes and GVN to eliminate redundant computation (reverse order).
+define amdgpu_kernel void @test2(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test2(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR3:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[XOR01:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR01]]
+; CHECK-NEXT:    [[GEP02:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 24576
+; CHECK-NEXT:    [[XOR13:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR13]]
+; CHECK-NEXT:    [[GEP14:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP1]], i32 16384
+; CHECK-NEXT:    [[XOR25:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR25]]
+; CHECK-NEXT:    [[GEP26:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP2]], i32 8192
+; CHECK-NEXT:    [[GEP3:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR3]]
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP02]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP14]], align 16
+; CHECK-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP26]], align 16
+; CHECK-NEXT:    [[V3:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP3]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[V3]]
+; CHECK-NEXT:    [[ADD2:%.*]] = fadd <8 x half> [[ADD0]], [[ADD1]]
+; CHECK-NEXT:    store <8 x half> [[ADD2]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test2(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR3:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR3]]
+; GVN-NEXT:    [[GEP02:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 24576
+; GVN-NEXT:    [[GEP14:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 16384
+; GVN-NEXT:    [[GEP26:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP02]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP14]], align 16
+; GVN-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP26]], align 16
+; GVN-NEXT:    [[V3:%.*]] = load <8 x half>, ptr addrspace(3) [[TMP0]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[V3]]
+; GVN-NEXT:    [[ADD2:%.*]] = fadd <8 x half> [[ADD0]], [[ADD1]]
+; GVN-NEXT:    store <8 x half> [[ADD2]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 12320
+  %xor1 = xor i32 %sel, 8224
+  %xor2 = xor i32 %sel, 4128
+  %xor3 = xor i32 %sel, 32
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %gep2 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor2
+  %gep3 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor3
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %v2 = load <8 x half>, ptr addrspace(3) %gep2, align 16
+  %v3 = load <8 x half>, ptr addrspace(3) %gep3, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  %add1 = fadd <8 x half> %v2, %v3
+  %add2 = fadd <8 x half> %add0, %add1
+  store <8 x half> %add2, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Verify that xor instructions with 
diff erent non-disjoint constants are optimized.
+define amdgpu_kernel void @test3(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test3(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[XOR11:%.*]] = xor i32 [[SEL]], 288
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR11]]
+; CHECK-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 4096
+; CHECK-NEXT:    [[XOR23:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR23]]
+; CHECK-NEXT:    [[GEP24:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP1]], i32 8192
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; CHECK-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP24]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[ADD0]]
+; CHECK-NEXT:    store <8 x half> [[ADD1]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test3(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[XOR11:%.*]] = xor i32 [[SEL]], 288
+; GVN-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR11]]
+; GVN-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 4096
+; GVN-NEXT:    [[GEP24:%.*]] = getelementptr i8, ptr addrspace(3) [[GEP0]], i32 8192
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; GVN-NEXT:    [[V2:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP24]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    [[ADD1:%.*]] = fadd <8 x half> [[V2]], [[ADD0]]
+; GVN-NEXT:    store <8 x half> [[ADD1]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %xor1 = xor i32 %sel, 2336
+  %xor2 = xor i32 %sel, 4128
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %gep2 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor2
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %v2 = load <8 x half>, ptr addrspace(3) %gep2, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  %add1 = fadd <8 x half> %v2, %add0
+  store <8 x half> %add1, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Verify that no optimization occurs when disjoint constants are absent.
+define amdgpu_kernel void @test4(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test4(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i32 [[SEL]], 288
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR1]]
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP1]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test4(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[XOR1:%.*]] = xor i32 [[SEL]], 288
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[GEP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR1]]
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP1]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %xor1 = xor i32 %sel, 288
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  store <8 x half> %add0, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Verify that XOR-BinOp-GEP usage chains are properly optimized.
+define amdgpu_kernel void @test5(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test5(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[XOR11:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[IDX2:%.*]] = add i32 [[XOR11]], 256
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[IDX2]]
+; CHECK-NEXT:    [[GEP13:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP13]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test5(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[IDX2:%.*]] = add i32 [[XOR0]], 256
+; GVN-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[IDX2]]
+; GVN-NEXT:    [[GEP13:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP13]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %xor1 = xor i32 %sel, 4128
+  %idx = add i32 %xor1, 256
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %idx
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  store <8 x half> %add0, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Verify that BinOp-XOR-GEP usage chains are properly optimized.
+; In the below test, make sure we stop processing the chain at xor
+; and not fold the constant from add instruction in to gep. The
+; constant from add can be folded and the future work will cover
+; these cases.
+define amdgpu_kernel void @test6(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test6(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[BASE:%.*]] = add i32 [[SEL]], 256
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[XOR11:%.*]] = xor i32 [[BASE]], 32
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR11]]
+; CHECK-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test6(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[BASE:%.*]] = add i32 [[SEL]], 256
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[XOR11:%.*]] = xor i32 [[BASE]], 32
+; GVN-NEXT:    [[TMP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR11]]
+; GVN-NEXT:    [[GEP12:%.*]] = getelementptr i8, ptr addrspace(3) [[TMP0]], i32 8192
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP12]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %base = add i32 %sel, 256
+  %xor1 = xor i32 %base, 4128
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  store <8 x half> %add0, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Verify that BinOp-XOR-GEP usage chains with non disjoint xor works as
+; intended.
+define amdgpu_kernel void @test6a(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test6a(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; CHECK-NEXT:    [[BASE:%.*]] = add i32 [[SEL]], 256
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i32 [[BASE]], 288
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR1]]
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP1]], align 16
+; CHECK-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; CHECK-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test6a(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32
+; GVN-NEXT:    [[BASE:%.*]] = add i32 [[SEL]], 256
+; GVN-NEXT:    [[XOR1:%.*]] = xor i32 [[BASE]], 288
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[GEP1:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR1]]
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    [[V1:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP1]], align 16
+; GVN-NEXT:    [[ADD0:%.*]] = fadd <8 x half> [[V0]], [[V1]]
+; GVN-NEXT:    store <8 x half> [[ADD0]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32
+  %base = add i32 %sel, 256
+  %xor1 = xor i32 %base, 288
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor1
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  %v1 = load <8 x half>, ptr addrspace(3) %gep1, align 16
+  %add0 = fadd <8 x half> %v0, %v1
+  store <8 x half> %add0, ptr addrspace(3) %ptr, align 16
+  ret void
+}
+
+; Ensure disjoint constants exceeding addressing mode limits are not extracted.
+define amdgpu_kernel void @test7(i1 %cond, ptr addrspace(3) %ptr) {
+; CHECK-LABEL: define amdgpu_kernel void @test7(
+; CHECK-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32800
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; CHECK-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; CHECK-NEXT:    store <8 x half> [[V0]], ptr addrspace(3) [[PTR]], align 16
+; CHECK-NEXT:    ret void
+;
+; GVN-LABEL: define amdgpu_kernel void @test7(
+; GVN-SAME: i1 [[COND:%.*]], ptr addrspace(3) [[PTR:%.*]]) {
+; GVN-NEXT:  [[ENTRY:.*:]]
+; GVN-NEXT:    [[SEL:%.*]] = select i1 [[COND]], i32 0, i32 288
+; GVN-NEXT:    [[XOR0:%.*]] = xor i32 [[SEL]], 32800
+; GVN-NEXT:    [[GEP0:%.*]] = getelementptr half, ptr addrspace(3) [[PTR]], i32 [[XOR0]]
+; GVN-NEXT:    [[V0:%.*]] = load <8 x half>, ptr addrspace(3) [[GEP0]], align 16
+; GVN-NEXT:    store <8 x half> [[V0]], ptr addrspace(3) [[PTR]], align 16
+; GVN-NEXT:    ret void
+;
+entry:
+  %sel = select i1 %cond, i32 0, i32 288
+  %xor0 = xor i32 %sel, 32800
+  %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %xor0
+  %v0 = load <8 x half>, ptr addrspace(3) %gep0, align 16
+  store <8 x half> %v0, ptr addrspace(3) %ptr, align 16
+  ret void
+}

diff  --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/xor-decompose.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/xor-decompose.ll
new file mode 100644
index 0000000000000..8ae5cbaf7a9c7
--- /dev/null
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/xor-decompose.ll
@@ -0,0 +1,163 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S  -passes='separate-const-offset-from-gep<lower-gep>' %s | FileCheck %s
+
+define ptr @xor_decompose_constant_base_op(i32 %a) {
+; CHECK-LABEL: define ptr @xor_decompose_constant_base_op(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:    [[BASE:%.*]] = alloca [4 x i32], align 16
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[BASE]], i64 12
+; CHECK-NEXT:    ret ptr [[UGLYGEP]]
+;
+  %base = alloca [4 x i32], align 16
+  %xor = xor i64 0, 3
+  %gep = getelementptr [4 x i32], ptr %base, i64 0, i64 %xor
+  ret ptr %gep
+}
+
+define ptr @xor_decompose_constant_base_op_2(i32 %a) {
+; CHECK-LABEL: define ptr @xor_decompose_constant_base_op_2(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:    [[BASE:%.*]] = alloca [4 x i32], align 16
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i64 2, 2
+; CHECK-NEXT:    [[TMP1:%.*]] = shl i64 [[XOR1]], 2
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[TMP1]]
+; CHECK-NEXT:    [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 4
+; CHECK-NEXT:    ret ptr [[UGLYGEP2]]
+;
+  %base = alloca [4 x i32], align 16
+  %xor = xor i64 2, 3
+  %gep = getelementptr [4 x i32], ptr %base, i64 0, i64 %xor
+  ret ptr %gep
+}
+
+define ptr @xor_decompose_constant_sign_bit(ptr %p, i32 %a) {
+; CHECK-LABEL: define ptr @xor_decompose_constant_sign_bit(
+; CHECK-SAME: ptr [[P:%.*]], i32 [[A:%.*]]) {
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[A]], -1
+; CHECK-NEXT:    [[IDXPROM:%.*]] = sext i32 [[XOR]] to i64
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDXPROM]]
+; CHECK-NEXT:    ret ptr [[GEP]]
+;
+  %xor = xor i32 %a, -1
+  %gep = getelementptr inbounds i32, ptr %p, i32 %xor
+  ret ptr %gep
+}
+
+define ptr @xor_decompose_casts_chain(i64 %x, ptr %p) {
+; CHECK-LABEL: define ptr @xor_decompose_casts_chain(
+; CHECK-SAME: i64 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[Y:%.*]] = and i64 [[X]], 8589934592
+; CHECK-NEXT:    [[XOR:%.*]] = xor i64 [[Y]], 12884901888
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[XOR]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[TRUNC]] to i64
+; CHECK-NEXT:    [[TMP2:%.*]] = shl i64 [[TMP1]], 2
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP2]]
+; CHECK-NEXT:    [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 28
+; CHECK-NEXT:    ret ptr [[UGLYGEP2]]
+;
+  %y = and i64 %x, 8589934592           ; bit 33 unknown, all others zero
+  %xor = xor i64 %y, 12884901888        ; xor w/ 0x300000000: bit 32 (disjoint) | bit 33 (non-disjoint).
+  %trunc = trunc i64 %xor to i32        ; Push 0.
+  %expr = add nuw i32 %trunc, 7
+  %idx = zext i32 %expr to i64
+  %gep = getelementptr i32, ptr %p, i64 %idx
+  ret ptr %gep
+}
+
+define ptr @xor_decompose_casts_chain_2(i32 %x, i32 %y, ptr %p) {
+; CHECK-LABEL: define ptr @xor_decompose_casts_chain_2(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[MASK:%.*]] = and i32 [[X]], 31
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i32 [[MASK]] to i64
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i64 [[TMP1]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = shl i64 [[XOR1]], 2
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP2]]
+; CHECK-NEXT:    [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 128
+; CHECK-NEXT:    ret ptr [[UGLYGEP2]]
+;
+  %mask = and i32 %x, 31
+  %xor = xor i32 %mask, 33
+  %idx = sext i32 %xor to i64
+  %gep = getelementptr i32, ptr %p, i64 %idx
+  ret ptr %gep
+}
+
+define <4 x ptr> @xor_decompose_gep_vector(<4 x ptr> %p, i32 %x) {
+; CHECK-LABEL: define <4 x ptr> @xor_decompose_gep_vector(
+; CHECK-SAME: <4 x ptr> [[P:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[XOR:%.*]] = xor i32 [[X]], 33
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i32, <4 x ptr> [[P]], i32 [[XOR]]
+; CHECK-NEXT:    ret <4 x ptr> [[GEP]]
+;
+  %xor = xor i32 %x, 33
+  %gep = getelementptr i32, <4 x ptr> %p, i32 %xor
+  ret <4 x ptr> %gep
+}
+
+define ptr @xor_decompose_overwrite_nondisjoint(ptr %p, i1 %c, i64 %x) {
+; CHECK-LABEL: define ptr @xor_decompose_overwrite_nondisjoint(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]], i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[Y:%.*]] = and i64 [[X]], 8589934592
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i64 [[Y]], 12884901888
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i64 [[XOR1]] to i32
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i32 0, i32 1
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i32 [[TRUNC]] to i64
+; CHECK-NEXT:    [[TMP2:%.*]] = sext i32 [[SEL]] to i64
+; CHECK-NEXT:    [[ADD2:%.*]] = add i64 [[TMP1]], [[TMP2]]
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[ADD2]]
+; CHECK-NEXT:    [[UGLYGEP3:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 1024
+; CHECK-NEXT:    ret ptr [[UGLYGEP3]]
+;
+  %y = and i64 %x, 8589934592
+  %xor1 = xor i64 %y, 12884901888
+  %trunc = trunc i64 %xor1 to i32
+  %sel = select i1 %c, i32 0, i32 1
+  %xor2 = xor i32 %sel, 1024
+  %add = add i32 %trunc, %xor2
+  %gep = getelementptr i8, ptr %p, i32 %add
+  ret ptr %gep
+}
+
+define ptr @xor_decompose_both_ops_xor(ptr %p, i1 %c) {
+; CHECK-LABEL: define ptr @xor_decompose_both_ops_xor(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], i32 0, i32 288
+; CHECK-NEXT:    [[XOR2:%.*]] = xor i32 [[SEL]], 8224
+; CHECK-NEXT:    [[TMP0:%.*]] = sext i32 [[XOR2]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i32 [[SEL]] to i64
+; CHECK-NEXT:    [[XOR11:%.*]] = xor i64 [[TMP1]], 32
+; CHECK-NEXT:    [[EXPR2:%.*]] = add i64 [[XOR11]], [[TMP0]]
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[EXPR2]]
+; CHECK-NEXT:    [[UGLYGEP3:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 4096
+; CHECK-NEXT:    ret ptr [[UGLYGEP3]]
+;
+entry:
+  %sel = select i1 %c, i32 0, i32 288
+  %xor1 = xor i32 %sel, 4128
+  %xor2 = xor i32 %sel, 8224
+  %expr = add i32 %xor1, %xor2
+  %gep = getelementptr i8, ptr %p, i32 %expr
+  ret ptr %gep
+}
+
+define void @xor_decompose_base_zext(ptr %p, i16 %x) {
+; CHECK-LABEL: define void @xor_decompose_base_zext(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[AND:%.*]] = and i16 [[X]], -16
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext i16 [[AND]] to i32
+; CHECK-NEXT:    [[TMP0:%.*]] = sext i32 [[ZEXT]] to i64
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[TMP0]]
+; CHECK-NEXT:    [[UGLYGEP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 65541
+; CHECK-NEXT:    store i32 0, ptr [[UGLYGEP2]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  %and = and i16 %x, -16
+  %zext = zext i16 %and to i32
+  %xor = xor i32 %zext, 65541
+  %gep = getelementptr i8, ptr %p, i32 %xor
+  store i32 0, ptr %gep
+  ret void
+}


        


More information about the llvm-commits mailing list