Skip to content

Conversation

@milos1397
Copy link
Contributor

Fixes #173180

The crash occurs when a vector constant refines its value during iterative analysis.
In SCCPInstVisitor::visitCastInst, the logic for folding constants through a CastInst uses markConstant. This function is strictly designed for initial assignments and contains an assertion that prevents a lattice element from being updated with a different constant pointer.

During the analysis of loops or complex data flows, a vector constant may "refine." For example:

First Pass: SCCP identifies a value as <4 x i64> {poison, poison, poison, 0}.

Second Pass: The value refines to <4 x i64> zeroinitializer.

Because these are distinct Constant* objects, markConstant triggers a "Marking constant with different value" assertion failure.

The call to markConstant is replaced with mergeInValue. Unlike the former, mergeInValue is lattice-aware, it allows for valid refinement (moving from a less-defined to a more-defined state) and gracefully handles transitions to Overdefined if a true conflict occurs. This brings BitCast handling in line with how Trunc and Ext instructions are already safely handled in the same pass.

@llvmbot
Copy link
Member

llvmbot commented Dec 21, 2025

@llvm/pr-subscribers-function-specialization

@llvm/pr-subscribers-llvm-transforms

Author: Miloš Poletanović (milos1397)

Changes

Fixes #173180

The crash occurs when a vector constant refines its value during iterative analysis.
In SCCPInstVisitor::visitCastInst, the logic for folding constants through a CastInst uses markConstant. This function is strictly designed for initial assignments and contains an assertion that prevents a lattice element from being updated with a different constant pointer.

During the analysis of loops or complex data flows, a vector constant may "refine." For example:

First Pass: SCCP identifies a value as &lt;4 x i64&gt; {poison, poison, poison, 0}.

Second Pass: The value refines to &lt;4 x i64&gt; zeroinitializer.

Because these are distinct Constant* objects, markConstant triggers a "Marking constant with different value" assertion failure.

The call to markConstant is replaced with mergeInValue. Unlike the former, mergeInValue is lattice-aware, it allows for valid refinement (moving from a less-defined to a more-defined state) and gracefully handles transitions to Overdefined if a true conflict occurs. This brings BitCast handling in line with how Trunc and Ext instructions are already safely handled in the same pass.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SCCPSolver.cpp (+5-2)
  • (added) llvm/test/Transforms/SCCP/bitcast-vector-refinement.l.ll (+29)
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 021bf0618754a..90ee55b87439a 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -1498,8 +1498,11 @@ void SCCPInstVisitor::visitCastInst(CastInst &I) {
   if (Constant *OpC = getConstant(OpSt, I.getOperand(0)->getType())) {
     // Fold the constant as we build.
     if (Constant *C =
-            ConstantFoldCastOperand(I.getOpcode(), OpC, I.getType(), DL))
-      return (void)markConstant(&I, C);
+            ConstantFoldCastOperand(I.getOpcode(), OpC, I.getType(), DL)) {
+      auto &LV = ValueState[&I];
+      mergeInValue(LV, &I, ValueLatticeElement::get(C));
+      return;
+    }
   }
 
   // Ignore bitcasts, as they may change the number of vector elements.
diff --git a/llvm/test/Transforms/SCCP/bitcast-vector-refinement.l.ll b/llvm/test/Transforms/SCCP/bitcast-vector-refinement.l.ll
new file mode 100644
index 0000000000000..94df835e317b7
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/bitcast-vector-refinement.l.ll
@@ -0,0 +1,29 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=sccp -S < %s | FileCheck %s
+
+define <32 x i8> @test(i1 %cond) {
+; CHECK-LABEL: define <32 x i8> @test(
+; CHECK-SAME: i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[FOR_COND2:.*]]
+; CHECK:       [[FOR_COND2]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = phi <4 x i64> [ zeroinitializer, %[[ENTRY]] ], [ splat (i64 1), %[[FOR_COND2]] ]
+; CHECK-NEXT:    br i1 [[COND]], label %[[FOR_COND2]], label %[[IF_THEN:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <4 x i64> zeroinitializer to <32 x i8>
+; CHECK-NEXT:    ret <32 x i8> [[TMP1]]
+;
+entry:
+  br label %for.cond2
+
+for.cond2:                                        ; preds = %for.cond2, %entry
+  %BS_VAR_1.0 = phi <4 x i64> [ <i64 poison, i64 poison, i64 poison, i64 0>, %entry ], [ zeroinitializer, %for.cond2 ]
+  %0 = phi <4 x i64> [ zeroinitializer, %entry ], [ splat (i64 1), %for.cond2 ]
+  br i1 %cond, label %for.cond2, label %if.then
+
+if.then:                                          ; preds = %for.cond2
+  %sub = sub <4 x i64> <i64 poison, i64 poison, i64 poison, i64 0>, %BS_VAR_1.0
+  %and = and <4 x i64> %0, %sub
+  %1 = bitcast <4 x i64> %and to <32 x i8>
+  ret <32 x i8> %1
+}

@XChy XChy requested review from dtcxzyw and nikic December 21, 2025 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SCCP] Assertion `getConstant() == V && "Marking constant with different value"' failed.

2 participants