Skip to content

Commit 70a5d38

Browse files
committed
Add #[rustc_pass_indirectly_in_non_rustic_abis]
1 parent c83e217 commit 70a5d38

29 files changed

+627
-21
lines changed

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
172172
fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
173173
fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
174174
fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
175+
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
176+
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool;
175177
}
176178

177179
impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -269,6 +271,30 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
269271
Ty::is_transparent(self)
270272
}
271273

274+
/// If this method returns `true`, then this type should always have a `PassMode` of
275+
/// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a
276+
/// non-Rustic ABI (this is true for structs annotated with the
277+
/// `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute). Currently this is only used by
278+
/// `VaList`, so this only needs to be handled on architectures where `VaList` requires it:
279+
/// specifically there is a `support_architectures` array in the
280+
/// `check_pass_indirectly_in_non_rustic_abis` function in
281+
/// `compiler/rustc_passes/src/check_attr.rs` which lists all the architectures this needs to be
282+
/// handled on. See the comment near the top of `library/core/src/ffi/va_list.rs` for more
283+
/// details.
284+
///
285+
/// This function handles transparent types automatically.
286+
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
287+
where
288+
Ty: TyAbiInterface<'a, C> + Copy,
289+
{
290+
while self.is_transparent()
291+
&& let Some((_, field)) = self.non_1zst_field(cx)
292+
{
293+
self = field;
294+
}
295+
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
296+
}
297+
272298
/// Finds the one field that is not a 1-ZST.
273299
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
274300
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>

compiler/rustc_abi/src/lib.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,17 @@ bitflags! {
8686
const IS_C = 1 << 0;
8787
const IS_SIMD = 1 << 1;
8888
const IS_TRANSPARENT = 1 << 2;
89-
// Internal only for now. If true, don't reorder fields.
90-
// On its own it does not prevent ABI optimizations.
89+
/// Internal only for now. If true, don't reorder fields.
90+
/// On its own it does not prevent ABI optimizations.
9191
const IS_LINEAR = 1 << 3;
92-
// If true, the type's crate has opted into layout randomization.
93-
// Other flags can still inhibit reordering and thus randomization.
94-
// The seed stored in `ReprOptions.field_shuffle_seed`.
92+
/// If true, the type's crate has opted into layout randomization.
93+
/// Other flags can still inhibit reordering and thus randomization.
94+
/// The seed stored in `ReprOptions.field_shuffle_seed`.
9595
const RANDOMIZE_LAYOUT = 1 << 4;
96-
// Any of these flags being set prevent field reordering optimisation.
96+
/// If true, the type is always passed indirectly by non-Rustic ABIs.
97+
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
98+
const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5;
99+
/// Any of these flags being set prevent field reordering optimisation.
97100
const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits()
98101
| ReprFlags::IS_SIMD.bits()
99102
| ReprFlags::IS_LINEAR.bits();

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ pub enum AttributeKind {
315315
/// Represents `#[rustc_object_lifetime_default]`.
316316
RustcObjectLifetimeDefault,
317317

318+
/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
319+
RustcPassIndirectlyInNonRusticAbis(Span),
320+
318321
/// Represents `#[rustc_skip_during_method_dispatch]`.
319322
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
320323

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl AttributeKind {
4545
RustcLayoutScalarValidRangeEnd(..) => Yes,
4646
RustcLayoutScalarValidRangeStart(..) => Yes,
4747
RustcObjectLifetimeDefault => No,
48+
RustcPassIndirectlyInNonRusticAbis(..) => No,
4849
SkipDuringMethodDispatch { .. } => No,
4950
Stability { .. } => Yes,
5051
TargetFeature(..) => No,

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
330330
features
331331
}
332332
}
333+
334+
pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
335+
impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
336+
const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
337+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
338+
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
339+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1616

1717
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
1818
use crate::attributes::codegen_attrs::{
19-
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
20-
TrackCallerParser, UsedParser,
19+
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
20+
RustcPassIndirectlyInNonRusticAbisParser, TargetFeatureParser, TrackCallerParser, UsedParser,
2121
};
2222
use crate::attributes::confusables::ConfusablesParser;
2323
use crate::attributes::deprecation::DeprecationParser;
@@ -150,6 +150,7 @@ attribute_parsers!(
150150
Single<WithoutArgs<NonExhaustiveParser>>,
151151
Single<WithoutArgs<PassByValueParser>>,
152152
Single<WithoutArgs<PubTransparentParser>>,
153+
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
153154
Single<WithoutArgs<TrackCallerParser>>,
154155
// tidy-alphabetical-end
155156
];

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
497497
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
498498
ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
499499
ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
500+
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
501+
rustc_attr!(
502+
rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing,
503+
EncodeCrossCrate::No,
504+
"types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic abis."
505+
),
500506

501507
// Limits:
502508
ungated!(

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{cmp, fmt};
33

44
use rustc_abi::{
55
AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo,
6-
PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout,
6+
PointerKind, Primitive, ReprFlags, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout,
77
TyAbiInterface, VariantIdx, Variants,
88
};
99
use rustc_error_messages::DiagMessage;
@@ -1138,6 +1138,11 @@ where
11381138
fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
11391139
matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
11401140
}
1141+
1142+
/// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details.
1143+
fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'tcx>) -> bool {
1144+
matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().flags.contains(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS))
1145+
}
11411146
}
11421147

11431148
/// Calculates whether a function's ABI can unwind or not.

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,14 @@ impl<'tcx> TyCtxt<'tcx> {
15891589
flags.insert(ReprFlags::IS_LINEAR);
15901590
}
15911591

1592+
// See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details.
1593+
if attr::find_attr!(
1594+
self.get_all_attrs(did),
1595+
AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
1596+
) {
1597+
flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS);
1598+
}
1599+
15921600
ReprOptions { int: size, align: max_align, pack: min_pack, flags, field_shuffle_seed }
15931601
}
15941602

compiler/rustc_passes/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,14 @@ passes_pass_by_value =
579579
`pass_by_value` attribute should be applied to a struct, enum or type alias
580580
.label = is not a struct, enum or type alias
581581
582+
passes_pass_indirectly_not_a_struct =
583+
`#[rustc_pass_indirectly_in_non_rustic_abis]` can only be applied to `struct`s
584+
.label = is not a `struct`
585+
586+
passes_pass_indirectly_unsupported_arch =
587+
support for `#[rustc_pass_indirectly_in_non_rustic_abis]` on `{$arch}` has not yet been implemented
588+
.help = see the comment near the top of `library/core/src/ffi/va_list.rs` for details
589+
582590
passes_proc_macro_bad_sig = {$kind} has incorrect signature
583591
584592
passes_remove_fields =

compiler/rustc_passes/src/check_attr.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
230230
&Attribute::Parsed(AttributeKind::PassByValue(attr_span)) => {
231231
self.check_pass_by_value(attr_span, span, target)
232232
}
233+
Attribute::Parsed(AttributeKind::RustcPassIndirectlyInNonRusticAbis(attr_span)) => {
234+
self.check_pass_indirectly_in_non_rustic_abis(*attr_span, span, target);
235+
}
233236
Attribute::Unparsed(attr_item) => {
234237
style = Some(attr_item.style);
235238
match attr.path().as_slice() {
@@ -2106,6 +2109,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
21062109
}
21072110
}
21082111

2112+
fn check_pass_indirectly_in_non_rustic_abis(
2113+
&self,
2114+
attr_span: Span,
2115+
span: Span,
2116+
target: Target,
2117+
) {
2118+
match target {
2119+
Target::Struct => {}
2120+
_ => {
2121+
self.dcx().emit_err(errors::PassIndirectlyNotAStruct { attr_span, span });
2122+
}
2123+
}
2124+
// Currently this attribute is only supported on architectures where it is required for the
2125+
// `VaList` type. See the comment near the top of `library/core/src/ffi/va_list.rs` for
2126+
// details.
2127+
let supported_architectures = ["aarch64", "powerpc", "s390x", "x86_64", "xtensa"];
2128+
let arch = &*self.tcx.sess.target.arch;
2129+
if !supported_architectures.contains(&arch) {
2130+
self.dcx().emit_err(errors::PassIndirectlyUnsupportedArch { span: attr_span, arch });
2131+
}
2132+
}
2133+
21092134
fn check_align_value(&self, align: Align, span: Span) {
21102135
if align.bytes() > 2_u64.pow(29) {
21112136
// for values greater than 2^29, a different error will be emitted, make sure that happens

compiler/rustc_passes/src/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,24 @@ pub(crate) struct PassByValue {
425425
pub span: Span,
426426
}
427427

428+
#[derive(Diagnostic)]
429+
#[diag(passes_pass_indirectly_not_a_struct)]
430+
pub(crate) struct PassIndirectlyNotAStruct {
431+
#[primary_span]
432+
pub attr_span: Span,
433+
#[label]
434+
pub span: Span,
435+
}
436+
437+
#[derive(Diagnostic)]
438+
#[diag(passes_pass_indirectly_unsupported_arch)]
439+
#[help]
440+
pub(crate) struct PassIndirectlyUnsupportedArch<'a> {
441+
#[primary_span]
442+
pub span: Span,
443+
pub arch: &'a str,
444+
}
445+
428446
#[derive(Diagnostic)]
429447
#[diag(passes_allow_incoherent_impl)]
430448
pub(crate) struct AllowIncoherentImpl {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,7 @@ symbols! {
18821882
rustc_partition_codegened,
18831883
rustc_partition_reused,
18841884
rustc_pass_by_value,
1885+
rustc_pass_indirectly_in_non_rustic_abis,
18851886
rustc_peek,
18861887
rustc_peek_liveness,
18871888
rustc_peek_maybe_init,

compiler/rustc_target/src/callconv/aarch64.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ where
114114
// Not touching this...
115115
return;
116116
}
117+
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
118+
arg.make_indirect();
119+
return;
120+
}
117121
if !arg.layout.is_aggregate() {
118122
if kind == AbiKind::DarwinPCS {
119123
// On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits,

compiler/rustc_target/src/callconv/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,16 @@ impl<'a, Ty> FnAbi<'a, Ty> {
703703
"bpf" => bpf::compute_abi_info(self),
704704
arch => panic!("no lowering implemented for {arch}"),
705705
}
706+
if cfg!(debug_assertions) {
707+
// When debug assertions are enabled, double check that any argument types annotated
708+
// with the `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute are passed
709+
// indirectly.
710+
for arg in &self.args {
711+
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
712+
assert!(matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }));
713+
}
714+
}
715+
}
706716
}
707717

708718
pub fn adjust_for_rust_abi<C>(&mut self, cx: &C)

compiler/rustc_target/src/callconv/powerpc.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use rustc_abi::TyAbiInterface;
2+
13
use crate::callconv::{ArgAbi, FnAbi};
24
use crate::spec::HasTargetSpec;
35

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

12-
fn classify_arg<Ty>(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) {
14+
fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
15+
where
16+
Ty: TyAbiInterface<'a, C> + Copy,
17+
{
1318
if arg.is_ignore() {
1419
// powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
1520
if cx.target_spec().os == "linux"
@@ -20,14 +25,17 @@ fn classify_arg<Ty>(cx: &impl HasTargetSpec, arg: &mut ArgAbi<'_, Ty>) {
2025
}
2126
return;
2227
}
23-
if arg.layout.is_aggregate() {
28+
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() {
2429
arg.make_indirect();
2530
} else {
2631
arg.extend_integer_width_to(32);
2732
}
2833
}
2934

30-
pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
35+
pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
36+
where
37+
Ty: TyAbiInterface<'a, C> + Copy,
38+
{
3139
if !fn_abi.ret.is_ignore() {
3240
classify_ret(&mut fn_abi.ret);
3341
}

compiler/rustc_target/src/callconv/s390x.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ where
3737
}
3838
return;
3939
}
40+
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
41+
arg.make_indirect();
42+
return;
43+
}
4044

4145
let size = arg.layout.size;
4246
if size.bits() <= 128 {

compiler/rustc_target/src/callconv/x86_64.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ where
186186
// Not touching this...
187187
return;
188188
}
189+
if is_arg && arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
190+
int_regs = int_regs.saturating_sub(1);
191+
arg.make_indirect();
192+
return;
193+
}
189194
let mut cls_or_mem = classify_arg(cx, arg);
190195

191196
if is_arg {

compiler/rustc_target/src/callconv/x86_win64.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size};
1+
use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size, TyAbiInterface};
22

33
use crate::callconv::{ArgAbi, FnAbi, Reg};
44
use crate::spec::{HasTargetSpec, RustcAbi};
55

66
// Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing
77

8-
pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
8+
pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
9+
where
10+
Ty: TyAbiInterface<'a, C> + Copy,
11+
{
912
let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| {
1013
match a.layout.backend_repr {
1114
BackendRepr::Memory { sized: false } => {}
@@ -59,6 +62,10 @@ pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'
5962
arg.make_indirect_from_ignore();
6063
continue;
6164
}
65+
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
66+
arg.make_indirect();
67+
continue;
68+
}
6269
fixup(arg, false);
6370
}
6471
// FIXME: We should likely also do something about ZST return types, similar to above.

0 commit comments

Comments
 (0)