Change:
In In NB: Some pre-1.0 nightlies already behaved this way.
Looks like a codegen bug related to the attribute we place on the IR. It also looks like this isn't necessarily related to a function pointer. The codegen for this function but when cleaned up it looks like: When optimized, we get: And as we can see, the store of 1 has been optimized away. If we instead remove the attribute on the pointer, the store remains cc perhaps we can't place on ? That seems unfortunate, but I guess there's also a possibility this is a bug in LLVM (but seems unlikely).
I'm wrong.
An LLVM IR is semantically the same as an with a trivial landing pad. There's a decent argument that LLVM's treatment of is wrong. Consider the following C++ (GNU-dialect) testcase: gcc prints 1; clang prints 0. On the other hand, you're likely to run into resistance proposing any restriction on how alias analysis can reason about noalias pointers.
This isn't just a issue; it can occur with other kinds of alias information as well. For example, the first store gets deleted in this code too, even though the call to may unwind. MemoryDependenceAnalysis in LLVM currently says that the second store here has a direct local dependence on the first store. There's a sense in which that is a correct answer, but DeadStoreElimination inteprets this answer to mean that the first store is dead. It's not immediately obvious which of these two should be fixed. having an implicit control path requires special handling in a other passes too (eg., the inliner has to know how to convert into if it's being inlined into a context where it'll have a cleanup), so it'd be cool if LLVM had something like an with only one successor, which the optimizer could treat like a kind of conditional return, however, that's a much bigger change.
triage: P-high
Is the problem here that LLVM is optimizing too heavily, or that we are providing incorrect annotations? Either way, it may be prudent to supply fewer annotations for the time being. That'd certainly be an easy patch to do.
I asked sunfish to clarify on IRC, and he believes this is 100% an llvm bug.
I've now filed an LLVM bug:
We were discussing in the compiler team meeting. Do we expect LLVM to fix this in a short time frame? I would think probably not -- we were thinking therefore that it would be prudent to workaround in the time being. Would this mean removing the aliasing annotations and thus taking some speed hit? Are there other solutions? Other implications?
No; fixing DSE is probably easy enough... but trusting noalias to work correctly alongside unwinding would probably require auditing every use of alias analysis in LLVM (for example, I'm pretty sure LICM is also broken). In any case, no matter how fast this is fixed, Rust can't unconditionally depend on any fix because we support linking against 3.7. Yes. No. I don't think so.
LICM is not obviously broken; see eg. isGuaranteedToExecute in ; it explicitly checks for instructions which may unwind before doing related things. The bug here does appear to arise from the difference between MemDep's way of viewing the world and DSE's. I don't know how conservative Rust wants to be here, but there is room for optimism that this may just be an isolated DSE bug (or a MemDep bug which only affects DSE).
Won't removing kill our performance?
Okay, LICM's is in fact correct... the definition of isGuaranteedToExecute is overly conservative in a way which happens to cover up this issue. That said, my non-comprehensive audit shows -memcpyopt, -mldst-motion, and -loop-idiom are broken. (Granted, -mldst-motion isn't part of the default pass pipeline.) A couple more testcases:
Wow. I revoke my optimism then.
I am curious what effect it will have, I don't really have a good feeling at all for how important this is. , Dan Gohman wrote:
This looks rather unambiguously like an LLVM bug (though we may want to work around it).
(a reasonable subtask for a relative newcomer to the project could be to evaluate the actual performance impact removing the attribute here.)
For rust itself, the text segment in the files grows by 1-3%. Runtime for also goes up by 1-3%, depending on the crate.
Could we have concrete numbers ( output)? This is much lower than I thought - what change have you done?
Of course, 1-3% perf losses still suck - this might be a reason to make official rustc builds use .
I removed all attributes from function arguments and return values. Sizes before: Sizes after: Ignoring the crate, which is pretty much unaffected by this change, the size of the text segment is 1.67% larger without the noalias attributes. I'll gather new timings in the evening. It'll probably be output from though, as the times didn't seem stable enough to be useful. I'll check them again though.
More numbers. I went with the times, because this time they seemed rather stable. Maybe I previously forgot to turn off something that ran in the background and messed with the timings.
great, thanks! This is sort of inline with my expectations. I also expect we'll see more impact on smaller examples.
As an aside, at the work week I got a lot of great suggestions for benchmarks, I have to try and draw them up together into a central list now.
Assigning to myself for the purpose of gathering up a few benchmarks.
Doesn't the above patch remove way more annotations than is necessary? In particular, The on returns from and the on shared references should be much less critical, and not be affected by the LLVM bug at hand. I can't quite tell where many of the other used to be .
So I ran the benchmarks from with a current master build and one with the above patch applied. master: Patched:
That is interesting. Looks like about a 5% slowdown.
This patch also removes NoAlias for " pointer return values" and for shared references. Neither of them are affected by the problem discussed here, and I assume at least return values are not even controversial. (The other places NoAlias is removed, I could not recognize; mind you I did not spend much time analyzing that.) Maybe it'd be interesting to see how big the impact of just disabling NoAlias for shared borrows is? EDIT: oops obviously I mixed up shared and mutable references,
Time for some new numbers. This time only removing the attribute from mutable borrows as suggested by I've been slightly sceptical about that the return value ones being ok to be marked as , but that was because I thought of them as an out-parameter (like in the LLVM IR) instead of the return values they are in the rust code. The problematic cases don't arise with return values because we only write to them at the end of the function. Current master (): Patched master: So basically no difference in performance. I'll make an PR for this.
Thanks for persevering,
On Thu, Feb 04, 2016 at 02:48:48PM -0800, Björn Steinbrink wrote: Great.
could be a regression from this change. It's not that particular code regresses all the time, but it makes the optimization flaky, and it depends on the surrounding program.

Discussion:
// on memory dependencies rather than pointer equality let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe(); <del> if mt.mutbl == hir::MutMutable || !interior_unsafe { </del> <ins> if mt.mutbl != hir::MutMutable && !interior_unsafe { </ins> attrs.arg(idx, llvm::Attribute::NoAlias); }

<ins> // Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![crate_name="a"] #![crate_type = "lib"] pub struct X(pub u8); impl Drop for X { fn drop(&mut self) { assert_eq!(self.0, 1) } } pub fn f(x: &mut X, g: fn()) { x.0 = 1; g(); x.0 = 0; } </ins>

pub fn unsafe_borrow(_: &UnsafeInner) { } <del> // CHECK: @mutable_unsafe_borrow(%UnsafeInner* noalias dereferenceable(2)) </del> <ins> // CHECK: @mutable_unsafe_borrow(%UnsafeInner* dereferenceable(2)) </ins> // ... unless this is a mutable borrow, those never alias <ins> // ... except that there's this LLVM bug that forces us to not use noalias, see #29485 </ins> #[no_mangle] pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) { } <del> // CHECK: @mutable_borrow(i32* noalias dereferenceable(4)) </del> <ins> // CHECK: @mutable_borrow(i32* dereferenceable(4)) </ins> // FIXME #25759 This should also have `nocapture` <ins> // ... there's this LLVM bug that forces us to not use noalias, see #29485 </ins> #[no_mangle] pub fn mutable_borrow(_: &mut i32) { }

fn slice(_: &[u8]) { } <del> // CHECK: @mutable_slice(i8* noalias nonnull, [[USIZE]]) </del> <ins> // CHECK: @mutable_slice(i8* nonnull, [[USIZE]]) </ins> // FIXME #25759 This should also have `nocapture` <ins> // ... there's this LLVM bug that forces us to not use noalias, see #29485 </ins> #[no_mangle] fn mutable_slice(_: &mut [u8]) { }

<ins> // Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // aux-build:issue-29485.rs #[feature(recover)] extern crate a; fn main() { let _ = std::thread::spawn(move || { a::f(&mut a::X(0), g); }).join(); } fn g() { panic!(); } </ins>