2 use rustc_hir::lang_items::LangItem;
3 use rustc_middle::ty::layout::{
4 fn_can_unwind, FnAbiError, HasParamEnv, HasTyCtxt, LayoutCx, LayoutOf, TyAndLayout,
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_session::config::OptLevel;
8 use rustc_span::def_id::DefId;
9 use rustc_target::abi::call::{
10 ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind,
12 use rustc_target::abi::*;
13 use rustc_target::spec::abi::Abi as SpecAbi;
17 pub fn provide(providers: &mut ty::query::Providers) {
18 *providers = ty::query::Providers { fn_abi_of_fn_ptr, fn_abi_of_instance, ..*providers };
21 // NOTE(eddyb) this is private to avoid using it from outside of
22 // `fn_abi_of_instance` - any other uses are either too high-level
23 // for `Instance` (e.g. typeck would use `Ty::fn_sig` instead),
24 // or should go through `FnAbi` instead, to avoid losing any
25 // adjustments `fn_abi_of_instance` might be performing.
26 #[tracing::instrument(level = "debug", skip(tcx, param_env))]
27 fn fn_sig_for_fn_abi<'tcx>(
29 instance: ty::Instance<'tcx>,
30 param_env: ty::ParamEnv<'tcx>,
31 ) -> ty::PolyFnSig<'tcx> {
32 let ty = instance.ty(tcx, param_env);
35 // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
36 // parameters unused if they show up in the signature, but not in the `mir::Body`
37 // (i.e. due to being inside a projection that got normalized, see
38 // `tests/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
39 // track of a polymorphization `ParamEnv` to allow normalizing later.
41 // We normalize the `fn_sig` again after substituting at a later point.
42 let mut sig = match *ty.kind() {
43 ty::FnDef(def_id, substs) => tcx
46 tcx.normalize_erasing_regions(tcx.param_env(def_id), fn_sig)
52 if let ty::InstanceDef::VTableShim(..) = instance.def {
53 // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
54 sig = sig.map_bound(|mut sig| {
55 let mut inputs_and_output = sig.inputs_and_output.to_vec();
56 inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
57 sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
63 ty::Closure(def_id, substs) => {
64 let sig = substs.as_closure().sig();
66 let bound_vars = tcx.mk_bound_variable_kinds(
67 sig.bound_vars().iter().chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
69 let br = ty::BoundRegion {
70 var: ty::BoundVar::from_usize(bound_vars.len() - 1),
71 kind: ty::BoundRegionKind::BrEnv,
73 let env_region = ty::ReLateBound(ty::INNERMOST, br);
74 let env_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
76 let sig = sig.skip_binder();
77 ty::Binder::bind_with_vars(
79 iter::once(env_ty).chain(sig.inputs().iter().cloned()),
88 ty::Generator(did, substs, _) => {
89 let sig = substs.as_generator().poly_sig();
91 let bound_vars = tcx.mk_bound_variable_kinds(
92 sig.bound_vars().iter().chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
94 let br = ty::BoundRegion {
95 var: ty::BoundVar::from_usize(bound_vars.len() - 1),
96 kind: ty::BoundRegionKind::BrEnv,
98 let env_region = ty::ReLateBound(ty::INNERMOST, br);
99 let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
101 let pin_did = tcx.require_lang_item(LangItem::Pin, None);
102 let pin_adt_ref = tcx.adt_def(pin_did);
103 let pin_substs = tcx.intern_substs(&[env_ty.into()]);
104 let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
106 let sig = sig.skip_binder();
107 // The `FnSig` and the `ret_ty` here is for a generators main
108 // `Generator::resume(...) -> GeneratorState` function in case we
109 // have an ordinary generator, or the `Future::poll(...) -> Poll`
110 // function in case this is a special generator backing an async construct.
111 let (resume_ty, ret_ty) = if tcx.generator_is_async(did) {
112 // The signature should be `Future::poll(_, &mut Context<'_>) -> Poll<Output>`
113 let poll_did = tcx.require_lang_item(LangItem::Poll, None);
114 let poll_adt_ref = tcx.adt_def(poll_did);
115 let poll_substs = tcx.intern_substs(&[sig.return_ty.into()]);
116 let ret_ty = tcx.mk_adt(poll_adt_ref, poll_substs);
118 // We have to replace the `ResumeTy` that is used for type and borrow checking
119 // with `&mut Context<'_>` which is used in codegen.
120 #[cfg(debug_assertions)]
122 if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
124 tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
125 assert_eq!(*resume_ty_adt, expected_adt);
127 panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
130 let context_mut_ref = tcx.mk_task_context();
132 (context_mut_ref, ret_ty)
134 // The signature should be `Generator::resume(_, Resume) -> GeneratorState<Yield, Return>`
135 let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
136 let state_adt_ref = tcx.adt_def(state_did);
137 let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
138 let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
140 (sig.resume_ty, ret_ty)
143 ty::Binder::bind_with_vars(
145 [env_ty, resume_ty].iter(),
148 hir::Unsafety::Normal,
149 rustc_target::spec::abi::Abi::Rust,
154 _ => bug!("unexpected type {:?} in Instance::fn_sig", ty),
159 fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
160 use rustc_target::spec::abi::Abi::*;
161 match tcx.sess.target.adjust_abi(abi) {
162 RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
163 RustCold => Conv::RustCold,
165 // It's the ABI's job to select this, not ours.
166 System { .. } => bug!("system abi should be selected elsewhere"),
167 EfiApi => bug!("eficall abi should be selected elsewhere"),
169 Stdcall { .. } => Conv::X86Stdcall,
170 Fastcall { .. } => Conv::X86Fastcall,
171 Vectorcall { .. } => Conv::X86VectorCall,
172 Thiscall { .. } => Conv::X86ThisCall,
174 Unadjusted => Conv::C,
175 Win64 { .. } => Conv::X86_64Win64,
176 SysV64 { .. } => Conv::X86_64SysV,
177 Aapcs { .. } => Conv::ArmAapcs,
178 CCmseNonSecureCall => Conv::CCmseNonSecureCall,
179 PtxKernel => Conv::PtxKernel,
180 Msp430Interrupt => Conv::Msp430Intr,
181 X86Interrupt => Conv::X86Intr,
182 AmdGpuKernel => Conv::AmdGpuKernel,
183 AvrInterrupt => Conv::AvrInterrupt,
184 AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt,
187 // These API constants ought to be more specific...
188 Cdecl { .. } => Conv::C,
192 fn fn_abi_of_fn_ptr<'tcx>(
194 query: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
195 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
196 let (param_env, (sig, extra_args)) = query.into_parts();
198 let cx = LayoutCx { tcx, param_env };
199 fn_abi_new_uncached(&cx, sig, extra_args, None, None, false)
202 fn fn_abi_of_instance<'tcx>(
204 query: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
205 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
206 let (param_env, (instance, extra_args)) = query.into_parts();
208 let sig = fn_sig_for_fn_abi(tcx, instance, param_env);
210 let caller_location = if instance.def.requires_caller_location(tcx) {
211 Some(tcx.caller_location_ty())
217 &LayoutCx { tcx, param_env },
221 Some(instance.def_id()),
222 matches!(instance.def, ty::InstanceDef::Virtual(..)),
226 // Handle safe Rust thin and fat pointers.
227 fn adjust_for_rust_scalar<'tcx>(
228 cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
229 attrs: &mut ArgAttributes,
231 layout: TyAndLayout<'tcx>,
235 // Booleans are always a noundef i1 that needs to be zero-extended.
236 if scalar.is_bool() {
237 attrs.ext(ArgExtension::Zext);
238 attrs.set(ArgAttribute::NoUndef);
242 if !scalar.is_uninit_valid() {
243 attrs.set(ArgAttribute::NoUndef);
246 // Only pointer types handled below.
247 let Scalar::Initialized { value: Pointer(_), valid_range} = scalar else { return };
249 if !valid_range.contains(0) {
250 attrs.set(ArgAttribute::NonNull);
253 if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
254 if let Some(kind) = pointee.safe {
255 attrs.pointee_align = Some(pointee.align);
257 // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
258 // for the entire duration of the function as they can be deallocated
259 // at any time. Same for shared mutable references. If LLVM had a
260 // way to say "dereferenceable on entry" we could use it here.
261 attrs.pointee_size = match kind {
262 PointerKind::UniqueBorrowed
263 | PointerKind::UniqueBorrowedPinned
264 | PointerKind::Frozen => pointee.size,
265 PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
268 // The aliasing rules for `Box<T>` are still not decided, but currently we emit
269 // `noalias` for it. This can be turned off using an unstable flag.
270 // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
271 let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias;
273 // LLVM prior to version 12 had known miscompiles in the presence of noalias attributes
274 // (see #54878), so it was conditionally disabled, but we don't support earlier
275 // versions at all anymore. We still support turning it off using -Zmutable-noalias.
276 let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
278 // `&mut` pointer parameters never alias other parameters,
279 // or mutable global data
281 // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
282 // and can be marked as both `readonly` and `noalias`, as
283 // LLVM's definition of `noalias` is based solely on memory
284 // dependencies rather than pointer equality
285 let no_alias = match kind {
286 PointerKind::SharedMutable | PointerKind::UniqueBorrowedPinned => false,
287 PointerKind::UniqueBorrowed => noalias_mut_ref,
288 PointerKind::UniqueOwned => noalias_for_box,
289 PointerKind::Frozen => true,
291 // We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
292 // (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).
293 if no_alias && !is_return {
294 attrs.set(ArgAttribute::NoAlias);
297 if kind == PointerKind::Frozen && !is_return {
298 attrs.set(ArgAttribute::ReadOnly);
304 // FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
305 // arguments of this method, into a separate `struct`.
306 #[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))]
307 fn fn_abi_new_uncached<'tcx>(
308 cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
309 sig: ty::PolyFnSig<'tcx>,
310 extra_args: &[Ty<'tcx>],
311 caller_location: Option<Ty<'tcx>>,
312 fn_def_id: Option<DefId>,
313 // FIXME(eddyb) replace this with something typed, like an `enum`.
314 force_thin_self_ptr: bool,
315 ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
316 let sig = cx.tcx.normalize_erasing_late_bound_regions(cx.param_env, sig);
318 let conv = conv_from_spec_abi(cx.tcx(), sig.abi);
320 let mut inputs = sig.inputs();
321 let extra_args = if sig.abi == RustCall {
322 assert!(!sig.c_variadic && extra_args.is_empty());
324 if let Some(input) = sig.inputs().last() {
325 if let ty::Tuple(tupled_arguments) = input.kind() {
326 inputs = &sig.inputs()[0..sig.inputs().len() - 1];
330 "argument to function with \"rust-call\" ABI \
336 "argument to function with \"rust-call\" ABI \
341 assert!(sig.c_variadic || extra_args.is_empty());
345 let target = &cx.tcx.sess.target;
346 let target_env_gnu_like = matches!(&target.env[..], "gnu" | "musl" | "uclibc");
347 let win_x64_gnu = target.os == "windows" && target.arch == "x86_64" && target.env == "gnu";
348 let linux_s390x_gnu_like =
349 target.os == "linux" && target.arch == "s390x" && target_env_gnu_like;
350 let linux_sparc64_gnu_like =
351 target.os == "linux" && target.arch == "sparc64" && target_env_gnu_like;
352 let linux_powerpc_gnu_like =
353 target.os == "linux" && target.arch == "powerpc" && target_env_gnu_like;
355 let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
357 let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
358 let span = tracing::debug_span!("arg_of");
359 let _entered = span.enter();
360 let is_return = arg_idx.is_none();
362 let layout = cx.layout_of(ty)?;
363 let layout = if force_thin_self_ptr && arg_idx == Some(0) {
364 // Don't pass the vtable, it's not an argument of the virtual fn.
365 // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
366 // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen
367 make_thin_self_ptr(cx, layout)
372 let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| {
373 let mut attrs = ArgAttributes::new();
374 adjust_for_rust_scalar(*cx, &mut attrs, scalar, *layout, offset, is_return);
378 if arg.layout.is_zst() {
379 // For some forsaken reason, x86_64-pc-windows-gnu
380 // doesn't ignore zero-sized struct arguments.
381 // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl,uclibc}.
385 && !linux_s390x_gnu_like
386 && !linux_sparc64_gnu_like
387 && !linux_powerpc_gnu_like)
389 arg.mode = PassMode::Ignore;
396 let mut fn_abi = FnAbi {
397 ret: arg_of(sig.output(), None)?,
401 .chain(extra_args.iter().copied())
402 .chain(caller_location)
404 .map(|(i, ty)| arg_of(ty, Some(i)))
405 .collect::<Result<_, _>>()?,
406 c_variadic: sig.c_variadic,
407 fixed_count: inputs.len() as u32,
409 can_unwind: fn_can_unwind(cx.tcx(), fn_def_id, sig.abi),
411 fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?;
412 debug!("fn_abi_new_uncached = {:?}", fn_abi);
413 Ok(cx.tcx.arena.alloc(fn_abi))
416 #[tracing::instrument(level = "trace", skip(cx))]
417 fn fn_abi_adjust_for_abi<'tcx>(
418 cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
419 fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>,
421 fn_def_id: Option<DefId>,
422 ) -> Result<(), FnAbiError<'tcx>> {
423 if abi == SpecAbi::Unadjusted {
427 if abi == SpecAbi::Rust
428 || abi == SpecAbi::RustCall
429 || abi == SpecAbi::RustIntrinsic
430 || abi == SpecAbi::PlatformIntrinsic
432 // Look up the deduced parameter attributes for this function, if we have its def ID and
433 // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes
435 let deduced_param_attrs = if cx.tcx.sess.opts.optimize != OptLevel::No
436 && cx.tcx.sess.opts.incremental.is_none()
438 fn_def_id.map(|fn_def_id| cx.tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default()
443 let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, arg_idx: Option<usize>| {
448 match arg.layout.abi {
449 Abi::Aggregate { .. } => {}
451 // This is a fun case! The gist of what this is doing is
452 // that we want callers and callees to always agree on the
453 // ABI of how they pass SIMD arguments. If we were to *not*
454 // make these arguments indirect then they'd be immediates
455 // in LLVM, which means that they'd used whatever the
456 // appropriate ABI is for the callee and the caller. That
457 // means, for example, if the caller doesn't have AVX
458 // enabled but the callee does, then passing an AVX argument
459 // across this boundary would cause corrupt data to show up.
461 // This problem is fixed by unconditionally passing SIMD
462 // arguments through memory between callers and callees
463 // which should get them all to agree on ABI regardless of
464 // target feature sets. Some more information about this
465 // issue can be found in #44367.
467 // Note that the platform intrinsic ABI is exempt here as
468 // that's how we connect up to LLVM and it's unstable
469 // anyway, we control all calls to it in libstd.
471 if abi != SpecAbi::PlatformIntrinsic
472 && cx.tcx.sess.target.simd_types_indirect =>
481 let size = arg.layout.size;
482 if arg.layout.is_unsized() || size > Pointer(AddressSpace::DATA).size(cx) {
485 // We want to pass small aggregates as immediates, but using
486 // a LLVM aggregate type for this leads to bad optimizations,
487 // so we pick an appropriately sized integer type instead.
488 arg.cast_to(Reg { kind: RegKind::Integer, size });
491 // If we deduced that this parameter was read-only, add that to the attribute list now.
493 // The `readonly` parameter only applies to pointers, so we can only do this if the
494 // argument was passed indirectly. (If the argument is passed directly, it's an SSA
495 // value, so it's implicitly immutable.)
496 if let (Some(arg_idx), &mut PassMode::Indirect { ref mut attrs, .. }) =
497 (arg_idx, &mut arg.mode)
499 // The `deduced_param_attrs` list could be empty if this is a type of function
500 // we can't deduce any parameters for, so make sure the argument index is in
502 if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) {
503 if deduced_param_attrs.read_only {
504 attrs.regular.insert(ArgAttribute::ReadOnly);
505 debug!("added deduced read-only attribute");
511 fixup(&mut fn_abi.ret, None);
512 for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
513 fixup(arg, Some(arg_idx));
516 fn_abi.adjust_for_foreign_abi(cx, abi)?;
522 #[tracing::instrument(level = "debug", skip(cx))]
523 fn make_thin_self_ptr<'tcx>(
524 cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),
525 layout: TyAndLayout<'tcx>,
526 ) -> TyAndLayout<'tcx> {
528 let fat_pointer_ty = if layout.is_unsized() {
529 // unsized `self` is passed as a pointer to `self`
530 // FIXME (mikeyhew) change this to use &own if it is ever added to the language
531 tcx.mk_mut_ptr(layout.ty)
534 Abi::ScalarPair(..) | Abi::Scalar(..) => (),
535 _ => bug!("receiver type has unsupported layout: {:?}", layout),
538 // In the case of Rc<Self>, we need to explicitly pass a *mut RcBox<Self>
539 // with a Scalar (not ScalarPair) ABI. This is a hack that is understood
540 // elsewhere in the compiler as a method on a `dyn Trait`.
541 // To get the type `*mut RcBox<Self>`, we just keep unwrapping newtypes until we
542 // get a built-in pointer type
543 let mut fat_pointer_layout = layout;
544 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr()
545 && !fat_pointer_layout.ty.is_region_ptr()
547 for i in 0..fat_pointer_layout.fields.count() {
548 let field_layout = fat_pointer_layout.field(cx, i);
550 if !field_layout.is_zst() {
551 fat_pointer_layout = field_layout;
552 continue 'descend_newtypes;
556 bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout);
559 fat_pointer_layout.ty
562 // we now have a type like `*mut RcBox<dyn Trait>`
563 // change its layout to that of `*mut ()`, a thin pointer, but keep the same type
564 // this is understood as a special case elsewhere in the compiler
565 let unit_ptr_ty = tcx.mk_mut_ptr(tcx.mk_unit());
570 // NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing the `Result`
571 // should always work because the type is always `*mut ()`.
572 ..tcx.layout_of(ty::ParamEnv::reveal_all().and(unit_ptr_ty)).unwrap()