Skip to content

Commit 5785a35

Browse files
committed
Redesign VaList
1 parent 52882f6 commit 5785a35

File tree

16 files changed

+387
-248
lines changed

16 files changed

+387
-248
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
503503
let va_list_arg_idx = self.fn_abi.args.len();
504504
match self.locals[mir::Local::from_usize(1 + va_list_arg_idx)] {
505505
LocalRef::Place(va_list) => {
506-
bx.va_end(va_list.val.llval);
506+
if super::is_va_list_struct_on_stack(bx) {
507+
// Call `va_end` on the `&mut VaListTag` that is stored in `va_list`.
508+
let inner_field = va_list.project_field(bx, 0);
509+
510+
let tag_ptr = bx.load(
511+
bx.backend_type(inner_field.layout),
512+
inner_field.val.llval,
513+
inner_field.layout.align.abi,
514+
);
515+
516+
bx.va_end(tag_ptr);
517+
} else {
518+
// Call `va_end` on the `VaListTag` that is stored in `va_list`.
519+
let tag_ptr = va_list.project_field(bx, 0);
520+
bx.va_end(tag_ptr.val.llval);
521+
}
507522
}
508523
_ => bug!("C-variadic function must have a `VaList` place"),
509524
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::iter;
22

3+
use rustc_ast::Mutability;
4+
use rustc_hir as hir;
35
use rustc_index::IndexVec;
46
use rustc_index::bit_set::DenseBitSet;
57
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -370,6 +372,41 @@ fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
370372
mir
371373
}
372374

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+
373410
/// Produces, for each argument, a `Value` pointing at the
374411
/// argument's value. As arguments are places, these are always
375412
/// indirect.
@@ -436,10 +473,34 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
436473
}
437474

438475
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+
};
441500

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);
443504
}
444505

445506
let arg = &fx.fn_abi.args[idx];

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ language_item_table! {
228228
UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None;
229229

230230
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
231+
VaListTag, sym::va_list_tag, va_list_tag, Target::Struct, GenericRequirement::None;
231232

232233
Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
233234
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ pub(crate) fn check_intrinsic_type(
179179
ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon),
180180
ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv),
181181
]);
182-
let mk_va_list_ty = |mutbl| {
183-
let did = tcx.require_lang_item(LangItem::VaList, Some(span));
182+
let mk_va_list_tag_ty = |mutbl| {
183+
let did = tcx.require_lang_item(LangItem::VaListTag, Some(span));
184184
let region = ty::Region::new_bound(
185185
tcx,
186186
ty::INNERMOST,
@@ -567,16 +567,16 @@ pub(crate) fn check_intrinsic_type(
567567
}
568568

569569
sym::va_start | sym::va_end => {
570-
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
570+
(0, 0, vec![mk_va_list_tag_ty(hir::Mutability::Mut).0], tcx.types.unit)
571571
}
572572

573573
sym::va_copy => {
574-
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
574+
let (va_list_ref_ty, va_list_ty) = mk_va_list_tag_ty(hir::Mutability::Not);
575575
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
576576
(0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit)
577577
}
578578

579-
sym::va_arg => (1, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], param(0)),
579+
sym::va_arg => (1, 0, vec![mk_va_list_tag_ty(hir::Mutability::Mut).0], param(0)),
580580

581581
sym::nontemporal_store => {
582582
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit)

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,7 @@ symbols! {
22752275
va_copy,
22762276
va_end,
22772277
va_list,
2278+
va_list_tag,
22782279
va_start,
22792280
val,
22802281
validity,

library/core/src/ffi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub mod c_str;
2828
issue = "44930",
2929
reason = "the `c_variadic` feature has not been properly tested on all supported platforms"
3030
)]
31-
pub use self::va_list::{VaArgSafe, VaList, VaListImpl};
31+
pub use self::va_list::{VaArgSafe, VaList, VaListTag, va_copy};
3232

3333
#[unstable(
3434
feature = "c_variadic",

0 commit comments

Comments
 (0)