Skip to content

Commit dc64c96

Browse files
committed
Redesign VaList
1 parent 6268d0a commit dc64c96

File tree

16 files changed

+388
-248
lines changed

16 files changed

+388
-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: 65 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;
@@ -371,6 +373,41 @@ fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
371373
mir
372374
}
373375

376+
/// Whether on this target, the `VaList` is a reference to a struct on the stack.
377+
///
378+
/// If this function returns `true`, the `core` crate defines a `VaListTag` struct
379+
/// matching the varargs ABI for this target.
380+
///
381+
/// In other cases, the `VaList` is an opaque pointer. Generally this is just a pointer to the
382+
/// caller's stack where the variadic arguments are stored sequentially.
383+
fn is_va_list_struct_on_stack<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> bool {
384+
let did = bx.tcx().require_lang_item(hir::LangItem::VaList, rustc_span::Span::default());
385+
let ty = bx.tcx().type_of(did).instantiate_identity();
386+
387+
// Check how `VaList` is implemented for the current target. It can be either:
388+
//
389+
// - a reference to a value on the stack
390+
// - a struct wrapping a pointer
391+
let Some(adt_def) = ty.ty_adt_def() else { bug!("invalid `VaList`") };
392+
let variant = adt_def.non_enum_variant();
393+
394+
if variant.fields.len() != 1 {
395+
bug!("`VaList` must have exactly 1 field")
396+
}
397+
let field = variant.fields.iter().next().unwrap();
398+
let field_ty = bx.tcx().type_of(field.did).instantiate_identity();
399+
400+
if field_ty.is_adt() {
401+
return false;
402+
}
403+
404+
if let Some(Mutability::Mut) = field_ty.ref_mutability() {
405+
return true;
406+
}
407+
408+
bug!("invalid `VaList` field type")
409+
}
410+
374411
/// Produces, for each argument, a `Value` pointing at the
375412
/// argument's value. As arguments are places, these are always
376413
/// indirect.
@@ -437,10 +474,35 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
437474
}
438475

439476
if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() {
440-
let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty));
441-
bx.va_start(va_list.val.llval);
477+
use rustc_hir::LangItem;
478+
479+
let va_list_tag_ty = {
480+
let did =
481+
bx.tcx().require_lang_item(LangItem::VaListTag, arg_decl.source_info.span);
482+
let ty = bx.tcx().type_of(did).instantiate_identity();
483+
bx.tcx().normalize_erasing_regions(fx.cx.typing_env(), ty)
484+
};
485+
let va_list_tag_layout = bx.layout_of(va_list_tag_ty);
486+
487+
// Construct the `VaListTag` on the stack.
488+
let va_list_tag = PlaceRef::alloca(bx, va_list_tag_layout);
489+
490+
// Initialize the alloca.
491+
bx.va_start(va_list_tag.val.llval);
492+
493+
let va_list_tag = if is_va_list_struct_on_stack(bx) {
494+
va_list_tag.val.llval
495+
} else {
496+
bx.load(
497+
bx.backend_type(va_list_tag_layout),
498+
va_list_tag.val.llval,
499+
va_list_tag.layout.align.abi,
500+
)
501+
};
442502

443-
return LocalRef::Place(va_list);
503+
let tmp = PlaceRef::alloca(bx, bx.layout_of(arg_ty));
504+
bx.store_to_place(va_list_tag, tmp.val);
505+
return LocalRef::Place(tmp);
444506
}
445507

446508
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
@@ -230,6 +230,7 @@ language_item_table! {
230230
UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None;
231231

232232
VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
233+
VaListTag, sym::va_list_tag, va_list_tag, Target::Struct, GenericRequirement::None;
233234

234235
Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
235236
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
@@ -175,8 +175,8 @@ pub(crate) fn check_intrinsic_type(
175175
ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon),
176176
ty::BoundVariableKind::Region(ty::BoundRegionKind::ClosureEnv),
177177
]);
178-
let mk_va_list_ty = |mutbl| {
179-
let did = tcx.require_lang_item(LangItem::VaList, span);
178+
let mk_va_list_tag_ty = |mutbl| {
179+
let did = tcx.require_lang_item(LangItem::VaListTag, span);
180180
let region = ty::Region::new_bound(
181181
tcx,
182182
ty::INNERMOST,
@@ -507,16 +507,16 @@ pub(crate) fn check_intrinsic_type(
507507
}
508508

509509
sym::va_start | sym::va_end => {
510-
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
510+
(0, 0, vec![mk_va_list_tag_ty(hir::Mutability::Mut).0], tcx.types.unit)
511511
}
512512

513513
sym::va_copy => {
514-
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
514+
let (va_list_ref_ty, va_list_ty) = mk_va_list_tag_ty(hir::Mutability::Not);
515515
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
516516
(0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit)
517517
}
518518

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

521521
sym::nontemporal_store => {
522522
(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
@@ -2312,6 +2312,7 @@ symbols! {
23122312
va_copy,
23132313
va_end,
23142314
va_list,
2315+
va_list_tag,
23152316
va_start,
23162317
val,
23172318
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)