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

Miscompilation: Equal pointers compare as inequal #60696

Closed
JakobDegen opened this issue Feb 13, 2023 · 4 comments
Closed

Miscompilation: Equal pointers compare as inequal #60696

JakobDegen opened this issue Feb 13, 2023 · 4 comments
Labels
duplicate Resolved as duplicate llvm:optimizations

Comments

@JakobDegen
Copy link

Upstream issue: rust-lang/rust#107975 .

Godbolt.

define void @_ZN7example3foo17h7be158369849a4c6E(ptr noalias nocapture noundef sret({ i64, i64, i8, [7 x i8] }) dereferenceable(24) %0) unnamed_addr #0 {
start:
  %1 = alloca i8, align 1
  %2 = alloca i8, align 1
  %v1 = alloca [16 x i8], align 1
  %v = alloca [16 x i8], align 1
  call void @llvm.lifetime.start.p0(i64 16, ptr %v)
  call void @llvm.lifetime.start.p0(i64 1, ptr %2)
  store i8 4, ptr %2, align 1
  call void asm sideeffect "", "r,~{memory}"(ptr %2)
  %_4 = load i8, ptr %2, align 1
  call void @llvm.lifetime.end.p0(i64 1, ptr %2)
  %3 = getelementptr inbounds [16 x i8], ptr %v, i64 0, i64 0
  call void @llvm.memset.p0.i64(ptr align 1 %3, i8 %_4, i64 16, i1 false)
  %_6 = getelementptr inbounds [16 x i8], ptr %v, i64 0, i64 0
  call void @llvm.lifetime.end.p0(i64 16, ptr %v)
  call void @llvm.lifetime.start.p0(i64 16, ptr %v1)
  call void @llvm.lifetime.start.p0(i64 1, ptr %1)
  store i8 4, ptr %1, align 1
  call void asm sideeffect "", "r,~{memory}"(ptr %1)
  %_9 = load i8, ptr %1, align 1
  call void @llvm.lifetime.end.p0(i64 1, ptr %1)
  %4 = getelementptr inbounds [16 x i8], ptr %v1, i64 0, i64 0
  call void @llvm.memset.p0.i64(ptr align 1 %4, i8 %_9, i64 16, i1 false)
  %_11 = getelementptr inbounds [16 x i8], ptr %v1, i64 0, i64 0
  call void @llvm.lifetime.end.p0(i64 16, ptr %v1)
  %_13 = ptrtoint ptr %_6 to i64
  %_15 = ptrtoint ptr %_11 to i64
  %_17 = icmp eq ptr %_6, %_11
  store i64 %_13, ptr %0, align 8
  %5 = getelementptr inbounds { i64, i64, i8, [7 x i8] }, ptr %0, i32 0, i32 1
  store i64 %_15, ptr %5, align 8
  %6 = getelementptr inbounds { i64, i64, i8, [7 x i8] }, ptr %0, i32 0, i32 2
  %7 = zext i1 %_17 to i8
  store i8 %7, ptr %6, align 8
  ret void
}

declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #1

declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2

declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2

This function creates two stack allocations with disjoint lifetimes and returns the pointers of those allocations cast to integers, as well as the result of comparing those pointers for equality. After optimization, the comparison is folded to false:

define void @_ZN7example3foo17h7be158369849a4c6E(ptr noalias nocapture noundef writeonly sret({ i64, i64, i8, [7 x i8] }) dereferenceable(24) %0) unnamed_addr {
  %1 = alloca i8, align 1
  %2 = alloca i8, align 1
  %v1 = alloca [16 x i8], align 1
  %v = alloca [16 x i8], align 1
  call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %v)
  call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %2)
  store i8 4, ptr %2, align 1
  call void asm sideeffect "", "r,~{memory}"(ptr nonnull %2) #1
  call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %2)
  call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %v)
  call void @llvm.lifetime.start.p0(i64 16, ptr nonnull %v1)
  call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %1)
  store i8 4, ptr %1, align 1
  call void asm sideeffect "", "r,~{memory}"(ptr nonnull %1) #1
  call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %1)
  call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %v1)
  %_13 = ptrtoint ptr %v to i64
  %_15 = ptrtoint ptr %v1 to i64
  store i64 %_13, ptr %0, align 8
  %3 = getelementptr inbounds { i64, i64, i8, [7 x i8] }, ptr %0, i64 0, i32 1
  store i64 %_15, ptr %3, align 8
  %4 = getelementptr inbounds { i64, i64, i8, [7 x i8] }, ptr %0, i64 0, i32 2
  store i8 0, ptr %4, align 8
  ret void
}

declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0

declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #0

attributes #0 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #1 = { nounwind }

However, the addresses of these pointers may be equal. This reproduces as an end-to-end miscompilation in Rust.

@efriedma-quic
Copy link
Collaborator

I think this is a dup of #45725.

@JakobDegen
Copy link
Author

I don't think it's the same issue at all - that one is about pointer replacement under a dominating icmp, this one is about replacing icmps with incorrect values. I can reproduce this issue too by casting the pointers to integers first. Now you have two numerically equal integers that icmp says are not the same.

@inclyc
Copy link
Member

inclyc commented Feb 15, 2023

I think this is a dup of #45725.

Agreed.

that one is about pointer replacement under a dominating icmp, this one is about replacing icmps with incorrect values

I think the problem mentioned by #45725 is actually the same as this one. The semantics of memory allocation "alloca" are inconsistent with lifetime intrinsics. See #45725 (comment).

@EugeneZelenko EugeneZelenko added the duplicate Resolved as duplicate label Feb 15, 2023
@EugeneZelenko EugeneZelenko closed this as not planned Won't fix, can't repro, duplicate, stale Feb 15, 2023
@JakobDegen
Copy link
Author

Ah, no, you're right, it is the same issue - had gotten that issue mixed up with another for some reason

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate Resolved as duplicate llvm:optimizations
Projects
None yet
Development

No branches or pull requests

4 participants