|
1 | 1 | use std::iter;
|
2 | 2 |
|
| 3 | +use rustc_ast::Mutability; |
| 4 | +use rustc_hir as hir; |
3 | 5 | use rustc_index::IndexVec;
|
4 | 6 | use rustc_index::bit_set::DenseBitSet;
|
5 | 7 | use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
@@ -370,6 +372,41 @@ fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
370 | 372 | mir
|
371 | 373 | }
|
372 | 374 |
|
| 375 | +/// Whether on this target, the `VaList` is a reference to a struct on the stack. |
| 376 | +/// |
| 377 | +/// If this function returns `true`, the `core` crate defines a `VaListTag` struct |
| 378 | +/// matching the varargs ABI for this target. |
| 379 | +/// |
| 380 | +/// In other cases, the `VaList` is an opaque pointer. Generally this is just a pointer to the |
| 381 | +/// caller's stack where the variadic arguments are stored sequentially. |
| 382 | +fn is_va_list_struct_on_stack<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> bool { |
| 383 | + let did = bx.tcx().require_lang_item(hir::LangItem::VaList, None); |
| 384 | + let ty = bx.tcx().type_of(did).instantiate_identity(); |
| 385 | + |
| 386 | + // Check how `VaList` is implemented for the current target. It can be either: |
| 387 | + // |
| 388 | + // - a reference to a value on the stack |
| 389 | + // - a struct wrapping a pointer |
| 390 | + let Some(adt_def) = ty.ty_adt_def() else { bug!("invalid `VaList`") }; |
| 391 | + let variant = adt_def.non_enum_variant(); |
| 392 | + |
| 393 | + if variant.fields.len() != 1 { |
| 394 | + bug!("`VaList` must have exactly 1 field") |
| 395 | + } |
| 396 | + let field = variant.fields.iter().next().unwrap(); |
| 397 | + let field_ty = bx.tcx().type_of(field.did).instantiate_identity(); |
| 398 | + |
| 399 | + if field_ty.is_adt() { |
| 400 | + return false; |
| 401 | + } |
| 402 | + |
| 403 | + if let Some(Mutability::Mut) = field_ty.ref_mutability() { |
| 404 | + return true; |
| 405 | + } |
| 406 | + |
| 407 | + bug!("invalid `VaList` field type") |
| 408 | +} |
| 409 | + |
373 | 410 | /// Produces, for each argument, a `Value` pointing at the
|
374 | 411 | /// argument's value. As arguments are places, these are always
|
375 | 412 | /// indirect.
|
@@ -436,10 +473,34 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
436 | 473 | }
|
437 | 474 |
|
438 | 475 | if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() {
|
439 |
| - let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); |
440 |
| - bx.va_start(va_list.val.llval); |
| 476 | + use rustc_hir::LangItem; |
| 477 | + |
| 478 | + let va_list_tag_ty = { |
| 479 | + let did = bx.tcx().require_lang_item(LangItem::VaListTag, None); |
| 480 | + let ty = bx.tcx().type_of(did).instantiate_identity(); |
| 481 | + bx.tcx().normalize_erasing_regions(fx.cx.typing_env(), ty) |
| 482 | + }; |
| 483 | + let va_list_tag_layout = bx.layout_of(va_list_tag_ty); |
| 484 | + |
| 485 | + // Construct the `VaListTag` on the stack. |
| 486 | + let va_list_tag = PlaceRef::alloca(bx, va_list_tag_layout); |
| 487 | + |
| 488 | + // Initialize the alloca. |
| 489 | + bx.va_start(va_list_tag.val.llval); |
| 490 | + |
| 491 | + let va_list_tag = if is_va_list_struct_on_stack(bx) { |
| 492 | + va_list_tag.val.llval |
| 493 | + } else { |
| 494 | + bx.load( |
| 495 | + bx.backend_type(va_list_tag_layout), |
| 496 | + va_list_tag.val.llval, |
| 497 | + va_list_tag.layout.align.abi, |
| 498 | + ) |
| 499 | + }; |
441 | 500 |
|
442 |
| - return LocalRef::Place(va_list); |
| 501 | + let tmp = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); |
| 502 | + bx.store_to_place(va_list_tag, tmp.val); |
| 503 | + return LocalRef::Place(tmp); |
443 | 504 | }
|
444 | 505 |
|
445 | 506 | let arg = &fx.fn_abi.args[idx];
|
|
0 commit comments