Skip to content

Rework c_variadic #141980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
}

impl<'a, Ty> TyAndLayout<'a, Ty> {
Expand Down Expand Up @@ -269,6 +271,30 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Ty::is_transparent(self)
}

/// If this method returns `true`, then this type should always have a `PassMode` of
/// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a
/// non-Rustic ABI (this is true for structs annotated with the
/// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute). Currently this is only used by
/// `VaList`, so this only needs to be handled on architectures where `VaList` requires it:
/// specifically there is a `support_architectures` array in the
/// `check_pass_indirectly_in_non_rustic_abis` function in
/// `compiler/rustc_passes/src/check_attr.rs` which lists all the architectures this needs to be
/// handled on. See the comment near the top of `library/core/src/ffi/va_list.rs` for more
/// details.
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
while self.is_transparent()
&& let Some((_, field)) = self.non_1zst_field(cx)
{
self = field;
}
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
}

/// Finds the one field that is not a 1-ZST.
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,17 @@ bitflags! {
const IS_C = 1 << 0;
const IS_SIMD = 1 << 1;
const IS_TRANSPARENT = 1 << 2;
// Internal only for now. If true, don't reorder fields.
// On its own it does not prevent ABI optimizations.
/// Internal only for now. If true, don't reorder fields.
/// On its own it does not prevent ABI optimizations.
const IS_LINEAR = 1 << 3;
// If true, the type's crate has opted into layout randomization.
// Other flags can still inhibit reordering and thus randomization.
// The seed stored in `ReprOptions.field_shuffle_seed`.
/// If true, the type's crate has opted into layout randomization.
/// Other flags can still inhibit reordering and thus randomization.
/// The seed stored in `ReprOptions.field_shuffle_seed`.
const RANDOMIZE_LAYOUT = 1 << 4;
// Any of these flags being set prevent field reordering optimisation.
/// If true, the type is always passed indirectly by non-Rustic ABIs.
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
/// Any of these flags being set prevent field reordering optimisation.
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
| ReprFlags::IS_SIMD.bits()
| ReprFlags::IS_LINEAR.bits();
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,

/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
RustcPassIndirectlyInNonRusticAbis(Span),

/// Represents `#[rustc_skip_during_method_dispatch]`.
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
RustcPassIndirectlyInNonRusticAbis(..) => No,
SkipDuringMethodDispatch { .. } => No,
Stability { .. } => Yes,
TargetFeature(..) => No,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
features
}
}

pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
}
5 changes: 3 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, TargetFeatureParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
Expand Down Expand Up @@ -150,6 +150,7 @@ attribute_parsers!(
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<TrackCallerParser>>,
// tidy-alphabetical-end
];
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
}

// FIXME implement variadics in cranelift
sym::va_copy | sym::va_arg | sym::va_end => {
sym::va_copy | sym::va_arg => {
fx.tcx.dcx().span_fatal(
source_info.span,
"Defining variadic functions is not yet supported by Cranelift",
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return Ok(());
}

sym::va_start => bx.va_start(args[0].immediate()),
sym::va_end => bx.va_end(args[0].immediate()),
sym::size_of_val => {
let tp_ty = fn_args.type_at(0);
let (_, meta) = args[0].val.pointer_parts();
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_ssa/src/traits/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
vtable_byte_offset: u64,
typeid: Self::Metadata,
) -> Self::Value;
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
/// Trait method used to inject `va_start` on the "spoofed" `VaList` in
/// Rust defined C-variadic functions.
fn va_start(&mut self, val: Self::Value) -> Self::Value;
/// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before
/// Trait method used to inject `va_end` on the "spoofed" `VaList` before
/// Rust defined C-variadic functions return.
fn va_end(&mut self, val: Self::Value) -> Self::Value;
}
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
rustc_attr!(
rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::No,
"types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic abis."
),

// Limits:
ungated!(
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,10 +506,6 @@ pub(crate) fn check_intrinsic_type(
)
}

sym::va_start | sym::va_end => {
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
}

sym::va_copy => {
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{cmp, fmt};

use rustc_abi::{
AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo,
PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout,
PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout,
TyAbiInterface, VariantIdx, Variants,
};
use rustc_error_messages::DiagMessage;
Expand Down Expand Up @@ -1138,6 +1138,11 @@ where
fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
}

/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'tcx>) -> bool {
matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().flags.contains(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS))
}
}

/// Calculates whether a function's ABI can unwind or not.
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,14 @@ impl<'tcx> TyCtxt<'tcx> {
flags.insert(ReprFlags::IS_LINEAR);
}

// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
if attr::find_attr!(
self.get_all_attrs(did),
AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
) {
flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS);
}

ReprOptions { int: size, align: max_align, pack: min_pack, flags, field_shuffle_seed }
}

Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ passes_pass_by_value =
`pass_by_value` attribute should be applied to a struct, enum or type alias
.label = is not a struct, enum or type alias

passes_pass_indirectly_not_a_struct =
`#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s
.label = is not a `struct`

passes_proc_macro_bad_sig = {$kind} has incorrect signature

passes_remove_fields =
Expand Down
26 changes: 26 additions & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
&Attribute::Parsed(AttributeKind::PassByValue(attr_span)) => {
self.check_pass_by_value(attr_span, span, target)
}
Attribute::Parsed(AttributeKind::RustcPassIndirectlyInNonRusticAbis(attr_span)) => {
self.check_pass_indirectly_in_non_rustic_abis(*attr_span, span, target);
}
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
Expand Down Expand Up @@ -2106,6 +2109,29 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}

fn check_pass_indirectly_in_non_rustic_abis(
&self,
attr_span: Span,
span: Span,
target: Target,
) {
if target != Target::Struct {
self.dcx().emit_err(errors::PassIndirectlyNotAStruct { attr_span, span });
}
// Currently this attribute is only supported on architectures where it is required for the
// `VaList` type. See the comment near the top of `library/core/src/ffi/va_list.rs` for
// details.
let supported_architectures = ["aarch64", "powerpc", "s390x", "x86_64", "xtensa"];
let arch = &*self.tcx.sess.target.arch;
if !supported_architectures.contains(&arch) {
span_bug!(
attr_span,
"support for `#[rustc_pass_indirectly_in_non_rustic_abis]` on `{arch}` has not yet been implemented; \
see the comment near the top of `library/core/src/ffi/va_list.rs` for details"
);
}
}

fn check_align_value(&self, align: Align, span: Span) {
if align.bytes() > 2_u64.pow(29) {
// for values greater than 2^29, a different error will be emitted, make sure that happens
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,15 @@ pub(crate) struct PassByValue {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(passes_pass_indirectly_not_a_struct)]
pub(crate) struct PassIndirectlyNotAStruct {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(passes_allow_incoherent_impl)]
pub(crate) struct AllowIncoherentImpl {
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,7 @@ symbols! {
rustc_partition_codegened,
rustc_partition_reused,
rustc_pass_by_value,
rustc_pass_indirectly_in_non_rustic_abis,
rustc_peek,
rustc_peek_liveness,
rustc_peek_maybe_init,
Expand Down Expand Up @@ -2309,9 +2310,7 @@ symbols! {
v8plus,
va_arg,
va_copy,
va_end,
va_list,
va_start,
val,
validity,
values,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_target/src/callconv/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ where
// Not touching this...
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}
if !arg.layout.is_aggregate() {
if kind == AbiKind::DarwinPCS {
// On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits,
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,16 @@ impl<'a, Ty> FnAbi<'a, Ty> {
"bpf" => bpf::compute_abi_info(self),
arch => panic!("no lowering implemented for {arch}"),
}
if cfg!(debug_assertions) {
// When debug assertions are enabled, double check that any argument types annotated
// with the `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute are passed
// indirectly.
for arg in &self.args {
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
assert!(matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }));
}
}
}
}

pub fn adjust_for_rust_abi<C>(&mut self, cx: &C)
Expand Down
14 changes: 11 additions & 3 deletions compiler/rustc_target/src/callconv/powerpc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use rustc_abi::TyAbiInterface;

use crate::callconv::{ArgAbi, FnAbi};
use crate::spec::HasTargetSpec;

Expand All @@ -9,7 +11,10 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
}
}

fn classify_arg<Ty>(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) {
fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if arg.is_ignore() {
// powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
if cx.target_spec().os == "linux"
Expand All @@ -20,14 +25,17 @@ fn classify_arg<Ty>(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) {
}
return;
}
if arg.layout.is_aggregate() {
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() {
arg.make_indirect();
} else {
arg.extend_integer_width_to(32);
}
}

pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if !fn_abi.ret.is_ignore() {
classify_ret(&mut fn_abi.ret);
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_target/src/callconv/s390x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ where
}
return;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
return;
}

let size = arg.layout.size;
if size.bits() <= 128 {
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_target/src/callconv/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ where
// Not touching this...
return;
}
if is_arg && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
int_regs = int_regs.saturating_sub(1);
arg.make_indirect();
return;
}
let mut cls_or_mem = classify_arg(cx, arg);

if is_arg {
Expand Down
Loading
Loading