Change:
// CHECK-LABEL: @vec_iterator_cast_aggregate #[no_mangle] pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> { <del> // FIXME These checks should be the same as other functions. // CHECK-NOT: @__rust_alloc // CHECK-NOT: @__rust_alloc </del> <ins> // CHECK-NOT: loop // CHECK-NOT: call </ins> vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect() } <del> // CHECK-LABEL: @vec_iterator_cast_deaggregate </del> <ins> // CHECK-LABEL: @vec_iterator_cast_deaggregate_tra </ins> #[no_mangle] <del> pub fn vec_iterator_cast_deaggregate(vec: Vec<Bar>) -> Vec<[u64; 4]> { // FIXME These checks should be the same as other functions. // CHECK-NOT: @__rust_alloc // CHECK-NOT: @__rust_alloc </del> <ins> pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> { // CHECK-NOT: loop // CHECK-NOT: call // Safety: For the purpose of this test we assume that Bar layout matches [u64; 4]. // This currently is not guaranteed for repr(Rust) types, but it happens to work here and // the UCG may add additional guarantees for homogenous types in the future that would make this // correct. vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect() } // CHECK-LABEL: @vec_iterator_cast_deaggregate_fold #[no_mangle] pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> { // CHECK-NOT: loop // CHECK-NOT: call </ins> // Safety: For the purpose of this test we assume that Bar layout matches [u64; 4]. // This currently is not guaranteed for repr(Rust) types, but it happens to work here and

Discussion:
Adding the follow method as part of a benchmark to which exercises results in the following assembly (extracted with objdump): The ghidra decompile for the same function (comments are mine): Note the useless loop. The number of loop iterations (or rather the pointer increments) is needed to calculate the new length of the output . LLVM already manages to hoist but then it fails to eliminate the now-useless loop. The issue does not occur if one uses instead, which always results in length == 0. I tried several variations of the loop (e.g. replacing with a simple ) but it generally results in the same or worse assembly. Note: The assembly looks somewhat different if I but the decrementing loop without side-effect is still there. I assume the differences are due to LTO or some other compiler settings. Tested on commit 2020-11-21 22:46 modify labels: +I-slow
It looks like this can be solved with more ~dakka~ passes. On godbolt it can be made loop-free by adding And branchless with Using as starting point requires the same passes to be to achieve these results, i.e. it does not provide a better baseline. I failed to reproduce this improvement locally but that might be due to argument escaping issues when attempting to put the space-separated list into
Surprisingly targeting a current CPU results in only one extra pass needed to achieve the loop-free result:
Inserting one loop deletion pass directly into also results in the branchless version without having to change target-cpu or adding additional other passes. So I assume that the other passes are only needed due to the way that works.
It looks like that this removed on nightly now. On nightly: On stable:
That's only true for types because I implemented a specialization for that purpose. The suboptimal assembly still exists for types not implementing Copy, e.g. newtypes. I'll update my initial post.
Something that was is that this case also fails for - i.e. with the specialization - is when the wrapper type is
Seems to work in the other direction weirdly is optimised away, whilst is not
Is there a case here that still doesn't optimize? It looks like all the existing godbolt links do.
Looks good, I'll update the existing codegen test.