Change:
A generated executable occasionally fails to launch when built with the rustc options . I tried this code: Compiled with the following shell script: When I ran the generated executable repeatedly, the execution of the program stalled (did not terminate nor output anything; did not even enter the function) 5 out of 100 times. When I ran the executable from , I could see that had occured because it attempted to load a 32-byte block from an unaligned memory using (which requires the operand address to be 32-byte aligned). thread , queue = '', stop reason = EXCBADACCESS (code=EXCI386GPFLT) frame : 0x0000000100000bf6 mainmain + 518 mainmain: -0x100000bf6 <+518: vmovdqa (%rax), %ymm0 0x100000bfa <+522: movl $0x1, %ecx 0x100000bff <+527: vmovq %rcx, %xmm1 0x100000c04 <+532: vmovdqa %ymm1, (%rax) (lldb) register read General Purpose Registers: rax = 0x0000000100300470 : rustc 1.21.0-nightly ( 2017-08-22) binary: rustc commit-hash: commit-date: 2017-08-22 host: x8664-apple-darwin release: 1.21.0-nightly LLVM version: 4.0 The output of (a tool that comes with macOS) when the program is stalled: Call graph: 2721 Thread15178881 DispatchQueue1: (serial) 2721 start (in ) + 1 [0x7fffa220d235] 2721 0x0 2721 sigtramp (in ) + 26 [0x7fffa241cb3a] 2721 std::sys::imp::stackoverflow::imp::signalhandler (in main) + 125 [0x105c58b7d] The offending instruction is supposedly , which is called during the execution of , which is called when the runtime is being initialized. After the optimization, this call to the intrinsic function is translated into the following LLVM instruction: %t.0.copyload.i.i.i.i.i.i.i.i.i = load <4 x i64, <4 x i64bitcast ({ { { i64, [32 x i8] } }, { { i1 } }, { { i1 } }, [6 x i8] } to <4 x i64), align 32, !dbg !3742, !noalias !3762 This is translated into the following x86_64 instruction: vmovdqa (%rax), %ymm0
Related:
Good analysis I guess that bitcast is dodgy, creating an unaligned pointer. I wonder if that comes from an LLVM pass or rust codegen. I'm not sure that is related is it?
The bitcast is fine, at least if LTO is not applied. As shown in , those is translated to with SSE. The question is why is chosen instead of ...
I guess because it expects a to be correctly 32 byte aligned and maybe the aligned instruction is more performant?
Ah yes, the bitcast is fine, its the with that load that is the problem. Without LTO you get as expected.
Narrowed down the code to reproduce the issue. The issue can be reproduced with : It wasn't nor LLVM's optimization passes; it all had to do with how thread-local variables (TLVs) are handled by macOS's . LLVM expects global variables (including thread local ones) are aligned as it is specified: = internal threadlocal global { i64, i64, i64, i64 } zeroinitializer, align 32 It does not have to be by itself, but maybe LLVM decided to make it anyway because that way it could optimize the program better by using aligned load/stores, I suppose. But anyway, this makes it legitimate to use for all load/stores on this variable like this: = load <4 x i64, <4 x i64bitcast ({ i64, i64, i64, i64 } to <4 x i64), align 32 And it outputs a Mach-O section for TLVs with a proper alignment requirement: The problem is that, 's when allocating a memory region for TLVs: The aforementioned program fails if the returned happened not to be 32-byte aligned. By the way, LDC devs seem to have experienced .
According to the LDC report, it has already been fixed on Xcode 8, but we are still seeing this bug? Would it be due to the distributed is built with Xcode 7? BTW none of the following will fix the issue: putting on , nor making nonzero (put it in instead of ).
Is this something we could perhaps work around by allocating larger thread locals on our end and then doing the alignment ourselves?
Yes, methods like would work. The overhead of bytes for every (TLV, thread) seems a little bit too much to me, but maybe it's reasonable on modern computer systems where plenty of memory is available. A compiler support would be required for types with even larger alignment requirements. (related to ) I guess the fix introduced with Xcode 8 only changes the way TLV segments are handled by the linker. The actual memory regions for TLVs are still allocated by (that comes with macOS, not Xcode) using , which effectively limits TLV's alignment size to 16 bytes. I think there's nothing we can do about this. This can be verified by running the following program, which sporadically crashes even if compiled with Xcode 8:

Discussion:
unsafe { let g = get_static(cx, def_id); <del> let v = match ::mir::codegen_static_initializer(cx, def_id) { </del> <ins> let (v, alloc) = match ::mir::codegen_static_initializer(cx, def_id) { </ins> Ok(v) => v, // Error has already been reported Err(_) => return,

if attr::contains_name(attrs, "thread_local") { llvm::set_thread_local_mode(g, cx.tls_model); <ins> // Do not allow LLVM to change the alignment of a TLS on macOS. // // By default a global's alignment can be freely increased. // This allows LLVM to generate more performant instructions // e.g. using load-aligned into a SIMD register. // // However, on macOS 10.10 or below, the dynamic linker does not // respect any alignment given on the TLS (radar 24221680). // This will violate the alignment assumption, and causing segfault at runtime. // // This bug is very easy to trigger. In `println!` and `panic!`, // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, // which the values would be `mem::replace`d on initialization. // The implementation of `mem::replace` will use SIMD // whenever the size is 32 bytes or higher. LLVM notices SIMD is used // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, // which macOS's dyld disregarded and causing crashes // (see issues #51794, #51758, #50867, #48866 and #44056). // // To workaround the bug, we trick LLVM into not increasing // the global's alignment by explicitly assigning a section to it // (equivalent to automatically generating a `#[link_section]` attribute). // See the comment in the `GlobalValue::canIncreaseAlignment()` function // of `lib/IR/Globals.cpp` for why this works. // // When the alignment is not increased, the optimized `mem::replace` // will use load-unaligned instructions instead, and thus avoiding the crash. // // We could remove this hack whenever we decide to drop macOS 10.10 support. if cx.tcx.sess.target.target.options.is_like_osx { let sect_name = if alloc.bytes.iter().all(|b| *b == 0) { CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss0") } else { CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data0") }; llvm::LLVMSetSection(g, sect_name.as_ptr()); } </ins> } base::set_link_section(cx, g, attrs);

pub fn codegen_static_initializer<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, def_id: DefId) <del> -> Result<ValueRef, Lrc<ConstEvalErr<'tcx>>> </del> <ins> -> Result<(ValueRef, &'tcx Allocation), Lrc<ConstEvalErr<'tcx>>> </ins> { let instance = ty::Instance::mono(cx.tcx, def_id); let cid = GlobalId {

ConstValue::ByRef(alloc, n) if n.bytes() == 0 => alloc, _ => bug!("static const eval returned {:#?}", static_), }; <del> Ok(const_alloc_to_llvm(cx, alloc)) </del> <ins> Ok((const_alloc_to_llvm(cx, alloc), alloc)) </ins> } impl<'a, 'tcx> FunctionCx<'a, 'tcx> {

<ins> // Copyright 2018 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. // ignore-tidy-linelength // only-macos // no-system-llvm // min-llvm-version 6.0 // compile-flags: -O #![crate_type = "rlib"] #![feature(thread_local)] // CHECK: @STATIC_VAR_1 = internal thread_local unnamed_addr global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 #[no_mangle] #[allow(private_no_mangle_statics)] #[thread_local] static mut STATIC_VAR_1: [u32; 8] = [0; 8]; // CHECK: @STATIC_VAR_2 = internal thread_local unnamed_addr global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 #[no_mangle] #[allow(private_no_mangle_statics)] #[thread_local] static mut STATIC_VAR_2: [u32; 8] = [4; 8]; #[no_mangle] pub unsafe fn f(x: &mut [u32; 8]) { std::mem::swap(x, &mut STATIC_VAR_1) } #[no_mangle] pub unsafe fn g(x: &mut [u32; 8]) { std::mem::swap(x, &mut STATIC_VAR_2) } </ins>

<ins> // Copyright 2018 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. // only-x86_64 // no-prefer-dynamic // compile-flags: -Ctarget-feature=+avx -Clto fn main() {} </ins>