[llvm] [SeparateConstOffsetFromGEP] - Restrict simplification of XOR index when its base operand is constant. (PR #175724)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 13 08:26:35 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Pawan Nirpal (pawan-nirpal-031)

<details>
<summary>Changes</summary>

Simplifying GEP indices involving XOR with a constant base can lead to incorrect offset separation. This change ensures such cases are excluded from the transformation. 

---
Full diff: https://github.com/llvm/llvm-project/pull/175724.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp (+9-1) 
- (added) llvm/test/Transforms/SeparateConstOffsetFromGEP/fix-const-xor-gep-simplify.ll (+34) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
index dc47b243625b8..10cc056eec0cc 100644
--- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
+++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp
@@ -729,8 +729,16 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
       // with non-disjoint constant bits.
       // TODO: The design should be updated to support partial constant
       // extraction.
-      if (BO->getOpcode() == Instruction::Xor)
+      if (BO->getOpcode() == Instruction::Xor) {
+        if (auto *ConstIOther = dyn_cast<ConstantInt>(TheOther)) {
+          const APInt &DisjointBits = extractDisjointBitsFromXor(BO);
+          const APInt &ConstantValue = ConstIOther->getValue();
+          const APInt &NonDisjointBits = ConstantValue & (~DisjointBits);
+          BO->setOperand(1 - OpNo,
+                         ConstantInt::get(BO->getType(), NonDisjointBits));
+        }
         return BO;
+      }
 
       // If NextInChain is 0 and not the LHS of a sub, we can simplify the
       // sub-expression to be just TheOther.
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/fix-const-xor-gep-simplify.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/fix-const-xor-gep-simplify.ll
new file mode 100644
index 0000000000000..049463b33ac71
--- /dev/null
+++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/fix-const-xor-gep-simplify.ll
@@ -0,0 +1,34 @@
+; 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-const-test(i32 %0) {
+; CHECK-LABEL: define ptr @xor-const-test(
+; CHECK-SAME: i32 [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[BASE:%.*]] = alloca [4 x i32], align 16
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i64 0, 0
+; CHECK-NEXT:    [[TMP3:%.*]] = shl i64 [[XOR1]], 2
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[TMP3]]
+; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr i8, ptr [[UGLYGEP]], i64 12
+; CHECK-NEXT:    ret ptr [[TMP2]]
+;
+  %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-const-test2(i32 %0) {
+; CHECK-LABEL: define ptr @xor-const-test2(
+; CHECK-SAME: i32 [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[BASE:%.*]] = alloca [4 x i32], align 16
+; CHECK-NEXT:    [[XOR1:%.*]] = xor i64 2, 2
+; CHECK-NEXT:    [[TMP2:%.*]] = shl i64 [[XOR1]], 2
+; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[TMP2]]
+; 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
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/175724


More information about the llvm-commits mailing list