Skip to content

Commit 80171db

Browse files
committed
codegen: add noundef for small immediate arguments with no padding
Add `noundef` metadata for arguments that: 1. are passed as immediates 2. fit in target pointer width 3. has no padding
1 parent d0438b4 commit 80171db

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

compiler/rustc_abi/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ pub struct ReprOptions {
101101
}
102102

103103
impl ReprOptions {
104+
pub fn rust(&self) -> bool {
105+
!(self.simd() || self.c() || self.packed() || self.transparent() || self.linear())
106+
}
107+
104108
#[inline]
105109
pub fn simd(&self) -> bool {
106110
self.flags.contains(ReprFlags::IS_SIMD)

compiler/rustc_ty_utils/src/abi.rs

+53-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt};
99
use rustc_session::config::OptLevel;
1010
use rustc_span::def_id::DefId;
1111
use rustc_target::abi::call::{
12-
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind,
13-
RiscvInterruptKind,
12+
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, Conv, FnAbi, PassMode, Reg,
13+
RegKind, RiscvInterruptKind,
1414
};
1515
use rustc_target::abi::*;
1616
use rustc_target::spec::abi::Abi as SpecAbi;
@@ -787,6 +787,19 @@ fn fn_abi_adjust_for_abi<'tcx>(
787787
// an LLVM aggregate type for this leads to bad optimizations,
788788
// so we pick an appropriately sized integer type instead.
789789
arg.cast_to(Reg { kind: RegKind::Integer, size });
790+
791+
// Now we see if we are allowed to annotate the small immediate argument with
792+
// `noundef`. This is only legal for small aggregates that do not have padding. For
793+
// instance, all unions are allowed `undef` so we must not annotate union (or
794+
// anything that contain unions) immediates with `noundef`.
795+
if can_annotate_small_immediate_argument_with_noundef(cx, arg.layout) {
796+
// Fixup arg attribute with `noundef`.
797+
let PassMode::Cast { ref mut cast, .. } = &mut arg.mode else {
798+
bug!("this cannot fail because of the previous cast_to `Reg`");
799+
};
800+
let box CastTarget { ref mut attrs, .. } = cast;
801+
attrs.set(ArgAttribute::NoUndef);
802+
}
790803
}
791804

792805
// If we deduced that this parameter was read-only, add that to the attribute list now.
@@ -822,6 +835,44 @@ fn fn_abi_adjust_for_abi<'tcx>(
822835
Ok(())
823836
}
824837

838+
fn can_annotate_small_immediate_argument_with_noundef<'tcx>(
839+
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
840+
outermost_layout: TyAndLayout<'tcx>,
841+
) -> bool {
842+
fn allowed_repr_rust<'tcx>(candidate_ty: Ty<'tcx>) -> bool {
843+
match candidate_ty.kind() {
844+
ty::Adt(def, _) if def.repr().rust() && !def.is_union() => true,
845+
ty::Array(elem_ty, _) if allowed_repr_rust(*elem_ty) => true,
846+
t => t.is_primitive(),
847+
}
848+
}
849+
850+
fn is_transparent_or_rust_wrapper<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
851+
return layout.is_transparent::<LayoutCx<'tcx, TyCtxt<'tcx>>>()
852+
|| allowed_repr_rust(layout.ty);
853+
}
854+
855+
if outermost_layout.ty.is_union() {
856+
return false;
857+
}
858+
859+
let mut innermost_layout = outermost_layout;
860+
// Recursively peel away wrapper layers.
861+
while is_transparent_or_rust_wrapper(innermost_layout) {
862+
let Some((_, layout)) = innermost_layout.non_1zst_field(cx) else {
863+
break;
864+
};
865+
866+
if layout.ty.is_union() {
867+
return false;
868+
}
869+
870+
innermost_layout = layout;
871+
}
872+
873+
allowed_repr_rust(innermost_layout.ty)
874+
}
875+
825876
#[tracing::instrument(level = "debug", skip(cx))]
826877
fn make_thin_self_ptr<'tcx>(
827878
cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),

0 commit comments

Comments
 (0)