Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ConstraintElimination] A miscompile in presence of shl nuw nsw and llvm.umin #78621

Closed
DaniilSuchkov opened this issue Jan 18, 2024 · 15 comments · May be fixed by #80121
Closed

[ConstraintElimination] A miscompile in presence of shl nuw nsw and llvm.umin #78621

DaniilSuchkov opened this issue Jan 18, 2024 · 15 comments · May be fixed by #80121

Comments

@DaniilSuchkov
Copy link
Contributor

DaniilSuchkov commented Jan 18, 2024

The following commit: 71f56e4
Triggered this miscompile: https://alive2.llvm.org/ce/z/oBg-u8

Now opt -passes=constraint-elimination -S turns

define i1 @test(i32 %arg) {
  %icmp = icmp slt i32 %arg, 0
  %shl = shl nuw nsw i32 %arg, 3
  %call4 = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  ret i1 %icmp
}

declare i32 @llvm.umin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

into

define i1 @test(i32 %arg) {
  %shl = shl nuw nsw i32 %arg, 3
  %call4 = call i32 @llvm.umin.i32(i32 %shl, i32 80)
  ret i1 false
}

declare i32 @llvm.umin.i32(i32, i32) #0

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

Given this debug output:

opt -passes=constraint-elimination before-constr-elim-renamed.ll -S -debug-only=constraint-elimination,constraint-system
Processing fact to add to the system: icmp ule i32 %call4, %shl
Adding 'icmp ule i32 %call4, %shl'
  constraint: -8 * %arg + %call4 <= 0

---
-1 * %arg <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
8 * %arg <= -1
unsat
Adding 'icmp sge i32 %call4, 0'
  constraint: -1 * %call4 <= 0

Adding 'icmp sle i32 %call4, %shl'
  constraint: -8 * %arg + %call4 <= 0

Processing fact to add to the system: icmp ule i32 %call4, 80
Adding 'icmp ule i32 %call4, 80'
  constraint: %call4 <= 80

Adding 'icmp sge i32 %call4, 0'
  constraint: -1 * %call4 <= 0

Adding 'icmp sle i32 %call4, 80'
  constraint: %call4 <= 80

Top of stack : 0 1
CB: 0 1
Processing condition to simplify:   %icmp = icmp slt i32 %arg, 0
Checking   %icmp = icmp slt i32 %arg, 0
---
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80
-1 * %arg <= 0
sat
---
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80
%arg <= -1
unsat
Condition icmp sge i32 %arg, 0 implied by dominating constraints
-1 * %call4 <= 0
-8 * %arg + %call4 <= 0
-1 * %call4 <= 0
%call4 <= 80

I'm not 100% sure if the aforementioned patch causes the miscompile, of if it merely triggers it on this specific reproducer. The fact that something like -1 * %call4 <= 0 is reported as a "dominating constraint" seems suspicious to a person who's not familiar with the logic of ConstraintElimination and it doesn't look directly related to that patch. However, if I replace the shl nuw nsw i32 %arg, 3 with mul nuw nsw i32 %arg, 8, the miscompile doesn't happen: https://alive2.llvm.org/ce/z/EL2bCh.

@nikic
Copy link
Contributor

nikic commented Jan 18, 2024

From the debug log, I suspect that we're using the unsigned constraint system to prove a signed fact for some reason.

cc @fhahn

@nikic
Copy link
Contributor

nikic commented Jan 19, 2024

Okay, this is an interesting case.

We are adding an icmp ule i32 %call4, %shl fact, which get transferred as icmp sge i32 %call4, 0 and icmp sle i32 %call4, %shl to the signed system, because %shl is known non-negative.

Then we are checking whether a solution of these constraints plus %arg <= -1 exists, which it does not.

This is "correct" in the sense that if that umin call returns a non-poison result, then icmp slt i32 %arg, 0 is indeed known to be false. But we obviously can't actually make that assumption.

@dtcxzyw @fhahn This makes me think that the way we are currently adding facts for MinMaxIntrinsic (and I guess abs as well) is pretty fundamentally incorrect, because it effectively imposes a constraint that the umin operation returns a well-defined value. Any thoughts on how to fix this without dropping support for them entirely?

@nikic
Copy link
Contributor

nikic commented Jan 19, 2024

(The difference between shl and mul in the reproducer is because ValueTracking apparently currently doesn't know that mul nuw nsw is non-negative, but knows that shl nuw nsw is.)

@dtcxzyw dtcxzyw self-assigned this Jan 22, 2024
@dtcxzyw
Copy link
Member

dtcxzyw commented Jan 22, 2024

@dtcxzyw @fhahn This makes me think that the way we are currently adding facts for MinMaxIntrinsic (and I guess abs as well) is pretty fundamentally incorrect, because it effectively imposes a constraint that the umin operation returns a well-defined value. Any thoughts on how to fix this without dropping support for them entirely?

Can we conservatively check these intrinsics using isGuaranteedNotToBePoison?

@nikic
Copy link
Contributor

nikic commented Jan 22, 2024

If it's not too complicated, I think a better solution would be to only add the intrinsic constraints when the result variable is added to the constraint system.

@fhahn
Copy link
Contributor

fhahn commented Jan 22, 2024

If it's not too complicated, I think a better solution would be to only add the intrinsic constraints when the result variable is added to the constraint system.

One way to do that would be to check the uses of the intrinsic and find all the branches that it feeds, and add it as fact for the successors.

@nikic
Copy link
Contributor

nikic commented Jan 22, 2024

@dtcxzyw Do you plan to submit a PR for this issue?

@dtcxzyw
Copy link
Member

dtcxzyw commented Jan 22, 2024

@dtcxzyw Do you plan to submit a PR for this issue?

I will post a patch later.

fhahn added a commit that referenced this issue Jan 24, 2024
Tests with umin where the result may be poison for
#78621.
@fhahn fhahn closed this as completed in 3d91d96 Jan 24, 2024
@fhahn
Copy link
Contributor

fhahn commented Jan 24, 2024

I've pushed a fix that only adds the facts if the result is guaranteed to not be poison so the mis-compile is fixed. I also added a TODO to improve things if possible.

Reopening so we can pick the fix for the release branch

@fhahn fhahn reopened this Jan 24, 2024
@nikic
Copy link
Contributor

nikic commented Jan 24, 2024

@fhahn Why does your fix only do this for min/max but not abs? Can't it have the same problem?

@fhahn
Copy link
Contributor

fhahn commented Jan 24, 2024

@fhahn Why does your fix only do this for min/max but not abs? Can't it have the same problem?

I wasn't able to come up with a problematic test case so far for abs, but there's a few more things to try.

@DaniilSuchkov
Copy link
Contributor Author

@fhahn I'm not familiar with how this pass works, so I have a question: will this pass still use min/max/etc. to infer facts about the arguments of those intrinsics? I'm just a bit confused by the terminology: I see that it adds "constraints" based on things like icmp, which on their own don't actually constrain anything unless you branch on the result (or do something similar).

@nikic nikic added this to the LLVM 18.X Release milestone Jan 29, 2024
@nikic
Copy link
Contributor

nikic commented Jan 29, 2024

@fhahn I'm not familiar with how this pass works, so I have a question: will this pass still use min/max/etc. to infer facts about the arguments of those intrinsics? I'm just a bit confused by the terminology: I see that it adds "constraints" based on things like icmp, which on their own don't actually constrain anything unless you branch on the result (or do something similar).

Constraints for icmps will only be added in branches that the icmp dominates (or code that an assume with an icmp dominates). The intrinsic handling was a bit special in that constraints are added unconditionally.

@nikic
Copy link
Contributor

nikic commented Feb 1, 2024

/cherry-pick c83180c 3d91d96

llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue Feb 1, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue Feb 1, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 1, 2024

/pull-request #80260

llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue Feb 7, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue Feb 7, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
@tstellar tstellar closed this as completed Feb 7, 2024
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
Tests with umin where the result may be poison for
llvm#78621.

(cherry picked from commit c83180c)
tstellar pushed a commit to tstellar/llvm-project that referenced this issue Feb 14, 2024
The result of umin may be poison and in that case the added constraints
are not be valid in contexts where poison doesn't cause UB. Only queue
facts for min/max intrinsics if the result is guaranteed to not be
poison.

This could be improved in the future, by only adding the fact when
solving conditions using the result value.

Fixes llvm#78621.

(cherry picked from commit 3d91d96)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment