Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -626,7 +626,9 @@ /// Determine if the call can synchroize with other threads bool hasNoSync() const { - return hasFnAttribute(Attribute::NoSync); + // Coordination with another thread requires at least one write to shared + // memory, so readonly also implies nosync. + return onlyReadsMemory() || hasFnAttribute(Attribute::NoSync); } void setNoSync() { addFnAttr(Attribute::NoSync); Index: llvm/lib/IR/Value.cpp =================================================================== --- llvm/lib/IR/Value.cpp +++ llvm/lib/IR/Value.cpp @@ -750,7 +750,8 @@ // A pointer to an object in a function which neither frees, nor can arrange // for another thread to free on its behalf, can not be freed in the scope - // of the function. + // of the function. Note that readonly implies both of these. Which is good + // because we don't directly infer nosync, but do readonly. if (F->doesNotFreeMemory() && F->hasNoSync()) return false; 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