diff options
| author | Tshepang Mbambo <hopsi@tuta.io> | 2025-07-17 06:25:42 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-17 06:25:42 +0200 |
| commit | b2474c87e1e4a8f01ba935084bb99804942de91b (patch) | |
| tree | 5e7f3165b5beac3fe72f520dbb94be30b73101bb /compiler/rustc_codegen_ssa/src | |
| parent | c113a60c0417ed9b9531648be9aa3d3c82a4524f (diff) | |
| parent | ce0de761f333204d931d2d43a96468986f51ebbd (diff) | |
| download | rust-b2474c87e1e4a8f01ba935084bb99804942de91b.tar.gz rust-b2474c87e1e4a8f01ba935084bb99804942de91b.zip | |
Merge pull request #2508 from rust-lang/rustc-pull
Rustc pull update
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/linker.rs | 41 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/back/write.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/base.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 71 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/errors.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/meth.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/analyze.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/mod.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/operand.rs | 267 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/place.rs | 128 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 357 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/statement.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/traits/builder.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/traits/intrinsic.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/traits/type_.rs | 6 |
17 files changed, 500 insertions, 516 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 343cb0eeca9..b46773396fc 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1379,7 +1379,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { } } - let features = sess.opts.unstable_opts.linker_features; + let features = sess.opts.cg.linker_features; // linker and linker flavor specified via command line have precedence over what the target // specification specifies @@ -3327,35 +3327,6 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); - - // On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some - // section garbage-collection features. For example, the somewhat popular `linkme` crate and - // its dependents rely in practice on this difference: when using lld, they need `-z - // nostart-stop-gc` to prevent encapsulation symbols and sections from being - // garbage-collected. - // - // More information about all this can be found in: - // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order - // - https://lld.llvm.org/ELF/start-stop-gc - // - // So when using lld, we restore, for now, the traditional behavior to help migration, but - // will remove it in the future. - // Since this only disables an optimization, it shouldn't create issues, but is in theory - // slightly suboptimal. However, it: - // - doesn't have any visible impact on our benchmarks - // - reduces the need to disable lld for the crates that depend on this - // - // Note that lld can detect some cases where this difference is relied on, and emits a - // dedicated error to add this link arg. We could make use of this error to emit an FCW. As - // of writing this, we don't do it, because lld is already enabled by default on nightly - // without this mitigation: no working project would see the FCW, so we do this to help - // stabilization. - // - // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the - // future. - if sess.target.llvm_target == "x86_64-unknown-linux-gnu" { - cmd.link_arg("-znostart-stop-gc"); - } } if !flavor.is_gnu() { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 1896f63bd2d..e0a3ad55be0 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -800,9 +800,7 @@ impl<'a> Linker for GccLinker<'a> { return; } - let is_windows = self.sess.target.is_like_windows; - let path = tmpdir.join(if is_windows { "list.def" } else { "list" }); - + let path = tmpdir.join(if self.sess.target.is_like_windows { "list.def" } else { "list" }); debug!("EXPORTED SYMBOLS:"); if self.sess.target.is_like_darwin { @@ -817,7 +815,8 @@ impl<'a> Linker for GccLinker<'a> { if let Err(error) = res { self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); } - } else if is_windows { + self.link_arg("-exported_symbols_list").link_arg(path); + } else if self.sess.target.is_like_windows { let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; @@ -835,6 +834,21 @@ impl<'a> Linker for GccLinker<'a> { if let Err(error) = res { self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); } + self.link_arg(path); + } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { + let res: io::Result<()> = try { + let mut f = File::create_buffered(&path)?; + writeln!(f, "{{")?; + for (sym, _) in symbols { + debug!(sym); + writeln!(f, " {sym};")?; + } + writeln!(f, "}};")?; + }; + if let Err(error) = res { + self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); + } + self.link_arg("--dynamic-list").link_arg(path); } else { // Write an LD version script let res: io::Result<()> = try { @@ -852,18 +866,13 @@ impl<'a> Linker for GccLinker<'a> { if let Err(error) = res { self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); } - } - - if self.sess.target.is_like_darwin { - self.link_arg("-exported_symbols_list").link_arg(path); - } else if self.sess.target.is_like_solaris { - self.link_arg("-M").link_arg(path); - } else if is_windows { - self.link_arg(path); - } else { - let mut arg = OsString::from("--version-script="); - arg.push(path); - self.link_arg(arg).link_arg("--no-undefined-version"); + if self.sess.target.is_like_solaris { + self.link_arg("-M").link_arg(path); + } else { + let mut arg = OsString::from("--version-script="); + arg.push(path); + self.link_arg(arg).link_arg("--no-undefined-version"); + } } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8330e4f7af0..d2a64ec2993 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1208,7 +1208,7 @@ fn start_executing_work<B: ExtraBackendMethods>( split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel: backend.supports_parallel() && !sess.opts.unstable_opts.no_parallel_backend, - pointer_size: tcx.data_layout.pointer_size, + pointer_size: tcx.data_layout.pointer_size(), invocation_temp: sess.invocation_temp.clone(), }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 102d4ea2fa6..18581f854b6 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -200,7 +200,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let vptr_entry_idx = cx.tcx().supertrait_vtable_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { - let ptr_size = bx.data_layout().pointer_size; + let ptr_size = bx.data_layout().pointer_size(); let vtable_byte_offset = u64::try_from(entry_idx).unwrap() * ptr_size.bytes(); load_vtable(bx, old_info, bx.type_ptr(), vtable_byte_offset, source, true) } else { @@ -577,8 +577,8 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> (Bx::Va // Params for UEFI let param_handle = bx.get_param(0); let param_system_table = bx.get_param(1); - let ptr_size = bx.tcx().data_layout.pointer_size; - let ptr_align = bx.tcx().data_layout.pointer_align.abi; + let ptr_size = bx.tcx().data_layout.pointer_size(); + let ptr_align = bx.tcx().data_layout.pointer_align().abi; let arg_argc = bx.const_int(bx.cx().type_isize(), 2); let arg_argv = bx.alloca(2 * ptr_size, ptr_align); bx.store(param_handle, arg_argv, ptr_align); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 2713ec07f97..dd49db26689 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,7 +4,7 @@ use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ - AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, UsedBy, find_attr, + AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -109,14 +109,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let hir::Attribute::Parsed(p) = attr { match p { - AttributeKind::Repr(reprs) => { - codegen_fn_attrs.alignment = reprs - .iter() - .filter_map( - |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None }, - ) - .max(); - } AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => { codegen_fn_attrs.export_name = Some(*name); @@ -124,6 +116,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), + AttributeKind::LinkOrdinal { ordinal, span } => { + codegen_fn_attrs.link_ordinal = Some(*ordinal); + link_ordinal_span = Some(*span); + } AttributeKind::LinkSection { name, .. } => { codegen_fn_attrs.link_section = Some(*name) } @@ -211,6 +207,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, }, + AttributeKind::FfiConst(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST + } + AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, + AttributeKind::StdInternalSymbol(_) => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + } _ => {} } } @@ -221,17 +224,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { match name { sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR, - sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE, - sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST, sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND, sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR, sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR, sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::rustc_std_internal_symbol => { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL - } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::linkage => { if let Some(val) = attr.value_str() { @@ -256,12 +254,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } } - sym::link_ordinal => { - link_ordinal_span = Some(attr.span()); - if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { - codegen_fn_attrs.link_ordinal = ordinal; - } - } sym::no_sanitize => { no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { @@ -574,45 +566,6 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> { tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment } -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option<u16> { - use rustc_ast::{LitIntType, LitKind, MetaItemLit}; - let meta_item_list = attr.meta_item_list()?; - let [sole_meta_list] = &meta_item_list[..] else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() }); - return None; - }; - if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = - sole_meta_list.lit() - { - // According to the table at - // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the - // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined - // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import - // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. - // - // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for - // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that - // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import - // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an - // import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I - // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL -- - // see earlier comment about LINK.EXE failing.) - if *ordinal <= u16::MAX as u128 { - Some(ordinal.get() as u16) - } else { - let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); - tcx.dcx() - .struct_span_err(attr.span(), msg) - .with_note("the value may not exceed `u16::MAX`") - .emit(); - None - } - } else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() }); - None - } -} - fn check_link_name_xor_ordinal( tcx: TyCtxt<'_>, codegen_fn_attrs: &CodegenFnAttrs, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 086c069745c..9040915b6af 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -457,7 +457,7 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { } else if arg.as_encoded_bytes().ends_with(b".rlib") { let rlib_path = Path::new(&arg); let dir = rlib_path.parent().unwrap(); - let filename = rlib_path.file_name().unwrap().to_owned(); + let filename = rlib_path.file_stem().unwrap().to_owned(); if let Some(ArgGroup::Rlibs(parent, rlibs)) = args.last_mut() { if parent == dir { rlibs.push(filename); @@ -471,7 +471,7 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { args.push(ArgGroup::Regular(arg)); } } - let crate_hash = regex::bytes::Regex::new(r"-[0-9a-f]+\.rlib$").unwrap(); + let crate_hash = regex::bytes::Regex::new(r"-[0-9a-f]+").unwrap(); self.command.args(args.into_iter().map(|arg_group| { match arg_group { // SAFETY: we are only matching on ASCII, not any surrogate pairs, so any replacements we do will still be valid. @@ -494,7 +494,11 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { Err(_) => false, }; let mut arg = dir.into_os_string(); - arg.push("/{"); + arg.push("/"); + let needs_braces = rlibs.len() >= 2; + if needs_braces { + arg.push("{"); + } let mut first = true; for mut rlib in rlibs { if !first { @@ -513,7 +517,10 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { } arg.push(rlib); } - arg.push("}.rlib"); + if needs_braces { + arg.push("}"); + } + arg.push(".rlib"); arg } } @@ -1102,22 +1109,6 @@ pub(crate) struct InvalidNoSanitize { } #[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_link_ordinal_nargs)] -#[note] -pub(crate) struct InvalidLinkOrdinalNargs { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_illegal_link_ordinal_format)] -#[note] -pub(crate) struct InvalidLinkOrdinalFormat { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] pub(crate) struct TargetFeatureSafeTrait { #[primary_span] diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 3a11ce6befb..34ad35a729b 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -27,7 +27,7 @@ impl<'a, 'tcx> VirtualIndex { debug!("get_fn({llvtable:?}, {ty:?}, {self:?})"); let llty = bx.fn_ptr_backend_type(fn_abi); - let ptr_size = bx.data_layout().pointer_size; + let ptr_size = bx.data_layout().pointer_size(); let vtable_byte_offset = self.0 * ptr_size.bytes(); load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, nonnull) @@ -63,7 +63,7 @@ impl<'a, 'tcx> VirtualIndex { debug!("get_int({:?}, {:?})", llvtable, self); let llty = bx.type_isize(); - let ptr_size = bx.data_layout().pointer_size; + let ptr_size = bx.data_layout().pointer_size(); let vtable_byte_offset = self.0 * ptr_size.bytes(); load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, false) @@ -115,7 +115,7 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); let vtable_const = cx.const_data_from_alloc(vtable_allocation); - let align = cx.data_layout().pointer_align.abi; + let align = cx.data_layout().pointer_align().abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); @@ -133,13 +133,14 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ty: Ty<'tcx>, nonnull: bool, ) -> Bx::Value { - let ptr_align = bx.data_layout().pointer_align.abi; + let ptr_align = bx.data_layout().pointer_align().abi; if bx.cx().sess().opts.unstable_opts.virtual_function_elimination && bx.cx().sess().lto() == Lto::Fat { if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) { - let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap(); + let typeid = + bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref).as_bytes()).unwrap(); let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); return func; } else if nonnull { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 99f35b79208..6d6465dd798 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -171,8 +171,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer if let Some(local) = place.as_local() { self.define(local, DefLocation::Assignment(location)); if self.locals[local] != LocalKind::Memory { - let decl_span = self.fx.mir.local_decls[local].source_info.span; - if !self.fx.rvalue_creates_operand(rvalue, decl_span) { + if !self.fx.rvalue_creates_operand(rvalue) { self.locals[local] = LocalKind::Memory; } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 10b44a1faf0..fa69820d5d2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -140,8 +140,13 @@ enum LocalRef<'tcx, V> { Place(PlaceRef<'tcx, V>), /// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place). /// `*p` is the wide pointer that references the actual unsized place. - /// Every time it is initialized, we have to reallocate the place - /// and update the wide pointer. That's the reason why it is indirect. + /// + /// MIR only supports unsized args, not dynamically-sized locals, so + /// new unsized temps don't exist and we must reuse the referred-to place. + /// + /// FIXME: Since the removal of unsized locals in <https://github.com/rust-lang/rust/pull/142911>, + /// can we maybe use `Place` here? Or refactor it in another way? There are quite a few + /// `UnsizedPlace => bug` branches now. UnsizedPlace(PlaceRef<'tcx, V>), /// The backend [`OperandValue`] has already been generated. Operand(OperandRef<'tcx, V>), @@ -498,7 +503,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) } } - // Unsized indirect qrguments + // Unsized indirect arguments PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { // As the storage for the indirect argument lives during // the whole function call, we just copy the wide pointer. diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 9da4b8cc8fd..beaf8950978 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -326,7 +326,7 @@ fn prefix_and_suffix<'tcx>( fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { let mut signature = String::with_capacity(64); - let ptr_type = match tcx.data_layout.pointer_size.bits() { + let ptr_type = match tcx.data_layout.pointer_size().bits() { 32 => "i32", 64 => "i64", other => bug!("wasm pointer size cannot be {other} bits"), diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index da615cc9a00..b0d191528a8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -1,5 +1,6 @@ use std::fmt; +use itertools::Either; use rustc_abi as abi; use rustc_abi::{ Align, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, TagEncoding, VariantIdx, Variants, @@ -13,11 +14,11 @@ use rustc_session::config::OptLevel; use tracing::{debug, instrument}; use super::place::{PlaceRef, PlaceValue}; -use super::rvalue::transmute_immediate; +use super::rvalue::transmute_scalar; use super::{FunctionCx, LocalRef}; +use crate::MemFlags; use crate::common::IntPredicate; use crate::traits::*; -use crate::{MemFlags, size_of_val}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -185,7 +186,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { offset: Size, ) -> Self { let alloc_align = alloc.inner().align; - assert!(alloc_align >= layout.align.abi); + assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi); let read_scalar = |start, size, s: abi::Scalar, ty| { match alloc.0.read_scalar( @@ -346,14 +347,16 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let val = if field.is_zst() { OperandValue::ZeroSized + } else if let BackendRepr::SimdVector { .. } = self.layout.backend_repr { + // codegen_transmute_operand doesn't support SIMD, but since the previous + // check handled ZSTs, the only possible field access into something SIMD + // is to the `non_1zst_field` that's the same SIMD. (Other things, even + // just padding, would change the wrapper's representation type.) + assert_eq!(field.size, self.layout.size); + self.val } else if field.size == self.layout.size { assert_eq!(offset.bytes(), 0); - fx.codegen_transmute_operand(bx, *self, field).unwrap_or_else(|| { - bug!( - "Expected `codegen_transmute_operand` to handle equal-size \ - field {i:?} projection from {self:?} to {field:?}" - ) - }) + fx.codegen_transmute_operand(bx, *self, field) } else { let (in_scalar, imm) = match (self.val, self.layout.backend_repr) { // Extract a scalar component from a pair. @@ -562,102 +565,198 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } } } +} + +/// Each of these variants starts out as `Either::Right` when it's uninitialized, +/// then setting the field changes that to `Either::Left` with the backend value. +#[derive(Debug, Copy, Clone)] +enum OperandValueBuilder<V> { + ZeroSized, + Immediate(Either<V, abi::Scalar>), + Pair(Either<V, abi::Scalar>, Either<V, abi::Scalar>), + /// `repr(simd)` types need special handling because they each have a non-empty + /// array field (which uses [`OperandValue::Ref`]) despite the SIMD type itself + /// using [`OperandValue::Immediate`] which for any other kind of type would + /// mean that its one non-ZST field would also be [`OperandValue::Immediate`]. + Vector(Either<V, ()>), +} + +/// Allows building up an `OperandRef` by setting fields one at a time. +#[derive(Debug, Copy, Clone)] +pub(super) struct OperandRefBuilder<'tcx, V> { + val: OperandValueBuilder<V>, + layout: TyAndLayout<'tcx>, +} - /// Creates an incomplete operand containing the [`abi::Scalar`]s expected based - /// on the `layout` passed. This is for use with [`OperandRef::insert_field`] - /// later to set the necessary immediate(s). +impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { + /// Creates an uninitialized builder for an instance of the `layout`. /// - /// Returns `None` for `layout`s which cannot be built this way. - pub(crate) fn builder( - layout: TyAndLayout<'tcx>, - ) -> Option<OperandRef<'tcx, Result<V, abi::Scalar>>> { + /// ICEs for [`BackendRepr::Memory`] types (other than ZSTs), which should + /// be built up inside a [`PlaceRef`] instead as they need an allocated place + /// into which to write the values of the fields. + pub(super) fn new(layout: TyAndLayout<'tcx>) -> Self { let val = match layout.backend_repr { - BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized, - BackendRepr::Scalar(s) => OperandValue::Immediate(Err(s)), - BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Err(a), Err(b)), - BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None, + BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized, + BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)), + BackendRepr::ScalarPair(a, b) => { + OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) + } + BackendRepr::SimdVector { .. } => OperandValueBuilder::Vector(Either::Right(())), + BackendRepr::Memory { .. } => { + bug!("Cannot use non-ZST Memory-ABI type in operand builder: {layout:?}"); + } }; - Some(OperandRef { val, layout }) + OperandRefBuilder { val, layout } } -} -impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Result<V, abi::Scalar>> { - pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + pub(super) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( &mut self, bx: &mut Bx, - v: VariantIdx, - f: FieldIdx, - operand: OperandRef<'tcx, V>, + variant: VariantIdx, + field: FieldIdx, + field_operand: OperandRef<'tcx, V>, ) { - let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields { + if let OperandValue::ZeroSized = field_operand.val { + // A ZST never adds any state, so just ignore it. + // This special-casing is worth it because of things like + // `Result<!, !>` where `Ok(never)` is legal to write, + // but the type shows as FieldShape::Primitive so we can't + // actually look at the layout for the field being set. + return; + } + + let is_zero_offset = if let abi::FieldsShape::Primitive = self.layout.fields { // The other branch looking at field layouts ICEs for primitives, // so we need to handle them separately. - // Multiple fields is possible for cases such as aggregating - // a thin pointer, where the second field is the unit. + // Because we handled ZSTs above (like the metadata in a thin pointer), + // the only possibility is that we're setting the one-and-only field. assert!(!self.layout.is_zst()); - assert_eq!(v, FIRST_VARIANT); - let first_field = f == FieldIdx::ZERO; - (!first_field, first_field) + assert_eq!(variant, FIRST_VARIANT); + assert_eq!(field, FieldIdx::ZERO); + true } else { - let variant_layout = self.layout.for_variant(bx.cx(), v); - let field_layout = variant_layout.field(bx.cx(), f.as_usize()); - let field_offset = variant_layout.fields.offset(f.as_usize()); - (field_layout.is_zst(), field_offset == Size::ZERO) + let variant_layout = self.layout.for_variant(bx.cx(), variant); + let field_offset = variant_layout.fields.offset(field.as_usize()); + field_offset == Size::ZERO }; - let mut update = |tgt: &mut Result<V, abi::Scalar>, src, from_scalar| { - let from_bty = bx.cx().type_from_scalar(from_scalar); - let to_scalar = tgt.unwrap_err(); - let to_bty = bx.cx().type_from_scalar(to_scalar); - let imm = transmute_immediate(bx, src, from_scalar, from_bty, to_scalar, to_bty); - *tgt = Ok(imm); + let mut update = |tgt: &mut Either<V, abi::Scalar>, src, from_scalar| { + let to_scalar = tgt.unwrap_right(); + // We transmute here (rather than just `from_immediate`) because in + // `Result<usize, *const ()>` the field of the `Ok` is an integer, + // but the corresponding scalar in the enum is a pointer. + let imm = transmute_scalar(bx, src, from_scalar, to_scalar); + *tgt = Either::Left(imm); }; - match (operand.val, operand.layout.backend_repr) { - (OperandValue::ZeroSized, _) if expect_zst => {} + match (field_operand.val, field_operand.layout.backend_repr) { + (OperandValue::ZeroSized, _) => unreachable!("Handled above"), (OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val { - OperandValue::Immediate(val @ Err(_)) if is_zero_offset => { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { update(val, v, from_scalar); } - OperandValue::Pair(fst @ Err(_), _) if is_zero_offset => { + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { update(fst, v, from_scalar); } - OperandValue::Pair(_, snd @ Err(_)) if !is_zero_offset => { + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { update(snd, v, from_scalar); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, + (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) if is_zero_offset => { + *val = Either::Left(v); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } }, (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => { match &mut self.val { - OperandValue::Pair(fst @ Err(_), snd @ Err(_)) => { + OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { update(fst, a, from_sa); update(snd, b, from_sb); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => bug!( + "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}" + ), + } + } + (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) => { + let ibty = bx.cx().immediate_backend_type(self.layout); + let simd = bx.load_from_place(ibty, place); + *val = Either::Left(simd); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") } + }, + _ => bug!("Operand cannot be used with `insert_field`: {field_operand:?}"), + } + } + + /// Insert the immediate value `imm` for field `f` in the *type itself*, + /// rather than into one of the variants. + /// + /// Most things want [`Self::insert_field`] instead, but this one is + /// necessary for writing things like enum tags that aren't in any variant. + pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) { + let field_offset = self.layout.fields.offset(f.as_usize()); + let is_zero_offset = field_offset == Size::ZERO; + match &mut self.val { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { + *val = Either::Left(imm); + } + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { + *fst = Either::Left(imm); } - _ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"), + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { + *snd = Either::Left(imm); + } + _ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"), } } - /// After having set all necessary fields, this converts the - /// `OperandValue<Result<V, _>>` (as obtained from [`OperandRef::builder`]) - /// to the normal `OperandValue<V>`. + /// After having set all necessary fields, this converts the builder back + /// to the normal `OperandRef`. /// /// ICEs if any required fields were not set. - pub fn build(&self) -> OperandRef<'tcx, V> { - let OperandRef { val, layout } = *self; - - let unwrap = |r: Result<V, abi::Scalar>| match r { - Ok(v) => v, - Err(_) => bug!("OperandRef::build called while fields are missing {self:?}"), + pub(super) fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> { + let OperandRefBuilder { val, layout } = *self; + + // For something like `Option::<u32>::None`, it's expected that the + // payload scalar will not actually have been set, so this converts + // unset scalars to corresponding `undef` values so long as the scalar + // from the layout allows uninit. + let unwrap = |r: Either<V, abi::Scalar>| match r { + Either::Left(v) => v, + Either::Right(s) if s.is_uninit_valid() => { + let bty = cx.type_from_scalar(s); + cx.const_undef(bty) + } + Either::Right(_) => bug!("OperandRef::build called while fields are missing {self:?}"), }; let val = match val { - OperandValue::ZeroSized => OperandValue::ZeroSized, - OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)), - OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), - OperandValue::Ref(_) => bug!(), + OperandValueBuilder::ZeroSized => OperandValue::ZeroSized, + OperandValueBuilder::Immediate(v) => OperandValue::Immediate(unwrap(v)), + OperandValueBuilder::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), + OperandValueBuilder::Vector(v) => match v { + Either::Left(v) => OperandValue::Immediate(v), + Either::Right(()) + if let BackendRepr::SimdVector { element, .. } = layout.backend_repr + && element.is_uninit_valid() => + { + let bty = cx.immediate_backend_type(layout); + OperandValue::Immediate(cx.const_undef(bty)) + } + Either::Right(()) => { + bug!("OperandRef::build called while fields are missing {self:?}") + } + }, }; OperandRef { val, layout } } @@ -762,44 +861,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { } } } - - pub fn store_unsized<Bx: BuilderMethods<'a, 'tcx, Value = V>>( - self, - bx: &mut Bx, - indirect_dest: PlaceRef<'tcx, V>, - ) { - debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest); - // `indirect_dest` must have `*mut T` type. We extract `T` out of it. - let unsized_ty = indirect_dest - .layout - .ty - .builtin_deref(true) - .unwrap_or_else(|| bug!("indirect_dest has non-pointer type: {:?}", indirect_dest)); - - let OperandValue::Ref(PlaceValue { llval: llptr, llextra: Some(llextra), .. }) = self - else { - bug!("store_unsized called with a sized value (or with an extern type)") - }; - - // Allocate an appropriate region on the stack, and copy the value into it. Since alloca - // doesn't support dynamic alignment, we allocate an extra align - 1 bytes, and align the - // pointer manually. - let (size, align) = size_of_val::size_and_align_of_dst(bx, unsized_ty, Some(llextra)); - let one = bx.const_usize(1); - let align_minus_1 = bx.sub(align, one); - let size_extra = bx.add(size, align_minus_1); - let min_align = Align::ONE; - let alloca = bx.dynamic_alloca(size_extra, min_align); - let address = bx.ptrtoint(alloca, bx.type_isize()); - let neg_address = bx.neg(address); - let offset = bx.and(neg_address, align_minus_1); - let dst = bx.inbounds_ptradd(alloca, offset); - bx.memcpy(dst, min_align, llptr, min_align, size, MemFlags::empty()); - - // Store the allocated region and the extra to the indirect place. - let indirect_operand = OperandValue::Pair(dst, llextra); - indirect_operand.store(bx, indirect_dest); - } } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 937063c24a6..0090be9fdef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,4 +1,6 @@ -use rustc_abi::{Align, BackendRepr, FieldsShape, Size, TagEncoding, VariantIdx, Variants}; +use rustc_abi::{ + Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants, +}; use rustc_middle::mir::PlaceTy; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; @@ -239,53 +241,17 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, variant_index: VariantIdx, ) { - if self.layout.for_variant(bx.cx(), variant_index).is_uninhabited() { - // We play it safe by using a well-defined `abort`, but we could go for immediate UB - // if that turns out to be helpful. - bx.abort(); - return; - } - match self.layout.variants { - Variants::Empty => unreachable!("we already handled uninhabited types"), - Variants::Single { index } => assert_eq!(index, variant_index), - - Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { - let ptr = self.project_field(bx, tag_field.as_usize()); - let to = - self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; - bx.store_to_place( - bx.cx().const_uint_big(bx.cx().backend_type(ptr.layout), to), - ptr.val, - ); + match codegen_tag_value(bx.cx(), variant_index, self.layout) { + Err(UninhabitedVariantError) => { + // We play it safe by using a well-defined `abort`, but we could go for immediate UB + // if that turns out to be helpful. + bx.abort(); } - Variants::Multiple { - tag_encoding: - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, - tag_field, - .. - } => { - if variant_index != untagged_variant { - let niche = self.project_field(bx, tag_field.as_usize()); - let niche_llty = bx.cx().immediate_backend_type(niche.layout); - let BackendRepr::Scalar(scalar) = niche.layout.backend_repr else { - bug!("expected a scalar placeref for the niche"); - }; - // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping - // around the `niche`'s type. - // The easiest way to do that is to do wrapping arithmetic on `u128` and then - // masking off any extra bits that occur because we did the arithmetic with too many bits. - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); - let niche_value = (niche_value as u128).wrapping_add(niche_start); - let niche_value = niche_value & niche.layout.size.unsigned_int_max(); - - let niche_llval = bx.cx().scalar_to_backend( - Scalar::from_uint(niche_value, niche.layout.size), - scalar, - niche_llty, - ); - OperandValue::Immediate(niche_llval).store(bx, niche); - } + Ok(Some((tag_field, imm))) => { + let tag_place = self.project_field(bx, tag_field.as_usize()); + OperandValue::Immediate(imm).store(bx, tag_place); } + Ok(None) => {} } } @@ -471,3 +437,73 @@ fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let offset = bx.and(neg_value, align_minus_1); bx.add(value, offset) } + +/// Calculates the value that needs to be stored to mark the discriminant. +/// +/// This might be `None` for a `struct` or a niched variant (like `Some(&3)`). +/// +/// If it's `Some`, it returns the value to store and the field in which to +/// store it. Note that this value is *not* the same as the discriminant, in +/// general, as it might be a niche value or have a different size. +/// +/// It might also be an `Err` because the variant is uninhabited. +pub(super) fn codegen_tag_value<'tcx, V>( + cx: &impl CodegenMethods<'tcx, Value = V>, + variant_index: VariantIdx, + layout: TyAndLayout<'tcx>, +) -> Result<Option<(FieldIdx, V)>, UninhabitedVariantError> { + // By checking uninhabited-ness first we don't need to worry about types + // like `(u32, !)` which are single-variant but weird. + if layout.for_variant(cx, variant_index).is_uninhabited() { + return Err(UninhabitedVariantError); + } + + Ok(match layout.variants { + Variants::Empty => unreachable!("we already handled uninhabited types"), + Variants::Single { index } => { + assert_eq!(index, variant_index); + None + } + + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { + let discr = layout.ty.discriminant_for_variant(cx.tcx(), variant_index); + let to = discr.unwrap().val; + let tag_layout = layout.field(cx, tag_field.as_usize()); + let tag_llty = cx.immediate_backend_type(tag_layout); + let imm = cx.const_uint_big(tag_llty, to); + Some((tag_field, imm)) + } + Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, + tag_field, + .. + } => { + if variant_index != untagged_variant { + let niche_layout = layout.field(cx, tag_field.as_usize()); + let niche_llty = cx.immediate_backend_type(niche_layout); + let BackendRepr::Scalar(scalar) = niche_layout.backend_repr else { + bug!("expected a scalar placeref for the niche"); + }; + // We are supposed to compute `niche_value.wrapping_add(niche_start)` wrapping + // around the `niche`'s type. + // The easiest way to do that is to do wrapping arithmetic on `u128` and then + // masking off any extra bits that occur because we did the arithmetic with too many bits. + let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = (niche_value as u128).wrapping_add(niche_start); + let niche_value = niche_value & niche_layout.size.unsigned_int_max(); + + let niche_llval = cx.scalar_to_backend( + Scalar::from_uint(niche_value, niche_layout.size), + scalar, + niche_llty, + ); + Some((tag_field, niche_llval)) + } else { + None + } + } + }) +} + +#[derive(Debug)] +pub(super) struct UninhabitedVariantError; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 60cf4e28b5a..e90463aacc8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,18 +1,15 @@ -use std::assert_matches::assert_matches; - use rustc_abi::{self as abi, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_middle::{bug, mir, span_bug}; +use rustc_middle::{bug, mir}; use rustc_session::config::OptLevel; -use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use super::operand::{OperandRef, OperandValue}; -use super::place::PlaceRef; +use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; +use super::place::{PlaceRef, codegen_tag_value}; use super::{FunctionCx, LocalRef}; -use crate::common::IntPredicate; +use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; @@ -183,13 +180,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP)); + assert!(self.rvalue_creates_operand(rvalue)); let temp = self.codegen_rvalue_operand(bx, rvalue); temp.val.store(bx, dest); } } } + /// Transmutes the `src` value to the destination type by writing it to `dst`. + /// + /// See also [`Self::codegen_transmute_operand`] for cases that can be done + /// without needing a pre-allocated place for the destination. fn codegen_transmute( &mut self, bx: &mut Bx, @@ -200,115 +201,71 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { assert!(src.layout.is_sized()); assert!(dst.layout.is_sized()); - if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) { - val.store(bx, dst); - return; - } - - match src.val { - OperandValue::Ref(..) | OperandValue::ZeroSized => { - span_bug!( - self.mir.span, - "Operand path should have handled transmute \ - from {src:?} to place {dst:?}" - ); - } - OperandValue::Immediate(..) | OperandValue::Pair(..) => { - // When we have immediate(s), the alignment of the source is irrelevant, - // so we can store them using the destination's alignment. - src.val.store(bx, dst.val.with_type(src.layout)); - } + if src.layout.size != dst.layout.size + || src.layout.is_uninhabited() + || dst.layout.is_uninhabited() + { + // These cases are all UB to actually hit, so don't emit code for them. + // (The size mismatches are reachable via `transmute_unchecked`.) + bx.unreachable_nonterminator(); + } else { + // Since in this path we have a place anyway, we can store or copy to it, + // making sure we use the destination place's alignment even if the + // source would normally have a higher one. + src.val.store(bx, dst.val.with_type(src.layout)); } } - /// Attempts to transmute an `OperandValue` to another `OperandValue`. + /// Transmutes an `OperandValue` to another `OperandValue`. /// - /// Returns `None` for cases that can't work in that framework, such as for - /// `Immediate`->`Ref` that needs an `alloc` to get the location. + /// This is supported only for cases where [`Self::rvalue_creates_operand`] + /// returns `true`, and will ICE otherwise. (In particular, anything that + /// would need to `alloca` in order to return a `PlaceValue` will ICE, + /// expecting those to go via [`Self::codegen_transmute`] instead where + /// the destination place is already allocated.) pub(crate) fn codegen_transmute_operand( &mut self, bx: &mut Bx, operand: OperandRef<'tcx, Bx::Value>, cast: TyAndLayout<'tcx>, - ) -> Option<OperandValue<Bx::Value>> { + ) -> OperandValue<Bx::Value> { // Check for transmutes that are always UB. if operand.layout.size != cast.size || operand.layout.is_uninhabited() || cast.is_uninhabited() { - if !operand.layout.is_uninhabited() { - // Since this is known statically and the input could have existed - // without already having hit UB, might as well trap for it. - bx.abort(); - } + bx.unreachable_nonterminator(); - // Because this transmute is UB, return something easy to generate, - // since it's fine that later uses of the value are probably UB. - return Some(OperandValue::poison(bx, cast)); + // We still need to return a value of the appropriate type, but + // it's already UB so do the easiest thing available. + return OperandValue::poison(bx, cast); } - let operand_kind = self.value_kind(operand.layout); - let cast_kind = self.value_kind(cast); - - match operand.val { - OperandValue::Ref(source_place_val) => { + match (operand.val, operand.layout.backend_repr, cast.backend_repr) { + _ if cast.is_zst() => OperandValue::ZeroSized, + (_, _, abi::BackendRepr::Memory { .. }) => { + bug!("Cannot `codegen_transmute_operand` to non-ZST memory-ABI output {cast:?}"); + } + (OperandValue::Ref(source_place_val), abi::BackendRepr::Memory { .. }, _) => { assert_eq!(source_place_val.llextra, None); - assert_matches!(operand_kind, OperandValueKind::Ref); // The existing alignment is part of `source_place_val`, // so that alignment will be used, not `cast`'s. - Some(bx.load_operand(source_place_val.with_type(cast)).val) - } - OperandValue::ZeroSized => { - let OperandValueKind::ZeroSized = operand_kind else { - bug!("Found {operand_kind:?} for operand {operand:?}"); - }; - if let OperandValueKind::ZeroSized = cast_kind { - Some(OperandValue::ZeroSized) - } else { - None - } - } - OperandValue::Immediate(imm) => { - let OperandValueKind::Immediate(from_scalar) = operand_kind else { - bug!("Found {operand_kind:?} for operand {operand:?}"); - }; - if let OperandValueKind::Immediate(to_scalar) = cast_kind - && from_scalar.size(self.cx) == to_scalar.size(self.cx) - { - let from_backend_ty = bx.backend_type(operand.layout); - let to_backend_ty = bx.backend_type(cast); - Some(OperandValue::Immediate(transmute_immediate( - bx, - imm, - from_scalar, - from_backend_ty, - to_scalar, - to_backend_ty, - ))) - } else { - None - } - } - OperandValue::Pair(imm_a, imm_b) => { - let OperandValueKind::Pair(in_a, in_b) = operand_kind else { - bug!("Found {operand_kind:?} for operand {operand:?}"); - }; - if let OperandValueKind::Pair(out_a, out_b) = cast_kind - && in_a.size(self.cx) == out_a.size(self.cx) - && in_b.size(self.cx) == out_b.size(self.cx) - { - let in_a_ibty = bx.scalar_pair_element_backend_type(operand.layout, 0, false); - let in_b_ibty = bx.scalar_pair_element_backend_type(operand.layout, 1, false); - let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false); - let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false); - Some(OperandValue::Pair( - transmute_immediate(bx, imm_a, in_a, in_a_ibty, out_a, out_a_ibty), - transmute_immediate(bx, imm_b, in_b, in_b_ibty, out_b, out_b_ibty), - )) - } else { - None - } + bx.load_operand(source_place_val.with_type(cast)).val } + ( + OperandValue::Immediate(imm), + abi::BackendRepr::Scalar(from_scalar), + abi::BackendRepr::Scalar(to_scalar), + ) => OperandValue::Immediate(transmute_scalar(bx, imm, from_scalar, to_scalar)), + ( + OperandValue::Pair(imm_a, imm_b), + abi::BackendRepr::ScalarPair(in_a, in_b), + abi::BackendRepr::ScalarPair(out_a, out_b), + ) => OperandValue::Pair( + transmute_scalar(bx, imm_a, in_a, out_a), + transmute_scalar(bx, imm_b, in_b, out_b), + ), + _ => bug!("Cannot `codegen_transmute_operand` {operand:?} to {cast:?}"), } } @@ -364,36 +321,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some(imm) } - pub(crate) fn codegen_rvalue_unsized( - &mut self, - bx: &mut Bx, - indirect_dest: PlaceRef<'tcx, Bx::Value>, - rvalue: &mir::Rvalue<'tcx>, - ) { - debug!( - "codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})", - indirect_dest.val.llval, rvalue - ); - - match *rvalue { - mir::Rvalue::Use(ref operand) => { - let cg_operand = self.codegen_operand(bx, operand); - cg_operand.val.store_unsized(bx, indirect_dest); - } - - _ => bug!("unsized assignment other than `Rvalue::Use`"), - } - } - pub(crate) fn codegen_rvalue_operand( &mut self, bx: &mut Bx, rvalue: &mir::Rvalue<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - assert!( - self.rvalue_creates_operand(rvalue, DUMMY_SP), - "cannot codegen {rvalue:?} to operand", - ); + assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",); match *rvalue { mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { @@ -479,9 +412,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // path as the other integer-to-X casts. | mir::CastKind::PointerWithExposedProvenance => { let imm = operand.immediate(); - let operand_kind = self.value_kind(operand.layout); - let OperandValueKind::Immediate(from_scalar) = operand_kind else { - bug!("Found {operand_kind:?} for operand {operand:?}"); + let abi::BackendRepr::Scalar(from_scalar) = operand.layout.backend_repr else { + bug!("Found non-scalar for operand {operand:?}"); }; let from_backend_ty = bx.cx().immediate_backend_type(operand.layout); @@ -491,9 +423,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = OperandValue::Immediate(bx.cx().const_poison(to_backend_ty)); return OperandRef { val, layout: cast }; } - let cast_kind = self.value_kind(cast); - let OperandValueKind::Immediate(to_scalar) = cast_kind else { - bug!("Found {cast_kind:?} for operand {cast:?}"); + let abi::BackendRepr::Scalar(to_scalar) = cast.layout.backend_repr else { + bug!("Found non-scalar for cast {cast:?}"); }; self.cast_immediate(bx, imm, from_scalar, from_backend_ty, to_scalar, to_backend_ty) @@ -503,9 +434,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } mir::CastKind::Transmute => { - self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| { - bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}"); - }) + self.codegen_transmute_operand(bx, operand, cast) } }; OperandRef { val, layout: cast } @@ -694,22 +623,44 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand), mir::Rvalue::Repeat(..) => bug!("{rvalue:?} in codegen_rvalue_operand"), - mir::Rvalue::Aggregate(_, ref fields) => { + mir::Rvalue::Aggregate(ref kind, ref fields) => { + let (variant_index, active_field_index) = match **kind { + mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { + (variant_index, active_field_index) + } + _ => (FIRST_VARIANT, None), + }; + let ty = rvalue.ty(self.mir, self.cx.tcx()); let ty = self.monomorphize(ty); let layout = self.cx.layout_of(ty); // `rvalue_creates_operand` has arranged that we only get here if // we can build the aggregate immediate from the field immediates. - let Some(mut builder) = OperandRef::builder(layout) else { - bug!("Cannot use type in operand builder: {layout:?}") - }; + let mut builder = OperandRefBuilder::new(layout); for (field_idx, field) in fields.iter_enumerated() { let op = self.codegen_operand(bx, field); - builder.insert_field(bx, FIRST_VARIANT, field_idx, op); + let fi = active_field_index.unwrap_or(field_idx); + builder.insert_field(bx, variant_index, fi, op); } - builder.build() + let tag_result = codegen_tag_value(self.cx, variant_index, layout); + match tag_result { + Err(super::place::UninhabitedVariantError) => { + // Like codegen_set_discr we use a sound abort, but could + // potentially `unreachable` or just return the poison for + // more optimizability, if that turns out to be helpful. + bx.abort(); + let val = OperandValue::poison(bx, layout); + OperandRef { val, layout } + } + Ok(maybe_tag_value) => { + if let Some((tag_field, tag_imm)) = maybe_tag_value { + builder.insert_imm(tag_field, tag_imm); + } + builder.build(bx.cx()) + } + } } mir::Rvalue::ShallowInitBox(ref operand, content_ty) => { let operand = self.codegen_operand(bx, operand); @@ -987,37 +938,46 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Pair(val, of) } - pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { + /// Returns `true` if the `rvalue` can be computed into an [`OperandRef`], + /// rather than needing a full `PlaceRef` for the assignment destination. + /// + /// This is used by the [`super::analyze`] code to decide which MIR locals + /// can stay as SSA values (as opposed to generating `alloca` slots for them). + /// As such, some paths here return `true` even where the specific rvalue + /// will not actually take the operand path because the result type is such + /// that it always gets an `alloca`, but where it's not worth re-checking the + /// layout in this code when the right thing will happen anyway. + pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { match *rvalue { mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => { let operand_ty = operand.ty(self.mir, self.cx.tcx()); let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty)); let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty)); + match (operand_layout.backend_repr, cast_layout.backend_repr) { + // When the output will be in memory anyway, just use its place + // (instead of the operand path) unless it's the trivial ZST case. + (_, abi::BackendRepr::Memory { .. }) => cast_layout.is_zst(), - match (self.value_kind(operand_layout), self.value_kind(cast_layout)) { - // Can always load from a pointer as needed - (OperandValueKind::Ref, _) => true, - - // ZST-to-ZST is the easiest thing ever - (OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true, - - // But if only one of them is a ZST the sizes can't match - (OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false, - - // Need to generate an `alloc` to get a pointer from an immediate - (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false, + // Otherwise (for a non-memory output) if the input is memory + // then we can just read the value from the place. + (abi::BackendRepr::Memory { .. }, _) => true, // When we have scalar immediates, we can only convert things // where the sizes match, to avoid endianness questions. - (OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) => + (abi::BackendRepr::Scalar(a), abi::BackendRepr::Scalar(b)) => a.size(self.cx) == b.size(self.cx), - (OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) => + (abi::BackendRepr::ScalarPair(a0, a1), abi::BackendRepr::ScalarPair(b0, b1)) => a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx), - // Send mixings between scalars and pairs through the memory route - // FIXME: Maybe this could use insertvalue/extractvalue instead? - (OperandValueKind::Immediate(..), OperandValueKind::Pair(..)) | - (OperandValueKind::Pair(..), OperandValueKind::Immediate(..)) => false, + // Mixing Scalars and ScalarPairs can get quite complicated when + // padding and undef get involved, so leave that to the memory path. + (abi::BackendRepr::Scalar(_), abi::BackendRepr::ScalarPair(_, _)) | + (abi::BackendRepr::ScalarPair(_, _), abi::BackendRepr::Scalar(_)) => false, + + // SIMD vectors aren't worth the trouble of dealing with complex + // cases like from vectors of f32 to vectors of pointers or + // from fat pointers to vectors of u16. (See #143194 #110021 ...) + (abi::BackendRepr::SimdVector { .. }, _) | (_, abi::BackendRepr::SimdVector { .. }) => false, } } mir::Rvalue::Ref(..) | @@ -1032,98 +992,53 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::NullaryOp(..) | mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) | + mir::Rvalue::Aggregate(..) | // (*) mir::Rvalue::WrapUnsafeBinder(..) => // (*) true, // Arrays are always aggregates, so it's not worth checking anything here. // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.) mir::Rvalue::Repeat(..) => false, - mir::Rvalue::Aggregate(ref kind, _) => { - let allowed_kind = match **kind { - // This always produces a `ty::RawPtr`, so will be Immediate or Pair - mir::AggregateKind::RawPtr(..) => true, - mir::AggregateKind::Array(..) => false, - mir::AggregateKind::Tuple => true, - mir::AggregateKind::Adt(def_id, ..) => { - let adt_def = self.cx.tcx().adt_def(def_id); - adt_def.is_struct() && !adt_def.repr().simd() - } - mir::AggregateKind::Closure(..) => true, - // FIXME: Can we do this for simple coroutines too? - mir::AggregateKind::Coroutine(..) | mir::AggregateKind::CoroutineClosure(..) => false, - }; - allowed_kind && { - let ty = rvalue.ty(self.mir, self.cx.tcx()); - let ty = self.monomorphize(ty); - let layout = self.cx.spanned_layout_of(ty, span); - OperandRef::<Bx::Value>::builder(layout).is_some() - } - } } // (*) this is only true if the type is suitable } - - /// Gets which variant of [`OperandValue`] is expected for a particular type. - fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind { - if layout.is_zst() { - OperandValueKind::ZeroSized - } else if self.cx.is_backend_immediate(layout) { - assert!(!self.cx.is_backend_scalar_pair(layout)); - OperandValueKind::Immediate(match layout.backend_repr { - abi::BackendRepr::Scalar(s) => s, - abi::BackendRepr::SimdVector { element, .. } => element, - x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"), - }) - } else if self.cx.is_backend_scalar_pair(layout) { - let abi::BackendRepr::ScalarPair(s1, s2) = layout.backend_repr else { - span_bug!( - self.mir.span, - "Couldn't translate {:?} as backend scalar pair", - layout.backend_repr, - ); - }; - OperandValueKind::Pair(s1, s2) - } else { - OperandValueKind::Ref - } - } } -/// The variants of this match [`OperandValue`], giving details about the -/// backend values that will be held in that other type. -#[derive(Debug, Copy, Clone)] -enum OperandValueKind { - Ref, - Immediate(abi::Scalar), - Pair(abi::Scalar, abi::Scalar), - ZeroSized, -} - -/// Transmutes one of the immediates from an [`OperandValue::Immediate`] -/// or an [`OperandValue::Pair`] to an immediate of the target type. +/// Transmutes a single scalar value `imm` from `from_scalar` to `to_scalar`. +/// +/// This is expected to be in *immediate* form, as seen in [`OperandValue::Immediate`] +/// or [`OperandValue::Pair`] (so `i1` for bools, not `i8`, for example). /// -/// `to_backend_ty` must be the *non*-immediate backend type (so it will be -/// `i8`, not `i1`, for `bool`-like types.) -pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +/// ICEs if the passed-in `imm` is not a value of the expected type for +/// `from_scalar`, such as if it's a vector or a pair. +pub(super) fn transmute_scalar<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, mut imm: Bx::Value, from_scalar: abi::Scalar, - from_backend_ty: Bx::Type, to_scalar: abi::Scalar, - to_backend_ty: Bx::Type, ) -> Bx::Value { assert_eq!(from_scalar.size(bx.cx()), to_scalar.size(bx.cx())); + let imm_ty = bx.cx().val_ty(imm); + assert_ne!( + bx.cx().type_kind(imm_ty), + TypeKind::Vector, + "Vector type {imm_ty:?} not allowed in transmute_scalar {from_scalar:?} -> {to_scalar:?}" + ); // While optimizations will remove no-op transmutes, they might still be // there in debug or things that aren't no-op in MIR because they change // the Rust type but not the underlying layout/niche. - if from_scalar == to_scalar && from_backend_ty == to_backend_ty { + if from_scalar == to_scalar { return imm; } use abi::Primitive::*; imm = bx.from_immediate(imm); + let from_backend_ty = bx.cx().type_from_scalar(from_scalar); + debug_assert_eq!(bx.cx().val_ty(imm), from_backend_ty); + let to_backend_ty = bx.cx().type_from_scalar(to_scalar); + // If we have a scalar, we must already know its range. Either // // 1) It's a parameter with `range` parameter metadata, @@ -1154,6 +1069,8 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } }; + debug_assert_eq!(bx.cx().val_ty(imm), to_backend_ty); + // This `assume` remains important for cases like (a conceptual) // transmute::<u32, NonZeroU32>(x) == 0 // since it's never passed to something with parameter metadata (especially @@ -1180,7 +1097,7 @@ fn assume_scalar_range<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let range = scalar.valid_range(bx.cx()); bx.assume_integer_range(imm, backend_ty, range); } - abi::Primitive::Pointer(abi::AddressSpace::DATA) + abi::Primitive::Pointer(abi::AddressSpace::ZERO) if !scalar.valid_range(bx.cx()).contains(0) => { bx.assume_nonnull(imm); diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index cd55a838a75..f164e0f9123 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -15,7 +15,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match self.locals[index] { LocalRef::Place(cg_dest) => self.codegen_rvalue(bx, cg_dest, rvalue), LocalRef::UnsizedPlace(cg_indirect_dest) => { - self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue) + let ty = cg_indirect_dest.layout.ty; + span_bug!( + statement.source_info.span, + "cannot reallocate from `UnsizedPlace({ty})` \ + into `{rvalue:?}`; dynamic alloca is not supported", + ); } LocalRef::PendingOperand => { let operand = self.codegen_rvalue_operand(bx, rvalue); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 9d367748c2a..979456a6ba7 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -136,6 +136,16 @@ pub trait BuilderMethods<'a, 'tcx>: ) -> Self::Value; fn unreachable(&mut self); + /// Like [`Self::unreachable`], but for use in the middle of a basic block. + fn unreachable_nonterminator(&mut self) { + // This is the preferred LLVM incantation for this per + // https://llvm.org/docs/Frontend/PerformanceTips.html#other-things-to-consider + // Other backends may override if they have a better way. + let const_true = self.cx().const_bool(true); + let poison_ptr = self.const_poison(self.cx().type_ptr()); + self.store(const_true, poison_ptr, Align::ONE); + } + fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; @@ -224,7 +234,6 @@ pub trait BuilderMethods<'a, 'tcx>: fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value; fn alloca(&mut self, size: Size, align: Align) -> Self::Value; - fn dynamic_alloca(&mut self, size: Self::Value, align: Align) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; @@ -555,12 +564,33 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + /// "Finally codegen the call" + /// + /// ## Arguments + /// + /// The `fn_attrs`, `fn_abi`, and `instance` arguments are Options because they are advisory. + /// They relate to optional codegen enhancements like LLVM CFI, and do not affect ABI per se. + /// Any ABI-related transformations should be handled by different, earlier stages of codegen. + /// For instance, in the caller of `BuilderMethods::call`. + /// + /// This means that a codegen backend which disregards `fn_attrs`, `fn_abi`, and `instance` + /// should still do correct codegen, and code should not be miscompiled if they are omitted. + /// It is not a miscompilation in this sense if it fails to run under CFI, other sanitizers, or + /// in the context of other compiler-enhanced security features. + /// + /// The typical case that they are None is during the codegen of intrinsics and lang-items, + /// as those are "fake functions" with only a trivial ABI if any, et cetera. + /// + /// ## Return + /// + /// Must return the value the function will return so it can be written to the destination, + /// assuming the function does not explicitly pass the destination as a pointer in `args`. fn call( &mut self, llty: Self::Type, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, - llfn: Self::Value, + fn_val: Self::Value, args: &[Self::Value], funclet: Option<&Self::Funclet>, instance: Option<Instance<'tcx>>, diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 7d0c6be4c65..c5ecf43046c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -6,16 +6,22 @@ use crate::mir::operand::OperandRef; use crate::mir::place::PlaceRef; pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { + /// Higher-level interface to emitting calls to intrinsics + /// /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, /// and in `library/core/src/intrinsics.rs`; if you need access to any LLVM intrinsics, /// add them to `compiler/rustc_codegen_llvm/src/context.rs`. /// Returns `Err` if another instance should be called instead. This is used to invoke /// intrinsic default bodies in case an intrinsic is not implemented by the backend. + /// + /// NOTE: allowed to call [`BuilderMethods::call`] + /// + /// [`BuilderMethods::call`]: super::builder::BuilderMethods::call fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], - result: PlaceRef<'tcx, Self::Value>, + result_dest: PlaceRef<'tcx, Self::Value>, span: Span, ) -> Result<(), ty::Instance<'tcx>>; diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index dcd9e25b2c9..32c24965e1b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -154,9 +154,9 @@ pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes { // For backends that support CFI using type membership (i.e., testing whether a given pointer is // associated with a type identifier). pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes { - fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {} - fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {} - fn typeid_metadata(&self, _typeid: String) -> Option<Self::Metadata> { + fn add_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} + fn set_type_metadata(&self, _function: Self::Function, _typeid: &[u8]) {} + fn typeid_metadata(&self, _typeid: &[u8]) -> Option<Self::Metadata> { None } fn add_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} |
