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