Skip to content

Commit 58eddba

Browse files
committed
Copy byval argument to local stackslot if alignment is insufficient
1 parent 8635445 commit 58eddba

1 file changed

Lines changed: 98 additions & 13 deletions

File tree

src/abi/mod.rs

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use cranelift_codegen::ir::{
1212
};
1313
use cranelift_codegen::isa::CallConv;
1414
use cranelift_module::ModuleError;
15-
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
15+
use rustc_abi::{Align, CanonAbi, ExternAbi, X86Call};
1616
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
1717
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
1818
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -243,10 +243,17 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
243243
self::returning::codegen_return_param(fx, &ssa_analyzed, &mut block_params_iter);
244244
assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE);
245245

246-
// None means pass_mode == NoPass
246+
struct ArgValue<'tcx> {
247+
value: Option<CValue<'tcx>>,
248+
/// If set, the argument is a byval/byref pointer whose pointee alignment is smaller than
249+
/// the Rust ABI alignment. In that case the argument must be copied to a sufficiently
250+
/// aligned local stack slot before it can be treated as a place.
251+
underaligned_pointee_align: Option<Align>,
252+
}
253+
247254
enum ArgKind<'tcx> {
248-
Normal(Option<CValue<'tcx>>),
249-
Spread(Vec<Option<CValue<'tcx>>>),
255+
Normal(ArgValue<'tcx>),
256+
Spread(Vec<ArgValue<'tcx>>),
250257
}
251258

252259
// FIXME implement variadics in cranelift
@@ -280,17 +287,34 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
280287
let mut params = Vec::new();
281288
for (i, _arg_ty) in tupled_arg_tys.iter().enumerate() {
282289
let arg_abi = arg_abis_iter.next().unwrap();
283-
let param =
290+
let value =
284291
cvalue_for_param(fx, Some(local), Some(i), arg_abi, &mut block_params_iter);
285-
params.push(param);
292+
let underaligned_pointee_align =
293+
match arg_abi.mode {
294+
PassMode::Indirect { attrs, .. } => attrs
295+
.pointee_align
296+
.filter(|&pointee_align| pointee_align < arg_abi.layout.align.abi),
297+
_ => None,
298+
};
299+
params.push(ArgValue { value, underaligned_pointee_align });
286300
}
287301

288302
(local, ArgKind::Spread(params), arg_ty)
289303
} else {
290304
let arg_abi = arg_abis_iter.next().unwrap();
291-
let param =
305+
let value =
292306
cvalue_for_param(fx, Some(local), None, arg_abi, &mut block_params_iter);
293-
(local, ArgKind::Normal(param), arg_ty)
307+
let underaligned_pointee_align = match arg_abi.mode {
308+
PassMode::Indirect { attrs, .. } => attrs
309+
.pointee_align
310+
.filter(|&pointee_align| pointee_align < arg_abi.layout.align.abi),
311+
_ => None,
312+
};
313+
(
314+
local,
315+
ArgKind::Normal(ArgValue { value, underaligned_pointee_align }),
316+
arg_ty,
317+
)
294318
}
295319
})
296320
.collect::<Vec<(Local, ArgKind<'tcx>, Ty<'tcx>)>>();
@@ -311,7 +335,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
311335
for (local, arg_kind, ty) in func_params {
312336
// While this is normally an optimization to prevent an unnecessary copy when an argument is
313337
// not mutated by the current function, this is necessary to support unsized arguments.
314-
if let ArgKind::Normal(Some(val)) = arg_kind {
338+
if let ArgKind::Normal(ArgValue { value: Some(val), underaligned_pointee_align: None }) =
339+
arg_kind
340+
{
315341
if let Some((addr, meta)) = val.try_to_ptr() {
316342
// Ownership of the value at the backing storage for an argument is passed to the
317343
// callee per the ABI, so it is fine to borrow the backing storage of this argument
@@ -336,15 +362,74 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_
336362
assert_eq!(fx.local_map.push(place), local);
337363

338364
match arg_kind {
339-
ArgKind::Normal(param) => {
340-
if let Some(param) = param {
365+
ArgKind::Normal(ArgValue { value: Some(param), underaligned_pointee_align }) => {
366+
if let Some(pointee_align) = underaligned_pointee_align
367+
&& let Some(dst_ptr) = place.try_to_ptr()
368+
&& let Some((src_ptr, None)) = param.try_to_ptr()
369+
&& layout.size != Size::ZERO
370+
{
371+
let mut flags = MemFlags::new();
372+
flags.set_notrap();
373+
374+
let to_addr = dst_ptr.get_addr(fx);
375+
let from_addr = src_ptr.get_addr(fx);
376+
let size = layout.size.bytes();
377+
378+
// `emit_small_memory_copy` uses `u8` for alignments, just use the maximum
379+
// alignment that fits in a `u8` if the actual alignment is larger.
380+
let dst_align = layout.align.bytes().try_into().unwrap_or(128);
381+
let src_align = pointee_align.bytes().try_into().unwrap_or(128);
382+
383+
fx.bcx.emit_small_memory_copy(
384+
fx.target_config,
385+
to_addr,
386+
from_addr,
387+
size,
388+
dst_align,
389+
src_align,
390+
true,
391+
flags,
392+
);
393+
} else {
341394
place.write_cvalue(fx, param);
342395
}
343396
}
397+
ArgKind::Normal(ArgValue { value: None, underaligned_pointee_align: _ }) => {}
344398
ArgKind::Spread(params) => {
345-
for (i, param) in params.into_iter().enumerate() {
399+
for (i, ArgValue { value: param, underaligned_pointee_align }) in
400+
params.into_iter().enumerate()
401+
{
346402
if let Some(param) = param {
347-
place.place_field(fx, FieldIdx::new(i)).write_cvalue(fx, param);
403+
let field_place = place.place_field(fx, FieldIdx::new(i));
404+
let field_layout = field_place.layout();
405+
if let Some(pointee_align) = underaligned_pointee_align
406+
&& let Some(dst_ptr) = field_place.try_to_ptr()
407+
&& let Some((src_ptr, None)) = param.try_to_ptr()
408+
&& field_layout.size != Size::ZERO
409+
{
410+
let mut flags = MemFlags::new();
411+
flags.set_notrap();
412+
413+
let to_addr = dst_ptr.get_addr(fx);
414+
let from_addr = src_ptr.get_addr(fx);
415+
let size = field_layout.size.bytes();
416+
417+
let dst_align = field_layout.align.bytes().try_into().unwrap_or(128);
418+
let src_align = pointee_align.bytes().try_into().unwrap_or(128);
419+
420+
fx.bcx.emit_small_memory_copy(
421+
fx.target_config,
422+
to_addr,
423+
from_addr,
424+
size,
425+
dst_align,
426+
src_align,
427+
true,
428+
flags,
429+
);
430+
} else {
431+
field_place.write_cvalue(fx, param);
432+
}
348433
}
349434
}
350435
}

0 commit comments

Comments
 (0)