Change:
} } <del> if arg.is_sized_indirect() { // Don't copy an indirect argument to an alloca, the caller // already put it in a temporary alloca and gave it up. // FIXME: lifetimes let llarg = bx.get_param(llarg_idx); llarg_idx += 1; LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) } else if arg.is_unsized_indirect() { // As the storage for the indirect argument lives during // the whole function call, we just copy the fat pointer. let llarg = bx.get_param(llarg_idx); llarg_idx += 1; let llextra = bx.get_param(llarg_idx); llarg_idx += 1; let indirect_operand = OperandValue::Pair(llarg, llextra); let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); indirect_operand.store(bx, tmp); LocalRef::UnsizedPlace(tmp) } else { let tmp = PlaceRef::alloca(bx, arg.layout); bx.store_fn_arg(arg, &mut llarg_idx, tmp); LocalRef::Place(tmp) </del> <ins> match arg.mode { // Sized indirect arguments PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { // Don't copy an indirect argument to an alloca, the caller already put it // in a temporary alloca and gave it up. // FIXME: lifetimes if let Some(pointee_align) = attrs.pointee_align && pointee_align < arg.layout.align.abi { // ...unless the argument is underaligned, then we need to copy it to // a higher-aligned alloca. let tmp = PlaceRef::alloca(bx, arg.layout); bx.store_fn_arg(arg, &mut llarg_idx, tmp); LocalRef::Place(tmp) } else { let llarg = bx.get_param(llarg_idx); llarg_idx += 1; LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) } } // Unsized indirect qrguments PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { // As the storage for the indirect argument lives during // the whole function call, we just copy the fat pointer. let llarg = bx.get_param(llarg_idx); llarg_idx += 1; let llextra = bx.get_param(llarg_idx); llarg_idx += 1; let indirect_operand = OperandValue::Pair(llarg, llextra); let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); indirect_operand.store(bx, tmp); LocalRef::UnsizedPlace(tmp) } _ => { let tmp = PlaceRef::alloca(bx, arg.layout); bx.store_fn_arg(arg, &mut llarg_idx, tmp); LocalRef::Place(tmp) } </ins> } }) .collect::<Vec<_>>();

Discussion:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. --I tried this code: I expected to see this happen: Alignment requirements are upheld. Instead, this happened: is clearly not as we indicate. This is unsound. label I-unsound A-ffi This issue is somewhat similar to . The fix for that issue, , made this problem worse by causing Rust and alignments to differ in more cases. (That change is correct, because the Rust and alignment must differ to satisfy the ABI. It just uncovered this issue in more cases.) This is also similar to . However, unlike that one, this issue is practical (and easy) to fix. pointers are not exposed to Rust code, so we can just make a copy of the argument into a higher-aligned alloca, without breaking Rust semantics. In fact, we already do this in the reverse situation, when the Rust alignment is lower than the alignment (): <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --:
I will open a fix for this shortly.
WG-prioritization assigning priority (). label -I-prioritize +P-high