Index: llvm/lib/IR/Value.cpp =================================================================== --- llvm/lib/IR/Value.cpp +++ llvm/lib/IR/Value.cpp @@ -760,6 +760,17 @@ if (F->doesNotFreeMemory() && F->hasNoSync()) return false; + // Free is modeled as writing to the freed memory, so a readonly implies + // nofree. Additionally, coordination with another thread to have it free + // on our behalf requires at least one write to shared memory, so readonly + // also implies that no other thread can free it (in a well defined manner). + // Subtly, this is weaker than implying nosync as the later would require + // us to prove this thread didn't read from shared memory either. + // (NOTE: This case is important in practice as we don't infer nosync, but + // do infer readonly.) + if (F->onlyReadsMemory()) + return false; + // With garbage collection, deallocation typically occurs solely at or after // safepoints. If we're compiling for a collector which uses the // gc.statepoint infrastructure, safepoints aren't explicitly present Index: llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll =================================================================== --- llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll +++ llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll @@ -260,9 +260,7 @@ } ; CHECK-LABEL: 'infer_func_attrs2' -; GLOBAL: %p -; POINT-NOT: %p -; FIXME: Can be inferred from attributes +; CHECK: %p define void @infer_func_attrs2(i32* dereferenceable(8) %p) readonly { call void @mayfree() %v = load i32, i32* %p