From ac7221cfae6d3955ccee66f1f653bb5032719d7e Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 21:28:08 +0000 Subject: [PATCH 1/6] CFI: Refactor: Split into 3 modules --- compiler/rustc_symbol_mangling/src/typeid.rs | 12 +- .../src/typeid/instance.rs | 218 +++++++++ ..._itanium_cxx_abi.rs => itanium_cxx_abi.rs} | 433 +----------------- .../rustc_symbol_mangling/src/typeid/ty.rs | 232 ++++++++++ 4 files changed, 463 insertions(+), 432 deletions(-) create mode 100644 compiler/rustc_symbol_mangling/src/typeid/instance.rs rename compiler/rustc_symbol_mangling/src/typeid/{typeid_itanium_cxx_abi.rs => itanium_cxx_abi.rs} (60%) create mode 100644 compiler/rustc_symbol_mangling/src/typeid/ty.rs diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 862ba285db807..bc35397730536 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -30,7 +30,9 @@ bitflags! { } } -mod typeid_itanium_cxx_abi; +mod instance; +mod itanium_cxx_abi; +mod ty; /// Returns a type metadata identifier for the specified FnAbi. pub fn typeid_for_fnabi<'tcx>( @@ -38,7 +40,7 @@ pub fn typeid_for_fnabi<'tcx>( fn_abi: &FnAbi<'tcx, Ty<'tcx>>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) + itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) } /// Returns a type metadata identifier for the specified Instance. @@ -47,7 +49,7 @@ pub fn typeid_for_instance<'tcx>( instance: Instance<'tcx>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) + instance::typeid_for_instance(tcx, instance, options) } /// Returns a KCFI type metadata identifier for the specified FnAbi. @@ -59,7 +61,7 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.write(itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); hash.finish() as u32 } @@ -77,6 +79,6 @@ pub fn kcfi_typeid_for_instance<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write(instance::typeid_for_instance(tcx, instance, options).as_bytes()); hash.finish() as u32 } diff --git a/compiler/rustc_symbol_mangling/src/typeid/instance.rs b/compiler/rustc_symbol_mangling/src/typeid/instance.rs new file mode 100644 index 0000000000000..a2742db563485 --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid/instance.rs @@ -0,0 +1,218 @@ +/// Adjusts an `Instance` prior to encoding. +use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{self, Instance, List, Ty, TyCtxt}; +use rustc_trait_selection::traits; +use std::iter; + +use crate::typeid::TypeIdOptions; + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + mut instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + if (matches!(instance.def, ty::InstanceDef::Virtual(..)) + && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) + || matches!(instance.def, ty::InstanceDef::DropGlue(..)) + { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. Rust allows + // for types to be erased to any trait object and retains the drop function for the original + // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has information after type erasure and it could be a call + // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on + // declaration/definition, and during code generation at call sites so they have the same + // type id and match. + // + // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of + // any other type. + // + let def_id = tcx + .lang_items() + .drop_trait() + .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); + let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { + def_id: def_id, + args: List::empty(), + }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + instance.args = tcx.mk_args_trait(self_ty, List::empty()); + } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + let upcast_ty = match tcx.trait_of_item(def_id) { + Some(trait_id) => trait_object_ty( + tcx, + ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), + ), + // drop_in_place won't have a defining trait, skip the upcast + None => instance.args.type_at(0), + }; + let stripped_ty = strip_receiver_auto(tcx, upcast_ty); + instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1)); + } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + && let Some(trait_id) = tcx.trait_of_item(def_id) + { + // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable, + // as the caller will not know the concrete Self. + let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + } + + if options.contains(TypeIdOptions::ERASE_SELF_TYPE) { + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) + { + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + let trait_id = trait_ref.skip_binder().def_id; + if traits::is_vtable_safe_method(tcx, trait_id, trait_method) + && tcx.object_safety_violations(trait_id).is_empty() + { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } else if tcx.is_closure_like(instance.def_id()) { + // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, + // instantiate it, and take the type of its only method as our own. + let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + let (trait_id, inputs) = match closure_ty.kind() { + ty::Closure(..) => { + let closure_args = instance.args.as_closure(); + let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); + let tuple_args = + tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; + (trait_id, Some(tuple_args)) + } + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, + ty::CoroutineClosure(..) => ( + tcx.require_lang_item(LangItem::FnOnce, None), + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), + ), + x => bug!("Unexpected type kind for closure-like: {x:?}"), + }; + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + // There should be exactly one method on this trait, and it should be the one we're + // defining. + let call = tcx + .associated_items(trait_id) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .expect("No call-family function on closure-like Fn trait?") + .def_id; + + instance.def = ty::InstanceDef::Virtual(call, 0); + instance.args = abstract_args; + } + } + + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) + .unwrap_or_else(|error| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") + }); + + super::itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) +} + +fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { + bug!("Tried to strip auto traits from non-dynamic type {ty}"); + }; + if preds.principal().is_some() { + let filtered_preds = + tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { + !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) + })); + Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + } else { + // If there's no principal type, re-encode it as a unit, since we don't know anything + // about it. This technically discards the knowledge that it was a type that was made + // into a trait object at some point, but that's not a lot. + tcx.types.unit + } +} + +#[instrument(skip(tcx), ret)] +fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { + assert!(!poly_trait_ref.has_non_region_param()); + let principal_pred = poly_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) + }); + let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) + .flat_map(|super_poly_trait_ref| { + tcx.associated_items(super_poly_trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(move |assoc_ty| { + super_poly_trait_ref.map_bound(|super_trait_ref| { + let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); + let resolved = tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + alias_ty.to_ty(tcx), + ); + debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + def_id: assoc_ty.def_id, + args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, + term: resolved.into(), + }) + }) + }) + }) + .collect(); + assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let preds = tcx.mk_poly_existential_predicates_from_iter( + iter::once(principal_pred).chain(assoc_preds.into_iter()), + ); + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) +} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs similarity index 60% rename from compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs rename to compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs index c632712f5a917..6b2b811b1b2d0 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs @@ -10,25 +10,23 @@ use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, - TermKind, Ty, TyCtxt, UintTy, + self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind, + Ty, TyCtxt, UintTy, }; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use rustc_span::def_id::DefId; use rustc_span::sym; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; use std::fmt::Write as _; -use std::iter; -use crate::typeid::TypeIdOptions; +use super::{ty::{TransformTy, TransformTyOptions}, TypeIdOptions}; + +type EncodeTyOptions = TypeIdOptions; /// Type and extended type qualifiers. #[derive(Eq, Hash, PartialEq)] @@ -47,12 +45,6 @@ enum DictKey<'tcx> { Predicate(ExistentialPredicate<'tcx>), } -/// Options for encode_ty. -type EncodeTyOptions = TypeIdOptions; - -/// Options for transform_ty. -type TransformTyOptions = TypeIdOptions; - /// Converts a number to a disambiguator (see /// ). fn to_disambiguator(num: u64) -> String { @@ -752,210 +744,6 @@ fn encode_ty<'tcx>( typeid } -struct TransformTy<'tcx> { - tcx: TyCtxt<'tcx>, - options: TransformTyOptions, - parents: Vec>, -} - -impl<'tcx> TransformTy<'tcx> { - fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { - TransformTy { tcx, options, parents: Vec::new() } - } -} - -impl<'tcx> TypeFolder> for TransformTy<'tcx> { - // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms - // all c_void types into unit types unconditionally, generalizes pointers if - // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if - // TransformTyOptions::NORMALIZE_INTEGERS option is set. - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind() { - ty::Array(..) - | ty::Closure(..) - | ty::Coroutine(..) - | ty::CoroutineClosure(..) - | ty::CoroutineWitness(..) - | ty::Float(..) - | ty::FnDef(..) - | ty::Foreign(..) - | ty::Never - | ty::Slice(..) - | ty::Str - | ty::Tuple(..) => t.super_fold_with(self), - - ty::Bool => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: on all platforms that Rust's currently supports, its size and alignment - // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - // - // Clang represents bool as an 8-bit unsigned integer. - self.tcx.types.u8 - } else { - t - } - } - - ty::Char => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Since #118032, char is guaranteed to have the same size, alignment, and - // function call ABI as u32 on all platforms. - self.tcx.types.u32 - } else { - t - } - } - - ty::Int(..) | ty::Uint(..) => { - if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit - // wide. All platforms we currently support have a C platform, and as a - // consequence, isize/usize are at least 16-bit wide for all of them. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) - match t.kind() { - ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.i16, - 32 => self.tcx.types.i32, - 64 => self.tcx.types.i64, - 128 => self.tcx.types.i128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { - 16 => self.tcx.types.u16, - 32 => self.tcx.types.u32, - 64 => self.tcx.types.u64, - 128 => self.tcx.types.u128, - _ => bug!( - "fold_ty: unexpected pointer width `{}`", - self.tcx.sess.target.pointer_width - ), - }, - _ => t, - } - } else { - t - } - } - - ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, - - ty::Adt(adt_def, args) => { - if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) - { - // Don't transform repr(transparent) types with an user-defined CFI encoding to - // preserve the user-defined CFI encoding. - if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { - return t; - } - let variant = adt_def.non_enum_variant(); - let param_env = self.tcx.param_env(variant.def_id); - let field = variant.fields.iter().find(|field| { - let ty = self.tcx.type_of(field.did).instantiate_identity(); - let is_zst = self - .tcx - .layout_of(param_env.and(ty)) - .is_ok_and(|layout| layout.is_zst()); - !is_zst - }); - if let Some(field) = field { - let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); - // Generalize any repr(transparent) user-defined type that is either a - // pointer or reference, and either references itself or any other type that - // contains or references itself, to avoid a reference cycle. - - // If the self reference is not through a pointer, for example, due - // to using `PhantomData`, need to skip normalizing it if we hit it again. - self.parents.push(t); - let ty = if ty0.is_any_ptr() && ty0.contains(t) { - let options = self.options; - self.options |= TransformTyOptions::GENERALIZE_POINTERS; - let ty = ty0.fold_with(self); - self.options = options; - ty - } else { - ty0.fold_with(self) - }; - self.parents.pop(); - ty - } else { - // Transform repr(transparent) types without non-ZST field into () - self.tcx.types.unit - } - } else { - t.super_fold_with(self) - } - } - - ty::Ref(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } else { - Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::RawPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if t.is_mutable_ptr() { - Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) - } else { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } - } else { - t.super_fold_with(self) - } - } - - ty::FnPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) - } else { - t.super_fold_with(self) - } - } - - ty::Dynamic(predicates, _region, kind) => { - let predicates = self.tcx.mk_poly_existential_predicates_from_iter( - predicates.iter().filter_map(|predicate| match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = ty::TraitRef::identity(self.tcx, trait_ref.def_id); - Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(self.tcx, trait_ref), - ))) - } - ty::ExistentialPredicate::Projection(..) => None, - ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - }), - ); - - Ty::new_dynamic(self.tcx, predicates, self.tcx.lifetimes.re_erased, *kind) - } - - ty::Alias(..) => { - self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) - } - - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { - bug!("fold_ty: unexpected `{:?}`", t.kind()); - } - } - } - - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } -} - /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. #[instrument(level = "trace", skip(tcx))] @@ -1041,212 +829,3 @@ pub fn typeid_for_fnabi<'tcx>( typeid } - -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - mut instance: Instance<'tcx>, - options: TypeIdOptions, -) -> String { - if (matches!(instance.def, ty::InstanceDef::Virtual(..)) - && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) - || matches!(instance.def, ty::InstanceDef::DropGlue(..)) - { - // Adjust the type ids of DropGlues - // - // DropGlues may have indirect calls to one or more given types drop function. Rust allows - // for types to be erased to any trait object and retains the drop function for the original - // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is - // called a second time, it only has information after type erasure and it could be a call - // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on - // declaration/definition, and during code generation at call sites so they have the same - // type id and match. - // - // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of - // any other type. - // - let def_id = tcx - .lang_items() - .drop_trait() - .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); - let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { - def_id: def_id, - args: List::empty(), - }); - let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); - instance.args = tcx.mk_args_trait(self_ty, List::empty()); - } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { - let upcast_ty = match tcx.trait_of_item(def_id) { - Some(trait_id) => trait_object_ty( - tcx, - ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), - ), - // drop_in_place won't have a defining trait, skip the upcast - None => instance.args.type_at(0), - }; - let stripped_ty = strip_receiver_auto(tcx, upcast_ty); - instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1)); - } else if let ty::InstanceDef::VTableShim(def_id) = instance.def - && let Some(trait_id) = tcx.trait_of_item(def_id) - { - // VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable, - // as the caller will not know the concrete Self. - let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - } - - if options.contains(EncodeTyOptions::ERASE_SELF_TYPE) { - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) - && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) - { - let impl_method = tcx.associated_item(instance.def_id()); - let method_id = impl_method - .trait_item_def_id - .expect("Part of a trait implementation, but not linked to the def_id?"); - let trait_method = tcx.associated_item(method_id); - let trait_id = trait_ref.skip_binder().def_id; - if traits::is_vtable_safe_method(tcx, trait_id, trait_method) - && tcx.object_safety_violations(trait_id).is_empty() - { - // Trait methods will have a Self polymorphic parameter, where the concreteized - // implementatation will not. We need to walk back to the more general trait method - let trait_ref = tcx.instantiate_and_normalize_erasing_regions( - instance.args, - ty::ParamEnv::reveal_all(), - trait_ref, - ); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - - // At the call site, any call to this concrete function through a vtable will be - // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the - // original method id, and we've recovered the trait arguments, we can make the callee - // instance we're computing the alias set for match the caller instance. - // - // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. - // If we ever *do* start encoding the vtable index, we will need to generate an alias set - // based on which vtables we are putting this method into, as there will be more than one - // index value when supertraits are involved. - instance.def = ty::InstanceDef::Virtual(method_id, 0); - let abstract_trait_args = - tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); - } - } else if tcx.is_closure_like(instance.def_id()) { - // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, - // instantiate it, and take the type of its only method as our own. - let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - let (trait_id, inputs) = match closure_ty.kind() { - ty::Closure(..) => { - let closure_args = instance.args.as_closure(); - let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); - let tuple_args = - tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; - (trait_id, Some(tuple_args)) - } - ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { - hir::CoroutineKind::Coroutine(..) => ( - tcx.require_lang_item(LangItem::Coroutine, None), - Some(instance.args.as_coroutine().resume_ty()), - ), - hir::CoroutineKind::Desugared(desugaring, _) => { - let lang_item = match desugaring { - hir::CoroutineDesugaring::Async => LangItem::Future, - hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, - hir::CoroutineDesugaring::Gen => LangItem::Iterator, - }; - (tcx.require_lang_item(lang_item, None), None) - } - }, - ty::CoroutineClosure(..) => ( - tcx.require_lang_item(LangItem::FnOnce, None), - Some( - tcx.instantiate_bound_regions_with_erased( - instance.args.as_coroutine_closure().coroutine_closure_sig(), - ) - .tupled_inputs_ty, - ), - ), - x => bug!("Unexpected type kind for closure-like: {x:?}"), - }; - let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); - let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); - let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); - let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); - // There should be exactly one method on this trait, and it should be the one we're - // defining. - let call = tcx - .associated_items(trait_id) - .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Fn) - .expect("No call-family function on closure-like Fn trait?") - .def_id; - - instance.def = ty::InstanceDef::Virtual(call, 0); - instance.args = abstract_args; - } - } - - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) - .unwrap_or_else(|error| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") - }); - - typeid_for_fnabi(tcx, fn_abi, options) -} - -fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { - bug!("Tried to strip auto traits from non-dynamic type {ty}"); - }; - if preds.principal().is_some() { - let filtered_preds = - tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { - !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) - })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) - } else { - // If there's no principal type, re-encode it as a unit, since we don't know anything - // about it. This technically discards the knowledge that it was a type that was made - // into a trait object at some point, but that's not a lot. - tcx.types.unit - } -} - -#[instrument(skip(tcx), ret)] -fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { - assert!(!poly_trait_ref.has_non_region_param()); - let principal_pred = poly_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) - }); - let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) - .flat_map(|super_poly_trait_ref| { - tcx.associated_items(super_poly_trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .map(move |assoc_ty| { - super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::ParamEnv::reveal_all(), - alias_ty.to_ty(tcx), - ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: assoc_ty.def_id, - args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, - term: resolved.into(), - }) - }) - }) - }) - .collect(); - assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - let preds = tcx.mk_poly_existential_predicates_from_iter( - iter::once(principal_pred).chain(assoc_preds.into_iter()), - ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) -} diff --git a/compiler/rustc_symbol_mangling/src/typeid/ty.rs b/compiler/rustc_symbol_mangling/src/typeid/ty.rs new file mode 100644 index 0000000000000..14cf7a23a218b --- /dev/null +++ b/compiler/rustc_symbol_mangling/src/typeid/ty.rs @@ -0,0 +1,232 @@ +/// Supports normalizing a type prior to encoding +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::sym; + +use crate::typeid::TypeIdOptions; + +pub type TransformTyOptions = TypeIdOptions; + +/// A type folder which normalizes a type to a form suitable for encoding. +/// Always: +/// +/// * `c_void` -> `()` +/// * Unwrap `#[repr(transparent)]` (when possible) +/// * Erases projection clauses (This behavior is undesirable and to be removed) +/// * Erases some arguments from trait clauses (To be adjusted) +/// +/// With NORMALIZE_INTEGERS: +/// +/// * `bool` -> `u8` +/// * `char` -> `u32` +/// * `isize` / `usize` -> `iK` / `uK` where K is platform pointer width +/// +/// With GENERALIZE_POINTERS: +/// +/// * `*mut T` / `*const T` -> `*mut ()` / `*const ()` +/// * `&mut T` / `&T` -> `&mut ()` / `&()` +/// * `fn(..) -> T` -> `*const ()` +pub struct TransformTy<'tcx> { + tcx: TyCtxt<'tcx>, + options: TransformTyOptions, + parents: Vec>, +} + +impl<'tcx> TransformTy<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + TransformTy { tcx, options, parents: Vec::new() } + } +} + +impl<'tcx> TypeFolder> for TransformTy<'tcx> { + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms + // all c_void types into unit types unconditionally, generalizes pointers if + // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if + // TransformTyOptions::NORMALIZE_INTEGERS option is set. + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Array(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Float(..) + | ty::FnDef(..) + | ty::Foreign(..) + | ty::Never + | ty::Slice(..) + | ty::Str + | ty::Tuple(..) => t.super_fold_with(self), + + ty::Bool => { + if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + // Note: on all platforms that Rust's currently supports, its size and alignment + // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + // + // Clang represents bool as an 8-bit unsigned integer. + self.tcx.types.u8 + } else { + t + } + } + + ty::Char => { + if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + // Since #118032, char is guaranteed to have the same size, alignment, and + // function call ABI as u32 on all platforms. + self.tcx.types.u32 + } else { + t + } + } + + ty::Int(..) | ty::Uint(..) => { + if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit + // wide. All platforms we currently support have a C platform, and as a + // consequence, isize/usize are at least 16-bit wide for all of them. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) + match t.kind() { + ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.i16, + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.u16, + 32 => self.tcx.types.u32, + 64 => self.tcx.types.u64, + 128 => self.tcx.types.u128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + _ => t, + } + } else { + t + } + } + + ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, + + ty::Adt(adt_def, args) => { + if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) + { + // Don't transform repr(transparent) types with an user-defined CFI encoding to + // preserve the user-defined CFI encoding. + if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + return t; + } + let variant = adt_def.non_enum_variant(); + let param_env = self.tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = self.tcx.type_of(field.did).instantiate_identity(); + let is_zst = self + .tcx + .layout_of(param_env.and(ty)) + .is_ok_and(|layout| layout.is_zst()); + !is_zst + }); + if let Some(field) = field { + let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); + // Generalize any repr(transparent) user-defined type that is either a + // pointer or reference, and either references itself or any other type that + // contains or references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + self.parents.push(t); + let ty = if ty0.is_any_ptr() && ty0.contains(t) { + let options = self.options; + self.options |= TransformTyOptions::GENERALIZE_POINTERS; + let ty = ty0.fold_with(self); + self.options = options; + ty + } else { + ty0.fold_with(self) + }; + self.parents.pop(); + ty + } else { + // Transform repr(transparent) types without non-ZST field into () + self.tcx.types.unit + } + } else { + t.super_fold_with(self) + } + } + + ty::Ref(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } else { + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::RawPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) + } else { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::FnPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } else { + t.super_fold_with(self) + } + } + + ty::Dynamic(predicates, _region, kind) => { + let predicates = self.tcx.mk_poly_existential_predicates_from_iter( + predicates.iter().filter_map(|predicate| match predicate.skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = ty::TraitRef::identity(self.tcx, trait_ref.def_id); + Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(self.tcx, trait_ref), + ))) + } + ty::ExistentialPredicate::Projection(..) => None, + ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), + }), + ); + + Ty::new_dynamic(self.tcx, predicates, self.tcx.lifetimes.re_erased, *kind) + } + + ty::Alias(..) => { + self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) + } + + ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + bug!("fold_ty: unexpected `{:?}`", t.kind()); + } + } + } + + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} From ac44e8649becbe57951d92e0d9666cb71dd7379c Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 21:45:57 +0000 Subject: [PATCH 2/6] CFI: Refactor: Separate instance transformation from type generation --- compiler/rustc_symbol_mangling/src/typeid.rs | 12 ++++++--- .../src/typeid/instance.rs | 25 ++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index bc35397730536..c39bdb70590b4 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, InstanceDef, List, ReifyReason, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -49,7 +49,13 @@ pub fn typeid_for_instance<'tcx>( instance: Instance<'tcx>, options: TypeIdOptions, ) -> String { - instance::typeid_for_instance(tcx, instance, options) + let instance = instance::transform(tcx, instance, options); + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, List::empty()))) + .unwrap_or_else(|error| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") + }); + itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) } /// Returns a KCFI type metadata identifier for the specified FnAbi. @@ -79,6 +85,6 @@ pub fn kcfi_typeid_for_instance<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(instance::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write(typeid_for_instance(tcx, instance, options).as_bytes()); hash.finish() as u32 } diff --git a/compiler/rustc_symbol_mangling/src/typeid/instance.rs b/compiler/rustc_symbol_mangling/src/typeid/instance.rs index a2742db563485..d1603cc328808 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/instance.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/instance.rs @@ -8,13 +8,21 @@ use std::iter; use crate::typeid::TypeIdOptions; -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( +/// Transform an instance where needed prior to encoding +/// +/// Always: +/// +/// * `drop_in_place::` -> `drop_in_place::` +/// * `Trait::method::` -> `Trait::method::` +/// * `FnOnce::call_once` (`VTableShim`) -> `FnOnce::call_once::` +/// +/// If `ERASE_SELF_TYPE` is set: +/// * `Trait::method::` -> `Trait::method::` +pub fn transform<'tcx>( tcx: TyCtxt<'tcx>, mut instance: Instance<'tcx>, options: TypeIdOptions, -) -> String { +) -> Instance<'tcx> { if (matches!(instance.def, ty::InstanceDef::Virtual(..)) && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) || matches!(instance.def, ty::InstanceDef::DropGlue(..)) @@ -154,14 +162,7 @@ pub fn typeid_for_instance<'tcx>( instance.args = abstract_args; } } - - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) - .unwrap_or_else(|error| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") - }); - - super::itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) + instance } fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { From 3bbe6f228030568920ca7e15263a784f972cb65b Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 22:12:44 +0000 Subject: [PATCH 3/6] CFI: Refactor: Put the TransformTy folder behind an abstraction --- .../src/typeid/itanium_cxx_abi.rs | 17 ++++++++--------- compiler/rustc_symbol_mangling/src/typeid/ty.rs | 11 ++++++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs index 6b2b811b1b2d0..8ba1f94ea3956 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs @@ -11,7 +11,6 @@ use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{ self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, UintTy, @@ -24,7 +23,9 @@ use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; -use super::{ty::{TransformTy, TransformTyOptions}, TypeIdOptions}; +use super::TypeIdOptions; +use crate::typeid; +use crate::typeid::ty::TransformTyOptions; type EncodeTyOptions = TypeIdOptions; @@ -175,15 +176,14 @@ fn encode_fnsig<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - let mut type_folder = TransformTy::new(tcx, transform_ty_options); - let ty = fn_sig.output().fold_with(&mut type_folder); + let ty = typeid::ty::transform(tcx, transform_ty_options, fn_sig.output()); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); // Encode the parameter types let tys = fn_sig.inputs(); if !tys.is_empty() { for ty in tys { - let ty = ty.fold_with(&mut type_folder); + let ty = typeid::ty::transform(tcx, transform_ty_options, *ty); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); } @@ -782,8 +782,7 @@ pub fn typeid_for_fnabi<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let mut type_folder = TransformTy::new(tcx, transform_ty_options); - let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); + let ty = typeid::ty::transform(tcx, transform_ty_options, fn_abi.ret.layout.ty); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types @@ -795,7 +794,7 @@ pub fn typeid_for_fnabi<'tcx>( let mut pushed_arg = false; for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { pushed_arg = true; - let ty = arg.layout.ty.fold_with(&mut type_folder); + let ty = typeid::ty::transform(tcx, transform_ty_options, arg.layout.ty); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } if !pushed_arg { @@ -808,7 +807,7 @@ pub fn typeid_for_fnabi<'tcx>( if fn_abi.args[n].mode == PassMode::Ignore { continue; } - let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); + let ty = typeid::ty::transform(tcx, transform_ty_options, fn_abi.args[n].layout.ty); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } diff --git a/compiler/rustc_symbol_mangling/src/typeid/ty.rs b/compiler/rustc_symbol_mangling/src/typeid/ty.rs index 14cf7a23a218b..3c372f1be3f16 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/ty.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/ty.rs @@ -8,7 +8,8 @@ use crate::typeid::TypeIdOptions; pub type TransformTyOptions = TypeIdOptions; -/// A type folder which normalizes a type to a form suitable for encoding. +/// Normalizes a type to a form suitable for encoding. +/// /// Always: /// /// * `c_void` -> `()` @@ -27,14 +28,18 @@ pub type TransformTyOptions = TypeIdOptions; /// * `*mut T` / `*const T` -> `*mut ()` / `*const ()` /// * `&mut T` / `&T` -> `&mut ()` / `&()` /// * `fn(..) -> T` -> `*const ()` -pub struct TransformTy<'tcx> { +pub fn transform<'tcx>(tcx: TyCtxt<'tcx>, options: TransformTyOptions, ty: Ty<'tcx>) -> Ty<'tcx> { + TransformTy::new(tcx, options).fold_ty(ty) +} + +struct TransformTy<'tcx> { tcx: TyCtxt<'tcx>, options: TransformTyOptions, parents: Vec>, } impl<'tcx> TransformTy<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { TransformTy { tcx, options, parents: Vec::new() } } } From e10a784ecac41bcf90a8f64db9647549c6e1b359 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 22:27:03 +0000 Subject: [PATCH 4/6] CFI: Refactor: Merge the three `TypeIdOption` aliases --- compiler/rustc_codegen_llvm/src/builder.rs | 15 ++-- compiler/rustc_codegen_llvm/src/declare.rs | 21 +++--- compiler/rustc_symbol_mangling/src/typeid.rs | 12 ++-- .../src/typeid/instance.rs | 6 +- .../src/typeid/itanium_cxx_abi.rs | 70 +++++++------------ .../rustc_symbol_mangling/src/typeid/ty.rs | 28 ++++---- 6 files changed, 64 insertions(+), 88 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 1a32958d3627b..4a7521f3d8f19 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -23,8 +23,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, + self, kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, }; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; @@ -1632,12 +1631,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - let mut options = TypeIdOptions::empty(); + let mut options = typeid::Options::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(typeid::Options::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(typeid::Options::NORMALIZE_INTEGERS); } let typeid = if let Some(instance) = instance { @@ -1680,12 +1679,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return None; } - let mut options = TypeIdOptions::empty(); + let mut options = typeid::Options::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(typeid::Options::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(typeid::Options::NORMALIZE_INTEGERS); } let kcfi_typeid = if let Some(instance) = instance { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index f58dd4066ad71..298ac159564a0 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -23,8 +23,7 @@ use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, + self, kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, }; use smallvec::SmallVec; @@ -145,13 +144,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if let Some(instance) = instance { let mut typeids = FxIndexSet::default(); for options in [ - TypeIdOptions::GENERALIZE_POINTERS, - TypeIdOptions::NORMALIZE_INTEGERS, - TypeIdOptions::ERASE_SELF_TYPE, + typeid::Options::GENERALIZE_POINTERS, + typeid::Options::NORMALIZE_INTEGERS, + typeid::Options::ERASE_SELF_TYPE, ] .into_iter() .powerset() - .map(TypeIdOptions::from_iter) + .map(typeid::Options::from_iter) { let typeid = typeid_for_instance(self.tcx, instance, options); if typeids.insert(typeid.clone()) { @@ -160,10 +159,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } else { for options in - [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS] + [typeid::Options::GENERALIZE_POINTERS, typeid::Options::NORMALIZE_INTEGERS] .into_iter() .powerset() - .map(TypeIdOptions::from_iter) + .map(typeid::Options::from_iter) { let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); self.add_type_metadata(llfn, typeid); @@ -175,12 +174,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { // LLVM KCFI does not support multiple !kcfi_type attachments // Default to erasing the self type. If we need the concrete type, there will be a // hint in the instance. - let mut options = TypeIdOptions::ERASE_SELF_TYPE; + let mut options = typeid::Options::ERASE_SELF_TYPE; if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(typeid::Options::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(typeid::Options::NORMALIZE_INTEGERS); } if let Some(instance) = instance { diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index c39bdb70590b4..faaec0e1e05ad 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -12,7 +12,7 @@ use twox_hash::XxHash64; bitflags! { /// Options for typeid_for_fnabi. #[derive(Clone, Copy, Debug)] - pub struct TypeIdOptions: u32 { + pub struct Options: u32 { /// Generalizes pointers for compatibility with Clang /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI /// support. @@ -38,7 +38,7 @@ mod ty; pub fn typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, + options: Options, ) -> String { itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) } @@ -47,7 +47,7 @@ pub fn typeid_for_fnabi<'tcx>( pub fn typeid_for_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - options: TypeIdOptions, + options: Options, ) -> String { let instance = instance::transform(tcx, instance, options); let fn_abi = tcx @@ -62,7 +62,7 @@ pub fn typeid_for_instance<'tcx>( pub fn kcfi_typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, + options: Options, ) -> u32 { // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) @@ -75,12 +75,12 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( pub fn kcfi_typeid_for_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - mut options: TypeIdOptions, + mut options: Options, ) -> u32 { // If we receive a `ReifyShim` intended to produce a function pointer, we need to remain // concrete - abstraction is for vtables. if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { - options.remove(TypeIdOptions::ERASE_SELF_TYPE); + options.remove(Options::ERASE_SELF_TYPE); } // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) diff --git a/compiler/rustc_symbol_mangling/src/typeid/instance.rs b/compiler/rustc_symbol_mangling/src/typeid/instance.rs index d1603cc328808..563792e94244a 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/instance.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/instance.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Instance, List, Ty, TyCtxt}; use rustc_trait_selection::traits; use std::iter; -use crate::typeid::TypeIdOptions; +use crate::typeid; /// Transform an instance where needed prior to encoding /// @@ -21,7 +21,7 @@ use crate::typeid::TypeIdOptions; pub fn transform<'tcx>( tcx: TyCtxt<'tcx>, mut instance: Instance<'tcx>, - options: TypeIdOptions, + options: typeid::Options, ) -> Instance<'tcx> { if (matches!(instance.def, ty::InstanceDef::Virtual(..)) && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) @@ -72,7 +72,7 @@ pub fn transform<'tcx>( instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); } - if options.contains(TypeIdOptions::ERASE_SELF_TYPE) { + if options.contains(typeid::Options::ERASE_SELF_TYPE) { if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) { diff --git a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs index 8ba1f94ea3956..dd8ef0ccc2670 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs @@ -23,11 +23,7 @@ use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; -use super::TypeIdOptions; use crate::typeid; -use crate::typeid::ty::TransformTyOptions; - -type EncodeTyOptions = TypeIdOptions; /// Type and extended type qualifiers. #[derive(Eq, Hash, PartialEq)] @@ -90,7 +86,7 @@ fn encode_const<'tcx>( tcx: TyCtxt<'tcx>, c: Const<'tcx>, dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, + options: typeid::Options, ) -> String { // L[n][]E as literal argument let mut s = String::from('L'); @@ -157,34 +153,26 @@ fn encode_fnsig<'tcx>( tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>, dict: &mut FxHashMap, usize>, - options: TypeIdOptions, + mut options: typeid::Options, ) -> String { // Function types are delimited by an "F..E" pair let mut s = String::from("F"); - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); match fn_sig.abi { - Abi::C { .. } => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); - } + Abi::C { .. } => options.insert(typeid::Options::GENERALIZE_REPR_C), + _ => options.remove(typeid::Options::GENERALIZE_REPR_C), } // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - let ty = typeid::ty::transform(tcx, transform_ty_options, fn_sig.output()); - s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + let ty = typeid::ty::transform(tcx, options, fn_sig.output()); + s.push_str(&encode_ty(tcx, ty, dict, options)); // Encode the parameter types let tys = fn_sig.inputs(); if !tys.is_empty() { for ty in tys { - let ty = typeid::ty::transform(tcx, transform_ty_options, *ty); - s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + let ty = typeid::ty::transform(tcx, options, *ty); + s.push_str(&encode_ty(tcx, ty, dict, options)); } if fn_sig.c_variadic { @@ -212,7 +200,7 @@ fn encode_predicate<'tcx>( tcx: TyCtxt<'tcx>, predicate: ty::PolyExistentialPredicate<'tcx>, dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, + options: typeid::Options, ) -> String { // u[IE], where is , as vendor // extended type. @@ -247,7 +235,7 @@ fn encode_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: &List>, dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, + options: typeid::Options, ) -> String { // E as part of vendor extended type let mut s = String::new(); @@ -297,7 +285,7 @@ fn encode_args<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, + options: typeid::Options, ) -> String { // [IE] as part of vendor extended type let mut s = String::new(); @@ -425,7 +413,7 @@ fn encode_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, + options: typeid::Options, ) -> String { let mut typeid = String::new(); @@ -570,7 +558,7 @@ fn encode_ty<'tcx>( } else { bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); } - } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { + } else if options.contains(typeid::Options::GENERALIZE_REPR_C) && adt_def.repr().c() { // For cross-language LLVM CFI support, the encoding must be compatible at the FFI // boundary. For instance: // @@ -702,7 +690,7 @@ fn encode_ty<'tcx>( ty::FnPtr(fn_sig) => { // PFE let mut s = String::from("P"); - s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty())); + s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, typeid::Options::empty())); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); } @@ -750,7 +738,7 @@ fn encode_ty<'tcx>( pub fn typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, + mut options: typeid::Options, ) -> String { // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions // its type. @@ -768,22 +756,14 @@ pub fn typeid_for_fnabi<'tcx>( // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). let mut dict: FxHashMap, usize> = FxHashMap::default(); - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); match fn_abi.conv { - Conv::C => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); - } + Conv::C => options.insert(typeid::Options::GENERALIZE_REPR_C), + _ => options.remove(typeid::Options::GENERALIZE_REPR_C), } // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let ty = typeid::ty::transform(tcx, transform_ty_options, fn_abi.ret.layout.ty); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + let ty = typeid::ty::transform(tcx, options, fn_abi.ret.layout.ty); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, options)); // Encode the parameter types @@ -794,8 +774,8 @@ pub fn typeid_for_fnabi<'tcx>( let mut pushed_arg = false; for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { pushed_arg = true; - let ty = typeid::ty::transform(tcx, transform_ty_options, arg.layout.ty); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + let ty = typeid::ty::transform(tcx, options, arg.layout.ty); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, options)); } if !pushed_arg { // Empty parameter lists, whether declared as () or conventionally as (void), are @@ -807,8 +787,8 @@ pub fn typeid_for_fnabi<'tcx>( if fn_abi.args[n].mode == PassMode::Ignore { continue; } - let ty = typeid::ty::transform(tcx, transform_ty_options, fn_abi.args[n].layout.ty); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + let ty = typeid::ty::transform(tcx, options, fn_abi.args[n].layout.ty); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, options)); } typeid.push('z'); @@ -818,11 +798,11 @@ pub fn typeid_for_fnabi<'tcx>( typeid.push('E'); // Add encoding suffixes - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + if options.contains(typeid::Options::NORMALIZE_INTEGERS) { typeid.push_str(".normalized"); } - if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { + if options.contains(typeid::Options::GENERALIZE_POINTERS) { typeid.push_str(".generalized"); } diff --git a/compiler/rustc_symbol_mangling/src/typeid/ty.rs b/compiler/rustc_symbol_mangling/src/typeid/ty.rs index 3c372f1be3f16..189113a3af5ae 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/ty.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/ty.rs @@ -4,9 +4,7 @@ use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy}; use rustc_span::sym; -use crate::typeid::TypeIdOptions; - -pub type TransformTyOptions = TypeIdOptions; +use crate::typeid; /// Normalizes a type to a form suitable for encoding. /// @@ -28,18 +26,18 @@ pub type TransformTyOptions = TypeIdOptions; /// * `*mut T` / `*const T` -> `*mut ()` / `*const ()` /// * `&mut T` / `&T` -> `&mut ()` / `&()` /// * `fn(..) -> T` -> `*const ()` -pub fn transform<'tcx>(tcx: TyCtxt<'tcx>, options: TransformTyOptions, ty: Ty<'tcx>) -> Ty<'tcx> { +pub fn transform<'tcx>(tcx: TyCtxt<'tcx>, options: typeid::Options, ty: Ty<'tcx>) -> Ty<'tcx> { TransformTy::new(tcx, options).fold_ty(ty) } struct TransformTy<'tcx> { tcx: TyCtxt<'tcx>, - options: TransformTyOptions, + options: typeid::Options, parents: Vec>, } impl<'tcx> TransformTy<'tcx> { - fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + fn new(tcx: TyCtxt<'tcx>, options: typeid::Options) -> Self { TransformTy { tcx, options, parents: Vec::new() } } } @@ -47,8 +45,8 @@ impl<'tcx> TransformTy<'tcx> { impl<'tcx> TypeFolder> for TransformTy<'tcx> { // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms // all c_void types into unit types unconditionally, generalizes pointers if - // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if - // TransformTyOptions::NORMALIZE_INTEGERS option is set. + // Options::GENERALIZE_POINTERS option is set, and normalizes integers if + // Options::NORMALIZE_INTEGERS option is set. fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match t.kind() { ty::Array(..) @@ -65,7 +63,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { | ty::Tuple(..) => t.super_fold_with(self), ty::Bool => { - if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + if self.options.contains(typeid::Options::NORMALIZE_INTEGERS) { // Note: on all platforms that Rust's currently supports, its size and alignment // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. // @@ -79,7 +77,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } ty::Char => { - if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + if self.options.contains(typeid::Options::NORMALIZE_INTEGERS) { // Since #118032, char is guaranteed to have the same size, alignment, and // function call ABI as u32 on all platforms. self.tcx.types.u32 @@ -89,7 +87,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } ty::Int(..) | ty::Uint(..) => { - if self.options.contains(TransformTyOptions::NORMALIZE_INTEGERS) { + if self.options.contains(typeid::Options::NORMALIZE_INTEGERS) { // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit // wide. All platforms we currently support have a C platform, and as a // consequence, isize/usize are at least 16-bit wide for all of them. @@ -154,7 +152,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { self.parents.push(t); let ty = if ty0.is_any_ptr() && ty0.contains(t) { let options = self.options; - self.options |= TransformTyOptions::GENERALIZE_POINTERS; + self.options |= typeid::Options::GENERALIZE_POINTERS; let ty = ty0.fold_with(self); self.options = options; ty @@ -173,7 +171,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } ty::Ref(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if self.options.contains(typeid::Options::GENERALIZE_POINTERS) { if t.is_mutable_ptr() { Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) } else { @@ -185,7 +183,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } ty::RawPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if self.options.contains(typeid::Options::GENERALIZE_POINTERS) { if t.is_mutable_ptr() { Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) } else { @@ -197,7 +195,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { } ty::FnPtr(..) => { - if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if self.options.contains(typeid::Options::GENERALIZE_POINTERS) { Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) } else { t.super_fold_with(self) From 0a85d93b5b1b77bbf86d422edbe04336fffe1332 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 22:42:38 +0000 Subject: [PATCH 5/6] CFI: Refactor: Clippy fixes --- .../rustc_symbol_mangling/src/typeid/instance.rs | 2 +- .../src/typeid/itanium_cxx_abi.rs | 12 +++++------- compiler/rustc_symbol_mangling/src/typeid/ty.rs | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid/instance.rs b/compiler/rustc_symbol_mangling/src/typeid/instance.rs index 563792e94244a..7d7cc68e6c39a 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/instance.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/instance.rs @@ -45,7 +45,7 @@ pub fn transform<'tcx>( .drop_trait() .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { - def_id: def_id, + def_id, args: List::empty(), }); let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); diff --git a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs index dd8ef0ccc2670..7be92af41be18 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/itanium_cxx_abi.rs @@ -178,14 +178,12 @@ fn encode_fnsig<'tcx>( if fn_sig.c_variadic { s.push('z'); } + } else if fn_sig.c_variadic { + s.push('z'); } else { - if fn_sig.c_variadic { - s.push('z'); - } else { - // Empty parameter lists, whether declared as () or conventionally as (void), are - // encoded with a void parameter specifier "v". - s.push('v') - } + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + s.push('v') } // Close the "F..E" pair diff --git a/compiler/rustc_symbol_mangling/src/typeid/ty.rs b/compiler/rustc_symbol_mangling/src/typeid/ty.rs index 189113a3af5ae..18084caafc6df 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/ty.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/ty.rs @@ -128,7 +128,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { { // Don't transform repr(transparent) types with an user-defined CFI encoding to // preserve the user-defined CFI encoding. - if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + if self.tcx.get_attr(adt_def.did(), sym::cfi_encoding).is_some() { return t; } let variant = adt_def.non_enum_variant(); From ea516f53365aa785d0cd0b5eb9d1ab66f916a2eb Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 5 Apr 2024 22:48:43 +0000 Subject: [PATCH 6/6] CFI: Refactor: Rename exported functions to not mention the module name --- compiler/rustc_codegen_llvm/src/builder.rs | 12 +++++------- compiler/rustc_codegen_llvm/src/declare.rs | 12 +++++------- compiler/rustc_symbol_mangling/src/typeid.rs | 12 ++++++------ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4a7521f3d8f19..f5228ae9f6a1b 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -22,9 +22,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{ - self, kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, -}; +use rustc_symbol_mangling::typeid; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -1640,9 +1638,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } let typeid = if let Some(instance) = instance { - typeid_for_instance(self.tcx, instance, options) + typeid::from_instance(self.tcx, instance, options) } else { - typeid_for_fnabi(self.tcx, fn_abi, options) + typeid::from_fnabi(self.tcx, fn_abi, options) }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); @@ -1688,9 +1686,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } let kcfi_typeid = if let Some(instance) = instance { - kcfi_typeid_for_instance(self.tcx, instance, options) + typeid::from_instance_kcfi(self.tcx, instance, options) } else { - kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + typeid::from_fnabi_kcfi(self.tcx, fn_abi, options) }; Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 298ac159564a0..6cef757e3b15a 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,9 +22,7 @@ use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; -use rustc_symbol_mangling::typeid::{ - self, kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, -}; +use rustc_symbol_mangling::typeid; use smallvec::SmallVec; /// Declare a function. @@ -152,7 +150,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { .powerset() .map(typeid::Options::from_iter) { - let typeid = typeid_for_instance(self.tcx, instance, options); + let typeid = typeid::from_instance(self.tcx, instance, options); if typeids.insert(typeid.clone()) { self.add_type_metadata(llfn, typeid); } @@ -164,7 +162,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { .powerset() .map(typeid::Options::from_iter) { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = typeid::from_fnabi(self.tcx, fn_abi, options); self.add_type_metadata(llfn, typeid); } } @@ -183,10 +181,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } if let Some(instance) = instance { - let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, instance, options); + let kcfi_typeid = typeid::from_instance_kcfi(self.tcx, instance, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } else { - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = typeid::from_fnabi_kcfi(self.tcx, fn_abi, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } } diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index faaec0e1e05ad..31e902ebb813c 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -25,7 +25,7 @@ bitflags! { /// CFI and KCFI support. const NORMALIZE_INTEGERS = 4; /// Generalize the instance by erasing the concrete `Self` type where possible. - /// Only has an effect on `{kcfi_,}typeid_for_instance`. + /// Only has an effect on `from_instance{_kcfi,}`. const ERASE_SELF_TYPE = 8; } } @@ -35,7 +35,7 @@ mod itanium_cxx_abi; mod ty; /// Returns a type metadata identifier for the specified FnAbi. -pub fn typeid_for_fnabi<'tcx>( +pub fn from_fnabi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, options: Options, @@ -44,7 +44,7 @@ pub fn typeid_for_fnabi<'tcx>( } /// Returns a type metadata identifier for the specified Instance. -pub fn typeid_for_instance<'tcx>( +pub fn from_instance<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, options: Options, @@ -59,7 +59,7 @@ pub fn typeid_for_instance<'tcx>( } /// Returns a KCFI type metadata identifier for the specified FnAbi. -pub fn kcfi_typeid_for_fnabi<'tcx>( +pub fn from_fnabi_kcfi<'tcx>( tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, options: Options, @@ -72,7 +72,7 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( } /// Returns a KCFI type metadata identifier for the specified Instance. -pub fn kcfi_typeid_for_instance<'tcx>( +pub fn from_instance_kcfi<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, mut options: Options, @@ -85,6 +85,6 @@ pub fn kcfi_typeid_for_instance<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write(from_instance(tcx, instance, options).as_bytes()); hash.finish() as u32 }