Skip to content

Commit ab0de54

Browse files
committed
Fix transmute::<T, U> where T requires a bigger alignment than U
For types that are not bitcast-compatible, transmute tries to avoid generating a temporary by translating its source expression directly into its destination, but when the source type has a bigger alignment requirement than the destination, this can lead to code that breaks due to misaligned stores. So in that case we need to generate a temporary for the source expression and then copy that into the destination, setting the proper alignment information on the memcpy/store. Fixes #32947
1 parent 41028de commit ab0de54

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/librustc_trans/intrinsic.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,28 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
227227
expr::SaveIn(d) => expr::SaveIn(PointerCast(bcx, d, llintype.ptr_to())),
228228
expr::Ignore => expr::Ignore
229229
};
230-
bcx = expr::trans_into(bcx, &arg_exprs[0], dest);
230+
let in_align = type_of::align_of(ccx, in_type);
231+
let out_align = type_of::align_of(ccx, out_type);
232+
if out_align >= in_align {
233+
bcx = expr::trans_into(bcx, &arg_exprs[0], dest);
234+
} else {
235+
let datum = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
236+
let datum = unpack_datum!(bcx, datum.to_rvalue_datum(bcx, "transmute"));
237+
match dest {
238+
expr::SaveIn(d) => {
239+
if datum.kind.is_by_ref() {
240+
let llsz = machine::llsize_of(bcx.ccx(), llouttype);
241+
call_memcpy(&B(bcx), d, datum.val, llsz, out_align as u32);
242+
} else {
243+
let store = Store(bcx, datum.val, d);
244+
unsafe {
245+
llvm::LLVMSetAlignment(store, out_align);
246+
}
247+
}
248+
}
249+
expr::Ignore => {}
250+
}
251+
}
231252
dest
232253
};
233254

src/test/run-pass/issue-32947.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(repr_simd, test)]
12+
13+
extern crate test;
14+
15+
#[repr(simd)]
16+
pub struct Mu64(pub u64, pub u64, pub u64, pub u64);
17+
18+
#[inline(never)]
19+
fn new(x: u64) -> Mu64 {
20+
Mu64(x, x, x, x)
21+
}
22+
23+
#[inline(never)]
24+
fn invoke_doom(x: &u8) -> [u8; 32] {
25+
// This transmute used to directly store the SIMD vector into a location
26+
// that isn't necessarily properly aligned
27+
unsafe { std::mem::transmute(new(*x as u64)) }
28+
}
29+
30+
fn main() {
31+
// Try to get the dest for the invoke_doom calls to be misaligned even in optimized builds
32+
let x = 0;
33+
test::black_box(invoke_doom(&x));
34+
let y = 1;
35+
test::black_box(invoke_doom(&y));
36+
}

0 commit comments

Comments
 (0)