Index: llvm/lib/Analysis/InstructionSimplify.cpp =================================================================== --- llvm/lib/Analysis/InstructionSimplify.cpp +++ llvm/lib/Analysis/InstructionSimplify.cpp @@ -2556,6 +2556,17 @@ // So, we'll assume that two non-empty allocas have different addresses // for now. + if (isNoAliasCall(V1) && isNoAliasCall(V2) && + cast(V1)->isReturnNonNull() && !V1->canBeFreed() && + cast(V2)->isReturnNonNull() && !V2->canBeFreed()) + // In general, the lifetime of two noalias (e.g. malloc'd) allocations + // can be disjoint. This means that the storage regions can overlap. + // However, if we know that the allocation can't be freed, then they + // must exist at the same time. The most common case of an allocation + // which can't be freed is an object in a GC'd language before lowering + // out of the abstract machine model. + return true; + auto isByValArgOrGlobalVarOrAlloca = [](const Value *V) { if (const Argument *A = dyn_cast(V)) return A->hasByValAttr(); Index: llvm/test/Transforms/InstSimplify/compare.ll =================================================================== --- llvm/test/Transforms/InstSimplify/compare.ll +++ llvm/test/Transforms/InstSimplify/compare.ll @@ -2831,4 +2831,57 @@ ; TODO: Add coverage for global aliases, link once, etc.. +declare noalias i8* @my_alloc(i64) allocsize(0) +declare void @my_free(i8*) + +; %a and %b might have the same address despite being noalias as +; the allocator is allowed to reuse the storage after %a was freed +define i1 @neg_overlapping_malloc() { +; CHECK-LABEL: @neg_overlapping_malloc( +; CHECK-NEXT: [[A:%.*]] = call i8* @my_alloc(i64 8) +; CHECK-NEXT: call void @my_free(i8* [[A]]) +; CHECK-NEXT: [[B:%.*]] = call i8* @my_alloc(i64 8) +; CHECK-NEXT: [[RES:%.*]] = icmp ne i8* [[A]], [[B]] +; CHECK-NEXT: ret i1 [[RES]] +; + %a = call i8* @my_alloc(i64 8) + call void @my_free(i8* %a) + %b = call i8* @my_alloc(i64 8) + %res = icmp ne i8* %a, %b + ret i1 %res +} + +declare noalias i8 addrspace(1)* @gc_alloc(i64) allocsize(0) + +; For an allocation which isn't freed (e.g. a allocation in a GCd language +; before lowering out of the abstract machine model), we *can* assume the +; pointers are non equal. Essentially, the abstract model tells us that +; a comparison which would reveal reuse after lowering is UB. +define i1 @test_gc_alloc() gc "statepoint-example" { +; CHECK-LABEL: @test_gc_alloc( +; CHECK-NEXT: [[A:%.*]] = call nonnull i8 addrspace(1)* @gc_alloc(i64 8) +; CHECK-NEXT: [[B:%.*]] = call nonnull i8 addrspace(1)* @gc_alloc(i64 8) +; CHECK-NEXT: ret i1 true +; + %a = call nonnull i8 addrspace(1)* @gc_alloc(i64 8) + %b = call nonnull i8 addrspace(1)* @gc_alloc(i64 8) + %res = icmp ne i8 addrspace(1)* %a, %b + ret i1 %res +} + +; Both could be null and thus equal +define i1 @neg_gc_alloc_null() gc "statepoint-example" { +; CHECK-LABEL: @neg_gc_alloc_null( +; CHECK-NEXT: [[A:%.*]] = call i8 addrspace(1)* @gc_alloc(i64 8) +; CHECK-NEXT: [[B:%.*]] = call i8 addrspace(1)* @gc_alloc(i64 8) +; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 addrspace(1)* [[A]], [[B]] +; CHECK-NEXT: ret i1 [[RES]] +; + %a = call i8 addrspace(1)* @gc_alloc(i64 8) + %b = call i8 addrspace(1)* @gc_alloc(i64 8) + %res = icmp ne i8 addrspace(1)* %a, %b + ret i1 %res +} + + attributes #0 = { null_pointer_is_valid }