From e1258e79d6cb709b26ded97d32de6c55f355e2aa Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 20:17:32 +0000 Subject: autodiff: Add basic TypeTree with NoTT flag Signed-off-by: Karan Janthe --- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 78107d95e5a..5ac3a87c158 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -563,6 +563,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) { config::AutoDiff::Enable => {} // We handle this below config::AutoDiff::NoPostopt => {} + // Disables TypeTree generation + config::AutoDiff::NoTT => {} } } // This helps with handling enums for now. -- cgit 1.4.1-3-g733a5 From 375e14ef491ac7bfa701e269b2815625abf2fca6 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 21:56:56 +0000 Subject: Add TypeTree metadata attachment for autodiff - Add F128 support to TypeTree Kind enum - Implement TypeTree FFI bindings and conversion functions - Add typetree.rs module for metadata attachment to LLVM functions - Integrate TypeTree generation with autodiff intrinsic pipeline - Support scalar types: f32, f64, integers, f16, f128 - Attach enzyme_type attributes as LLVM string metadata for Enzyme Signed-off-by: Karan Janthe --- compiler/rustc_ast/src/expand/typetree.rs | 1 + .../rustc_codegen_llvm/src/builder/autodiff.rs | 6 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 + compiler/rustc_codegen_llvm/src/lib.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 182 ++++++++++++++++++++- compiler/rustc_codegen_llvm/src/typetree.rs | 144 ++++++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 19 +-- 7 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/typetree.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_ast/src/expand/typetree.rs b/compiler/rustc_ast/src/expand/typetree.rs index 9a2dd2e85e0..e7b4f3aff41 100644 --- a/compiler/rustc_ast/src/expand/typetree.rs +++ b/compiler/rustc_ast/src/expand/typetree.rs @@ -31,6 +31,7 @@ pub enum Kind { Half, Float, Double, + F128, Unknown, } diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index b66e3dfdeec..c3485f56391 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -1,6 +1,7 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; +use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; @@ -294,6 +295,7 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args: &[&'ll Value], attrs: AutoDiffAttrs, dest: PlaceRef<'tcx, &'ll Value>, + fnc_tree: FncTree, ) { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { @@ -370,6 +372,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args, ); + if !fnc_tree.args.is_empty() || !fnc_tree.ret.0.is_empty() { + crate::typetree::add_tt(cx.llmod, cx.llcx, fn_to_diff, fnc_tree); + } + let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); builder.store_to_place(call, dest.val); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e7f4a357048..3254b318651 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1213,6 +1213,9 @@ fn codegen_autodiff<'ll, 'tcx>( &mut diff_attrs.input_activity, ); + let fnc_tree = + rustc_middle::ty::fnc_typetrees(tcx, fn_source.ty(tcx, TypingEnv::fully_monomorphized())); + // Build body generate_enzyme_call( bx, @@ -1223,6 +1226,7 @@ fn codegen_autodiff<'ll, 'tcx>( &val_arr, diff_attrs.clone(), result, + fnc_tree, ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6fb23d09843..a5ac789285d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -67,6 +67,7 @@ mod llvm_util; mod mono_item; mod type_; mod type_of; +mod typetree; mod va_arg; mod value; diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 695435eb6da..12b6ffa3c0b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -3,9 +3,35 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; -use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; +use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value}; use crate::llvm::{Bool, Builder}; +// TypeTree types +pub(crate) type CTypeTreeRef = *mut EnzymeTypeTree; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(crate) struct EnzymeTypeTree { + _unused: [u8; 0], +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub(crate) enum CConcreteType { + DT_Anything = 0, + DT_Integer = 1, + DT_Pointer = 2, + DT_Half = 3, + DT_Float = 4, + DT_Double = 5, + DT_Unknown = 6, +} + +pub(crate) struct TypeTree { + pub(crate) inner: CTypeTreeRef, +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -68,10 +94,33 @@ pub(crate) mod Enzyme_AD { use libc::c_void; + use super::{CConcreteType, CTypeTreeRef, Context}; + unsafe extern "C" { pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); pub(crate) fn EnzymeSetCLString(arg1: *mut ::std::os::raw::c_void, arg2: *const c_char); } + + // TypeTree functions + unsafe extern "C" { + pub(crate) fn EnzymeNewTypeTree() -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef; + pub(crate) fn EnzymeFreeTypeTree(CTT: CTypeTreeRef); + pub(crate) fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool; + pub(crate) fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64); + pub(crate) fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef); + pub(crate) fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ); + pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; + pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); + } + unsafe extern "C" { static mut EnzymePrintPerf: c_void; static mut EnzymePrintActivity: c_void; @@ -141,6 +190,57 @@ pub(crate) use self::Fallback_AD::*; pub(crate) mod Fallback_AD { #![allow(unused_variables)] + use libc::c_char; + + use super::{CConcreteType, CTypeTreeRef, Context}; + + // TypeTree function fallbacks + pub(crate) unsafe fn EnzymeNewTypeTree() -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeFreeTypeTree(CTT: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToStringFree(arg1: *const c_char) { + unimplemented!() + } + pub(crate) fn set_inline(val: bool) { unimplemented!() } @@ -169,3 +269,83 @@ pub(crate) mod Fallback_AD { unimplemented!() } } + +impl TypeTree { + pub(crate) fn new() -> TypeTree { + let inner = unsafe { EnzymeNewTypeTree() }; + TypeTree { inner } + } + + pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree { + let inner = unsafe { EnzymeNewTypeTreeCT(t, ctx) }; + TypeTree { inner } + } + + pub(crate) fn merge(self, other: Self) -> Self { + unsafe { + EnzymeMergeTypeTree(self.inner, other.inner); + } + drop(other); + self + } + + #[must_use] + pub(crate) fn shift( + self, + layout: &str, + offset: isize, + max_size: isize, + add_offset: usize, + ) -> Self { + let layout = std::ffi::CString::new(layout).unwrap(); + + unsafe { + EnzymeTypeTreeShiftIndiciesEq( + self.inner, + layout.as_ptr(), + offset as i64, + max_size as i64, + add_offset as u64, + ); + } + + self + } +} + +impl Clone for TypeTree { + fn clone(&self) -> Self { + let inner = unsafe { EnzymeNewTypeTreeTR(self.inner) }; + TypeTree { inner } + } +} + +impl std::fmt::Display for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ptr = unsafe { EnzymeTypeTreeToString(self.inner) }; + let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; + match cstr.to_str() { + Ok(x) => write!(f, "{}", x)?, + Err(err) => write!(f, "could not parse: {}", err)?, + } + + // delete C string pointer + unsafe { + EnzymeTypeTreeToStringFree(ptr); + } + + Ok(()) + } +} + +impl std::fmt::Debug for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ::fmt(self, f) + } +} + +impl Drop for TypeTree { + fn drop(&mut self) { + unsafe { EnzymeFreeTypeTree(self.inner) } + } +} diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs new file mode 100644 index 00000000000..434316464e6 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -0,0 +1,144 @@ +use std::ffi::{CString, c_char, c_uint}; + +use rustc_ast::expand::typetree::{FncTree, TypeTree as RustTypeTree}; + +use crate::attributes; +use crate::llvm::{self, Value}; + +/// Converts a Rust TypeTree to Enzyme's internal TypeTree format +/// +/// This function takes a Rust-side TypeTree (from rustc_ast::expand::typetree) +/// and converts it to Enzyme's internal C++ TypeTree representation that +/// Enzyme can understand during differentiation analysis. +#[cfg(llvm_enzyme)] +fn to_enzyme_typetree( + rust_typetree: RustTypeTree, + data_layout: &str, + llcx: &llvm::Context, +) -> llvm::TypeTree { + // Start with an empty TypeTree + let mut enzyme_tt = llvm::TypeTree::new(); + + // Convert each Type in the Rust TypeTree to Enzyme format + for rust_type in rust_typetree.0 { + let concrete_type = match rust_type.kind { + rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything, + rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer, + rustc_ast::expand::typetree::Kind::Pointer => llvm::CConcreteType::DT_Pointer, + rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half, + rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float, + rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double, + rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_Unknown, + rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, + }; + + // Create a TypeTree for this specific type + let type_tt = llvm::TypeTree::from_type(concrete_type, llcx); + + // Apply offset if specified + let type_tt = if rust_type.offset == -1 { + type_tt // -1 means everywhere/no specific offset + } else { + // Apply specific offset positioning + type_tt.shift(data_layout, rust_type.offset, rust_type.size as isize, 0) + }; + + // Merge this type into the main TypeTree + enzyme_tt = enzyme_tt.merge(type_tt); + } + + enzyme_tt +} + +#[cfg(not(llvm_enzyme))] +fn to_enzyme_typetree( + _rust_typetree: RustTypeTree, + _data_layout: &str, + _llcx: &llvm::Context, +) -> ! { + unimplemented!("TypeTree conversion not available without llvm_enzyme support") +} + +// Attaches TypeTree information to LLVM function as enzyme_type attributes. +#[cfg(llvm_enzyme)] +pub(crate) fn add_tt<'ll>( + llmod: &'ll llvm::Module, + llcx: &'ll llvm::Context, + fn_def: &'ll Value, + tt: FncTree, +) { + let inputs = tt.args; + let ret_tt: RustTypeTree = tt.ret; + + // Get LLVM data layout string for TypeTree conversion + let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; + let llvm_data_layout = + std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) + .expect("got a non-UTF8 data-layout from LLVM"); + + // Attribute name that Enzyme recognizes for TypeTree information + let attr_name = "enzyme_type"; + let c_attr_name = CString::new(attr_name).unwrap(); + + // Attach TypeTree attributes to each input parameter + // Enzyme uses these to understand parameter memory layouts during differentiation + for (i, input) in inputs.iter().enumerate() { + unsafe { + // Convert Rust TypeTree to Enzyme's internal format + let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx); + + // Serialize TypeTree to string format that Enzyme can parse + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + // Create LLVM string attribute with TypeTree information + let attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + // Attach attribute to the specific function parameter + // Note: ArgumentPlace uses 0-based indexing, but LLVM uses 1-based for arguments + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); + + // Free the C string to prevent memory leaks + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } + } + + // Attach TypeTree attribute to the return type + // Enzyme needs this to understand how to handle return value derivatives + unsafe { + let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx); + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + let ret_attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + // Attach to function return type + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); + + // Free the C string + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } +} + +// Fallback implementation when Enzyme is not available +#[cfg(not(llvm_enzyme))] +pub(crate) fn add_tt<'ll>( + _llmod: &'ll llvm::Module, + _llcx: &'ll llvm::Context, + _fn_def: &'ll Value, + _tt: FncTree, +) { + unimplemented!() +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index df42c400317..e7130757f1d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2251,36 +2251,29 @@ pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { /// Generate TypeTree for a specific type. /// This function analyzes a Rust type and creates appropriate TypeTree metadata. -fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { - // Handle basic scalar types +pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if ty.is_scalar() { let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) } else if ty.is_floating_point() { match ty { + x if x == tcx.types.f16 => (Kind::Half, 2), x if x == tcx.types.f32 => (Kind::Float, 4), x if x == tcx.types.f64 => (Kind::Double, 8), - _ => return TypeTree::new(), // Unknown float type + x if x == tcx.types.f128 => (Kind::F128, 16), + _ => return TypeTree::new(), } } else { - // TODO(KMJ-007): Handle other scalar types if needed return TypeTree::new(); }; - - return TypeTree(vec![Type { - offset: -1, - size, - kind, - child: TypeTree::new() - }]); + + return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); } - // Handle references and pointers if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { let inner_ty = if let Some(inner) = ty.builtin_deref(true) { inner } else { - // TODO(KMJ-007): Handle complex pointer types return TypeTree::new(); }; -- cgit 1.4.1-3-g733a5 From 664e83b3e76b51fb5192e74a64eef3bc5bbd4e32 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Sat, 23 Aug 2025 23:10:48 +0000 Subject: added typetree support for memcpy --- compiler/rustc_codegen_gcc/src/builder.rs | 1 + compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 1 + compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/builder.rs | 15 +++++++-- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 + compiler/rustc_codegen_llvm/src/typetree.rs | 20 ++++------- compiler/rustc_codegen_llvm/src/va_arg.rs | 1 + compiler/rustc_codegen_ssa/src/mir/block.rs | 1 + compiler/rustc_codegen_ssa/src/mir/intrinsic.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/statement.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/builder.rs | 3 +- compiler/rustc_interface/src/tests.rs | 1 - compiler/rustc_middle/src/ty/mod.rs | 4 +-- tests/codegen-llvm/autodiff/typetree.rs | 2 +- .../type-trees/memcpy-typetree/memcpy-ir.check | 8 +++++ .../type-trees/memcpy-typetree/memcpy.check | 13 ++++++++ .../autodiff/type-trees/memcpy-typetree/memcpy.rs | 36 ++++++++++++++++++++ .../autodiff/type-trees/memcpy-typetree/rmake.rs | 39 ++++++++++++++++++++++ .../autodiff/type-trees/nott-flag/rmake.rs | 14 +++----- .../run-make/autodiff/type-trees/nott-flag/test.rs | 2 +- tests/ui/autodiff/flag_nott.rs | 2 +- 21 files changed, 135 insertions(+), 34 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs create mode 100644 tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f7a7a3f8c7e..5657620879c 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1383,6 +1383,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _src_align: Align, size: RValue<'gcc>, flags: MemFlags, + _tt: Option, // Autodiff TypeTrees are LLVM-only, ignored in GCC backend ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_size_t(), false); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 84fa56cf903..3b897a19835 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -771,6 +771,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(self.layout.size.bytes()), MemFlags::empty(), + None, ); bx.lifetime_end(scratch, scratch_size); diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 11be7041167..61fadad6066 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -246,6 +246,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); bx.lifetime_end(llscratch, scratch_size); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0f17cc9063a..83a9cf620f1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2,6 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; +use rustc_ast::expand::typetree::FncTree; pub(crate) mod autodiff; pub(crate) mod gpu_offload; @@ -1107,11 +1108,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align: Align, size: &'ll Value, flags: MemFlags, + tt: Option, ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_isize(), false); let is_volatile = flags.contains(MemFlags::VOLATILE); - unsafe { + let memcpy = unsafe { llvm::LLVMRustBuildMemCpy( self.llbuilder, dst, @@ -1120,7 +1122,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align.bytes() as c_uint, size, is_volatile, - ); + ) + }; + + // TypeTree metadata for memcpy is especially important: when Enzyme encounters + // a memcpy during autodiff, it needs to know the structure of the data being + // copied to properly track derivatives. For example, copying an array of floats + // vs. copying a struct with mixed types requires different derivative handling. + // The TypeTree tells Enzyme exactly what memory layout to expect. + if let Some(tt) = tt { + crate::typetree::add_tt(self.cx().llmod, self.cx().llcx, memcpy, tt); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 12b6ffa3c0b..9dd84ad9a4d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -25,6 +25,7 @@ pub(crate) enum CConcreteType { DT_Half = 3, DT_Float = 4, DT_Double = 5, + // FIXME(KMJ-007): handle f128 using long double here(https://github.com/EnzymeAD/Enzyme/issues/1600) DT_Unknown = 6, } diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 434316464e6..8e6272a186b 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -1,8 +1,11 @@ -use std::ffi::{CString, c_char, c_uint}; - -use rustc_ast::expand::typetree::{FncTree, TypeTree as RustTypeTree}; +use rustc_ast::expand::typetree::FncTree; +#[cfg(llvm_enzyme)] +use { + crate::attributes, + rustc_ast::expand::typetree::TypeTree as RustTypeTree, + std::ffi::{CString, c_char, c_uint}, +}; -use crate::attributes; use crate::llvm::{self, Value}; /// Converts a Rust TypeTree to Enzyme's internal TypeTree format @@ -50,15 +53,6 @@ fn to_enzyme_typetree( enzyme_tt } -#[cfg(not(llvm_enzyme))] -fn to_enzyme_typetree( - _rust_typetree: RustTypeTree, - _data_layout: &str, - _llcx: &llvm::Context, -) -> ! { - unimplemented!("TypeTree conversion not available without llvm_enzyme support") -} - // Attaches TypeTree information to LLVM function as enzyme_type attributes. #[cfg(llvm_enzyme)] pub(crate) fn add_tt<'ll>( diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index ab08125217f..d48c7cf874a 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -738,6 +738,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( src_align, bx.const_u32(layout.layout.size().bytes() as u32), MemFlags::empty(), + None, ); tmp } else { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1b218a0d339..b2dc4fe32b0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1626,6 +1626,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); // ...and then load it with the ABI type. llval = load_cast(bx, cast, llscratch, scratch_align); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 3c667b8e882..befa00c6861 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -30,7 +30,7 @@ fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if allow_overlap { bx.memmove(dst, align, src, align, size, flags); } else { - bx.memcpy(dst, align, src, align, size, flags); + bx.memcpy(dst, align, src, align, size, flags, None); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index f164e0f9123..0a50d7f18db 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let align = pointee_layout.align; let dst = dst_val.immediate(); let src = src_val.immediate(); - bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty()); + bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 4a5694e97fa..60296e36e0c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -451,6 +451,7 @@ pub trait BuilderMethods<'a, 'tcx>: src_align: Align, size: Self::Value, flags: MemFlags, + tt: Option, ); fn memmove( &mut self, @@ -507,7 +508,7 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst.with_type(layout), flags); } else if !layout.is_zst() { let bytes = self.const_usize(layout.size.bytes()); - self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags); + self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None); } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 837acdadd57..0dc5b5af3ac 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -765,7 +765,6 @@ fn test_unstable_options_tracking_hash() { tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); - tracked!(autodiff, vec![AutoDiff::Enable]); tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e7130757f1d..741b5d7fd4e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2280,12 +2280,12 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let child = typetree_from_ty(tcx, inner_ty); return TypeTree(vec![Type { offset: -1, - size: 8, // TODO(KMJ-007): Get actual pointer size from target + size: tcx.data_layout.pointer_size().bytes_usize(), kind: Kind::Pointer, child, }]); } - // TODO(KMJ-007): Handle arrays, slices, structs, and other complex types + // FIXME(KMJ-007): Handle arrays, slices, structs, and other complex types TypeTree::new() } diff --git a/tests/codegen-llvm/autodiff/typetree.rs b/tests/codegen-llvm/autodiff/typetree.rs index 3ad38d581b9..1cb0c2fb68b 100644 --- a/tests/codegen-llvm/autodiff/typetree.rs +++ b/tests/codegen-llvm/autodiff/typetree.rs @@ -30,4 +30,4 @@ fn main() { let output_ = d_simple(&x, &mut df_dx, 1.0); assert_eq!(output, output_); assert_eq!(2.0, df_dx); -} \ No newline at end of file +} diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check new file mode 100644 index 00000000000..3a59f06dda1 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check @@ -0,0 +1,8 @@ +; Check that enzyme_type attributes are present in the LLVM IR function definition +; This verifies our TypeTree system correctly attaches metadata for Enzyme + +CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[]:Pointer}" + +; Check that llvm.memcpy exists (either call or declare) +CHECK: {{(call|declare).*}}@llvm.memcpy + diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check new file mode 100644 index 00000000000..ae70830297a --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.check @@ -0,0 +1,13 @@ +CHECK: force_memcpy + +CHECK: @llvm.memcpy.p0.p0.i64 + +CHECK: test_memcpy - {[-1]:Float@double} |{[-1]:Pointer}:{} + +CHECK-DAG: ptr %{{[0-9]+}}: {[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double, [-1,24]:Float@double} + +CHECK-DAG: load double{{.*}}: {[-1]:Float@double} + +CHECK-DAG: fmul double{{.*}}: {[-1]:Float@double} + +CHECK-DAG: fadd double{{.*}}: {[-1]:Float@double} \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs new file mode 100644 index 00000000000..3c1029190c8 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy.rs @@ -0,0 +1,36 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; +use std::ptr; + +#[inline(never)] +fn force_memcpy(src: *const f64, dst: *mut f64, count: usize) { + unsafe { + ptr::copy_nonoverlapping(src, dst, count); + } +} + +#[autodiff_reverse(d_test_memcpy, Duplicated, Active)] +#[no_mangle] +fn test_memcpy(input: &[f64; 128]) -> f64 { + let mut local_data = [0.0f64; 128]; + + // Use a separate function to prevent inlining and optimization + force_memcpy(input.as_ptr(), local_data.as_mut_ptr(), 128); + + // Sum only first few elements to keep the computation simple + local_data[0] * local_data[0] + + local_data[1] * local_data[1] + + local_data[2] * local_data[2] + + local_data[3] * local_data[3] +} + +fn main() { + let input = [1.0; 128]; + let mut d_input = [0.0; 128]; + let result = test_memcpy(&input); + let result_d = d_test_memcpy(&input, &mut d_input, 1.0); + + assert_eq!(result, result_d); + println!("Memcpy test passed: result = {}", result); +} diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs new file mode 100644 index 00000000000..b4c650330fe --- /dev/null +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/rmake.rs @@ -0,0 +1,39 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + // First, compile to LLVM IR to check for enzyme_type attributes + let _ir_output = rustc() + .input("memcpy.rs") + .arg("-Zautodiff=Enable") + .arg("-Zautodiff=NoPostopt") + .opt_level("0") + .arg("--emit=llvm-ir") + .arg("-o") + .arg("main.ll") + .run(); + + // Then compile with TypeTree analysis output for the existing checks + let output = rustc() + .input("memcpy.rs") + .arg("-Zautodiff=Enable,PrintTAFn=test_memcpy") + .arg("-Zautodiff=NoPostopt") + .opt_level("3") + .arg("-Clto=fat") + .arg("-g") + .run(); + + let stdout = output.stdout_utf8(); + let stderr = output.stderr_utf8(); + let ir_content = rfs::read_to_string("main.ll"); + + rfs::write("memcpy.stdout", &stdout); + rfs::write("memcpy.stderr", &stderr); + rfs::write("main.ir", &ir_content); + + llvm_filecheck().patterns("memcpy.check").stdin_buf(stdout).run(); + + llvm_filecheck().patterns("memcpy-ir.check").stdin_buf(ir_content).run(); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index bab863ca9ff..de540b990ca 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -23,14 +23,8 @@ fn main() { .run(); // Verify NoTT version does NOT have enzyme_type attributes - llvm_filecheck() - .patterns("nott.check") - .stdin_buf(rfs::read("nott.ll")) - .run(); - + llvm_filecheck().patterns("nott.check").stdin_buf(rfs::read("nott.ll")).run(); + // Verify TypeTree version DOES have enzyme_type attributes - llvm_filecheck() - .patterns("with_tt.check") - .stdin_buf(rfs::read("with_tt.ll")) - .run(); -} \ No newline at end of file + llvm_filecheck().patterns("with_tt.check").stdin_buf(rfs::read("with_tt.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/test.rs b/tests/run-make/autodiff/type-trees/nott-flag/test.rs index 5c634eea035..de3549c37c6 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/test.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/test.rs @@ -12,4 +12,4 @@ fn main() { let x = 2.0; let mut dx = 0.0; let _result = d_square(&x, &mut dx, 1.0); -} \ No newline at end of file +} diff --git a/tests/ui/autodiff/flag_nott.rs b/tests/ui/autodiff/flag_nott.rs index 7a97d892cd8..faa9949fe81 100644 --- a/tests/ui/autodiff/flag_nott.rs +++ b/tests/ui/autodiff/flag_nott.rs @@ -16,4 +16,4 @@ fn main() { let x = 2.0; let mut dx = 0.0; let result = d_square(&x, &mut dx, 1.0); -} \ No newline at end of file +} -- cgit 1.4.1-3-g733a5 From 54f9376660707d4ca9fce51fd423658f75128ac4 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Wed, 27 Aug 2025 18:23:54 +0000 Subject: autodiff: f128 support added for typetree --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 2 +- compiler/rustc_codegen_llvm/src/typetree.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 9dd84ad9a4d..1596dc48379 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -25,8 +25,8 @@ pub(crate) enum CConcreteType { DT_Half = 3, DT_Float = 4, DT_Double = 5, - // FIXME(KMJ-007): handle f128 using long double here(https://github.com/EnzymeAD/Enzyme/issues/1600) DT_Unknown = 6, + DT_FP128 = 9, } pub(crate) struct TypeTree { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 8e6272a186b..8c0d255bba8 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -31,7 +31,7 @@ fn to_enzyme_typetree( rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half, rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float, rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double, - rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_Unknown, + rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_FP128, rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, }; -- cgit 1.4.1-3-g733a5 From 4f3f0f48e7b1e61818b2bcbe4451f89bb4f47049 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Thu, 4 Sep 2025 11:17:34 +0000 Subject: autodiff: fixed test to be more precise for type tree checking --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 23 +++++++ compiler/rustc_codegen_llvm/src/typetree.rs | 70 +++++++++------------- compiler/rustc_middle/src/ty/mod.rs | 36 +++-------- .../autodiff/type-trees/array-typetree/array.check | 2 +- .../type-trees/memcpy-typetree/memcpy-ir.check | 2 +- .../type-trees/mixed-struct-typetree/mixed.check | 2 + .../type-trees/mixed-struct-typetree/rmake.rs | 16 +++++ .../type-trees/mixed-struct-typetree/test.rs | 23 +++++++ .../autodiff/type-trees/nott-flag/with_tt.check | 2 +- .../scalar-types/f128-typetree/f128.check | 4 +- .../type-trees/scalar-types/f16-typetree/f16.check | 4 +- .../type-trees/scalar-types/f32-typetree/f32.check | 2 +- .../type-trees/scalar-types/f64-typetree/f64.check | 2 +- .../type-trees/scalar-types/i32-typetree/i32.check | 4 +- .../type-trees/scalar-types/i32-typetree/test.rs | 7 ++- .../autodiff/type-trees/slice-typetree/slice.check | 2 +- .../type-trees/struct-typetree/struct.check | 2 +- .../autodiff/type-trees/tuple-typetree/tuple.check | 2 +- .../type-trees/type-analysis/vec/vec.check | 2 +- 19 files changed, 120 insertions(+), 87 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 1596dc48379..e63043b2122 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -118,6 +118,13 @@ pub(crate) mod Enzyme_AD { max_size: i64, add_offset: u64, ); + pub(crate) fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); } @@ -234,6 +241,16 @@ pub(crate) mod Fallback_AD { unimplemented!() } + pub(crate) unsafe fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ) { + unimplemented!() + } + pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { unimplemented!() } @@ -312,6 +329,12 @@ impl TypeTree { self } + + pub(crate) fn insert(&mut self, indices: &[i64], ct: CConcreteType, ctx: &Context) { + unsafe { + EnzymeTypeTreeInsertEq(self.inner, indices.as_ptr(), indices.len(), ct, ctx); + } + } } impl Clone for TypeTree { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 8c0d255bba8..ae6a2da62b5 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -8,22 +8,24 @@ use { use crate::llvm::{self, Value}; -/// Converts a Rust TypeTree to Enzyme's internal TypeTree format -/// -/// This function takes a Rust-side TypeTree (from rustc_ast::expand::typetree) -/// and converts it to Enzyme's internal C++ TypeTree representation that -/// Enzyme can understand during differentiation analysis. #[cfg(llvm_enzyme)] fn to_enzyme_typetree( rust_typetree: RustTypeTree, - data_layout: &str, + _data_layout: &str, llcx: &llvm::Context, ) -> llvm::TypeTree { - // Start with an empty TypeTree let mut enzyme_tt = llvm::TypeTree::new(); - - // Convert each Type in the Rust TypeTree to Enzyme format - for rust_type in rust_typetree.0 { + process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); + enzyme_tt +} +#[cfg(llvm_enzyme)] +fn process_typetree_recursive( + enzyme_tt: &mut llvm::TypeTree, + rust_typetree: &RustTypeTree, + parent_indices: &[i64], + llcx: &llvm::Context, +) { + for rust_type in &rust_typetree.0 { let concrete_type = match rust_type.kind { rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything, rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer, @@ -35,25 +37,27 @@ fn to_enzyme_typetree( rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, }; - // Create a TypeTree for this specific type - let type_tt = llvm::TypeTree::from_type(concrete_type, llcx); - - // Apply offset if specified - let type_tt = if rust_type.offset == -1 { - type_tt // -1 means everywhere/no specific offset + let mut indices = parent_indices.to_vec(); + if !parent_indices.is_empty() { + if rust_type.offset == -1 { + indices.push(-1); + } else { + indices.push(rust_type.offset as i64); + } + } else if rust_type.offset == -1 { + indices.push(-1); } else { - // Apply specific offset positioning - type_tt.shift(data_layout, rust_type.offset, rust_type.size as isize, 0) - }; + indices.push(rust_type.offset as i64); + } - // Merge this type into the main TypeTree - enzyme_tt = enzyme_tt.merge(type_tt); - } + enzyme_tt.insert(&indices, concrete_type, llcx); - enzyme_tt + if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer && !rust_type.child.0.is_empty() { + process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx); + } + } } -// Attaches TypeTree information to LLVM function as enzyme_type attributes. #[cfg(llvm_enzyme)] pub(crate) fn add_tt<'ll>( llmod: &'ll llvm::Module, @@ -64,28 +68,20 @@ pub(crate) fn add_tt<'ll>( let inputs = tt.args; let ret_tt: RustTypeTree = tt.ret; - // Get LLVM data layout string for TypeTree conversion let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; let llvm_data_layout = std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) .expect("got a non-UTF8 data-layout from LLVM"); - // Attribute name that Enzyme recognizes for TypeTree information let attr_name = "enzyme_type"; let c_attr_name = CString::new(attr_name).unwrap(); - // Attach TypeTree attributes to each input parameter - // Enzyme uses these to understand parameter memory layouts during differentiation for (i, input) in inputs.iter().enumerate() { unsafe { - // Convert Rust TypeTree to Enzyme's internal format let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx); - - // Serialize TypeTree to string format that Enzyme can parse let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); let c_str = std::ffi::CStr::from_ptr(c_str); - // Create LLVM string attribute with TypeTree information let attr = llvm::LLVMCreateStringAttribute( llcx, c_attr_name.as_ptr(), @@ -94,17 +90,11 @@ pub(crate) fn add_tt<'ll>( c_str.to_bytes().len() as c_uint, ); - // Attach attribute to the specific function parameter - // Note: ArgumentPlace uses 0-based indexing, but LLVM uses 1-based for arguments attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); - - // Free the C string to prevent memory leaks llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); } } - // Attach TypeTree attribute to the return type - // Enzyme needs this to understand how to handle return value derivatives unsafe { let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx); let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); @@ -118,15 +108,11 @@ pub(crate) fn add_tt<'ll>( c_str.to_bytes().len() as c_uint, ); - // Attach to function return type attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); - - // Free the C string llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); } } -// Fallback implementation when Enzyme is not available #[cfg(not(llvm_enzyme))] pub(crate) fn add_tt<'ll>( _llmod: &'ll llvm::Module, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 703d417f96d..581f20e8492 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2261,10 +2261,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { x if x == tcx.types.f32 => (Kind::Float, 4), x if x == tcx.types.f64 => (Kind::Double, 8), x if x == tcx.types.f128 => (Kind::F128, 16), - _ => return TypeTree::new(), + _ => (Kind::Integer, 0), } } else { - return TypeTree::new(); + (Kind::Integer, 0) }; return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); @@ -2295,32 +2295,14 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let element_tree = typetree_from_ty(tcx, *element_ty); - let element_layout = tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*element_ty)) - .ok() - .map(|layout| layout.size.bytes_usize()) - .unwrap_or(0); - - if element_layout == 0 { - return TypeTree::new(); - } - let mut types = Vec::new(); - for i in 0..len { - let base_offset = (i as usize * element_layout) as isize; - - for elem_type in &element_tree.0 { - types.push(Type { - offset: if elem_type.offset == -1 { - base_offset - } else { - base_offset + elem_type.offset - }, - size: elem_type.size, - kind: elem_type.kind, - child: elem_type.child.clone(), - }); - } + for elem_type in &element_tree.0 { + types.push(Type { + offset: -1, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); } return TypeTree(types); diff --git a/tests/run-make/autodiff/type-trees/array-typetree/array.check b/tests/run-make/autodiff/type-trees/array-typetree/array.check index 7513458b8ab..0d38bdec17e 100644 --- a/tests/run-make/autodiff/type-trees/array-typetree/array.check +++ b/tests/run-make/autodiff/type-trees/array-typetree/array.check @@ -1,4 +1,4 @@ ; Check that array TypeTree metadata is correctly generated ; Should show Float@double at each array element offset (0, 8, 16, 24, 32 bytes) -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_array{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_array{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check index 3a59f06dda1..0e6351ac4d3 100644 --- a/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check +++ b/tests/run-make/autodiff/type-trees/memcpy-typetree/memcpy-ir.check @@ -1,7 +1,7 @@ ; Check that enzyme_type attributes are present in the LLVM IR function definition ; This verifies our TypeTree system correctly attaches metadata for Enzyme -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[]:Pointer}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_memcpy({{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" ; Check that llvm.memcpy exists (either call or declare) CHECK: {{(call|declare).*}}@llvm.memcpy diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check new file mode 100644 index 00000000000..584f5840843 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/mixed.check @@ -0,0 +1,2 @@ +; Check that mixed struct with large array generates correct detailed type tree +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_mixed_struct{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer, [-1,8]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs new file mode 100644 index 00000000000..1c19963bc36 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs @@ -0,0 +1,16 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc() + .input("test.rs") + .arg("-Zautodiff=Enable") + .arg("-Zautodiff=NoPostopt") + .opt_level("0") + .emit("llvm-ir") + .run(); + + llvm_filecheck().patterns("mixed.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs new file mode 100644 index 00000000000..7a734980e61 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/test.rs @@ -0,0 +1,23 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +#[repr(C)] +struct Container { + header: i64, + data: [f32; 1000], +} + +#[autodiff_reverse(d_test, Duplicated, Active)] +#[no_mangle] +#[inline(never)] +fn test_mixed_struct(container: &Container) -> f32 { + container.data[0] + container.data[999] +} + +fn main() { + let container = Container { header: 42, data: [1.0; 1000] }; + let mut d_container = Container { header: 0, data: [0.0; 1000] }; + let result = d_test(&container, &mut d_container, 1.0); + std::hint::black_box(result); +} diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check index 53eafc10a65..3c02003c882 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -1,4 +1,4 @@ // Check that enzyme_type attributes are present when TypeTree is enabled // This verifies our TypeTree metadata attachment is working -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check index 31cef113420..733e46aa45a 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check @@ -1,4 +1,4 @@ ; Check that f128 TypeTree metadata is correctly generated -; f128 maps to Unknown in our current implementation since CConcreteType doesn't have DT_F128 +; Should show Float@fp128 for f128 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f128{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@fp128}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check index 97a3964a569..9caca26b5cf 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check @@ -1,4 +1,4 @@ ; Check that f16 TypeTree metadata is correctly generated -; Should show Half for f16 values and Pointer for references +; Should show Float@half for f16 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"={{.*}}@test_f16{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@half}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check index b32d5086d07..ec12ba6b234 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check @@ -1,4 +1,4 @@ ; Check that f32 TypeTree metadata is correctly generated ; Should show Float@float for f32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check index 0e9d9c03a0f..f1af270824d 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check @@ -1,4 +1,4 @@ ; Check that f64 TypeTree metadata is correctly generated ; Should show Float@double for f64 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check index aa8e5223e8e..fd9a94be810 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check @@ -1,4 +1,4 @@ ; Check that i32 TypeTree metadata is correctly generated -; Should show Integer for i32 values (integers are typically Const in autodiff) +; Should show Integer for i32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[]:Integer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Integer}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs index 530c137f62b..249803c5d9f 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/test.rs @@ -2,13 +2,14 @@ use std::autodiff::autodiff_reverse; -#[autodiff_reverse(d_test, Const, Active)] +#[autodiff_reverse(d_test, Duplicated, Active)] #[no_mangle] -fn test_i32(x: i32) -> i32 { +fn test_i32(x: &i32) -> i32 { x * x } fn main() { let x = 5_i32; - let _result = d_test(x, 1); + let mut dx = 0_i32; + let _result = d_test(&x, &mut dx, 1); } diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/slice.check b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check index 8fc9e9c4f1b..6543b616115 100644 --- a/tests/run-make/autodiff/type-trees/slice-typetree/slice.check +++ b/tests/run-make/autodiff/type-trees/slice-typetree/slice.check @@ -1,4 +1,4 @@ ; Check that slice TypeTree metadata is correctly generated ; Should show Float@double for slice elements -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_slice{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_slice{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/struct.check b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check index 2f763f18c1c..54956317e1e 100644 --- a/tests/run-make/autodiff/type-trees/struct-typetree/struct.check +++ b/tests/run-make/autodiff/type-trees/struct-typetree/struct.check @@ -1,4 +1,4 @@ ; Check that struct TypeTree metadata is correctly generated ; Should show Float@double at offsets 0, 8, 16 for Point struct fields -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_struct{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_struct{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check index 50aa25e96ae..47647e78cc3 100644 --- a/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/tuple.check @@ -1,4 +1,4 @@ ; Check that tuple TypeTree metadata is correctly generated ; Should show Float@double at offsets 0, 8, 16 for (f64, f64, f64) -CHECK: define{{.*}}"enzyme_type"="{[]:Float@double}"{{.*}}@test_tuple{{.*}}"enzyme_type"="{[]:Pointer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_tuple{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double, [-1,8]:Float@double, [-1,16]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check b/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check index dcf9508b69d..cdb70eb83fc 100644 --- a/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check +++ b/tests/run-make/autodiff/type-trees/type-analysis/vec/vec.check @@ -1,7 +1,7 @@ // CHECK: callee - {[-1]:Float@float} |{[-1]:Pointer}:{} // CHECK: ptr %{{[0-9]+}}: {[-1]:Pointer} // CHECK-DAG: %{{[0-9]+}} = getelementptr inbounds nuw i8, ptr %{{[0-9]+}}, i64 8, !dbg !{{[0-9]+}}: {[-1]:Pointer} -// CHECK-DAG: %{{[0-9]+}} = load ptr, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !nonnull !102, !noundef !{{[0-9]+}}: {} +// CHECK-DAG: %{{[0-9]+}} = load ptr, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !nonnull !{{[0-9]+}}, !noundef !{{[0-9]+}}: {} // CHECK-DAG: %{{[0-9]+}} = getelementptr inbounds nuw i8, ptr %{{[0-9]+}}, i64 16, !dbg !{{[0-9]+}}: {[-1]:Pointer} // CHECK-DAG: %{{[0-9]+}} = load i64, ptr %{{[0-9]+}}, align 8, !dbg !{{[0-9]+}}, !noundef !{{[0-9]+}}: {} // CHECK-DAG: %{{[0-9]+}} = icmp eq i64 %{{[0-9]+}}, 0, !dbg !{{[0-9]+}}: {[-1]:Integer} -- cgit 1.4.1-3-g733a5 From 4520926bb527bd43edbf0de84c2b0c6a9c5fc5ce Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Thu, 11 Sep 2025 07:30:35 +0000 Subject: autodiff: recurion added for typetree --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 + compiler/rustc_codegen_llvm/src/typetree.rs | 10 +-- compiler/rustc_middle/src/ty/mod.rs | 76 ++++++++++++++-- .../autodiff/type-trees/nott-flag/with_tt.check | 2 +- .../type-trees/recursion-typetree/recursion.check | 3 + .../type-trees/recursion-typetree/rmake.rs | 9 ++ .../autodiff/type-trees/recursion-typetree/test.rs | 100 +++++++++++++++++++++ .../scalar-types/f128-typetree/f128.check | 2 +- .../type-trees/scalar-types/f16-typetree/f16.check | 2 +- .../type-trees/scalar-types/f32-typetree/f32.check | 2 +- .../type-trees/scalar-types/f64-typetree/f64.check | 2 +- .../type-trees/scalar-types/i32-typetree/i32.check | 2 +- 12 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs create mode 100644 tests/run-make/autodiff/type-trees/recursion-typetree/test.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index e63043b2122..b604f5139c8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -127,6 +127,7 @@ pub(crate) mod Enzyme_AD { ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); + pub(crate) fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; } unsafe extern "C" { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index ae6a2da62b5..1a54884f6c5 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -39,11 +39,7 @@ fn process_typetree_recursive( let mut indices = parent_indices.to_vec(); if !parent_indices.is_empty() { - if rust_type.offset == -1 { - indices.push(-1); - } else { - indices.push(rust_type.offset as i64); - } + indices.push(rust_type.offset as i64); } else if rust_type.offset == -1 { indices.push(-1); } else { @@ -52,7 +48,9 @@ fn process_typetree_recursive( enzyme_tt.insert(&indices, concrete_type, llcx); - if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer && !rust_type.child.0.is_empty() { + if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer + && !rust_type.child.0.is_empty() + { process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx); } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 581f20e8492..7ca2355947a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2252,6 +2252,61 @@ pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { /// Generate TypeTree for a specific type. /// This function analyzes a Rust type and creates appropriate TypeTree metadata. pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + let mut visited = Vec::new(); + typetree_from_ty_inner(tcx, ty, 0, &mut visited) +} + +/// Internal recursive function for TypeTree generation with cycle detection and depth limiting. +fn typetree_from_ty_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, +) -> TypeTree { + #[cfg(llvm_enzyme)] + { + unsafe extern "C" { + fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; + } + let max_depth = unsafe { EnzymeGetMaxTypeDepth() } as usize; + if depth > max_depth { + return TypeTree::new(); + } + } + + #[cfg(not(llvm_enzyme))] + if depth > 6 { + return TypeTree::new(); + } + + if visited.contains(&ty) { + return TypeTree::new(); + } + + visited.push(ty); + let result = typetree_from_ty_impl(tcx, ty, depth, visited); + visited.pop(); + result +} + +/// Implementation of TypeTree generation logic. +fn typetree_from_ty_impl<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, +) -> TypeTree { + typetree_from_ty_impl_inner(tcx, ty, depth, visited, false) +} + +/// Internal implementation with context about whether this is for a reference target. +fn typetree_from_ty_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec>, + is_reference_target: bool, +) -> TypeTree { if ty.is_scalar() { let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) @@ -2267,7 +2322,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { (Kind::Integer, 0) }; - return TypeTree(vec![Type { offset: -1, size, kind, child: TypeTree::new() }]); + // Use offset 0 for scalars that are direct targets of references (like &f64) + // Use offset -1 for scalars used directly (like function return types) + let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; + return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); } if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { @@ -2277,7 +2335,7 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { return TypeTree::new(); }; - let child = typetree_from_ty(tcx, inner_ty); + let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); return TypeTree(vec![Type { offset: -1, size: tcx.data_layout.pointer_size().bytes_usize(), @@ -2292,9 +2350,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if len == 0 { return TypeTree::new(); } - - let element_tree = typetree_from_ty(tcx, *element_ty); - + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); let mut types = Vec::new(); for elem_type in &element_tree.0 { types.push(Type { @@ -2311,7 +2368,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { if ty.is_slice() { if let ty::Slice(element_ty) = ty.kind() { - let element_tree = typetree_from_ty(tcx, *element_ty); + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); return element_tree; } } @@ -2325,7 +2383,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { let mut current_offset = 0; for tuple_ty in tuple_types.iter() { - let element_tree = typetree_from_ty(tcx, tuple_ty); + let element_tree = + typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); let element_layout = tcx .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) @@ -2361,7 +2420,8 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { for (field_idx, field_def) in adt_def.all_fields().enumerate() { let field_ty = field_def.ty(tcx, args); - let field_tree = typetree_from_ty(tcx, field_ty); + let field_tree = + typetree_from_ty_impl_inner(tcx, field_ty, depth + 1, visited, false); let field_offset = layout.fields.offset(field_idx).bytes_usize(); diff --git a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check index 3c02003c882..0b4c9119179 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check +++ b/tests/run-make/autodiff/type-trees/nott-flag/with_tt.check @@ -1,4 +1,4 @@ // Check that enzyme_type attributes are present when TypeTree is enabled // This verifies our TypeTree metadata attachment is working -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@square{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check b/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check new file mode 100644 index 00000000000..1960e7b816c --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/recursion.check @@ -0,0 +1,3 @@ +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_deep{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_graph{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer, [-1,8]:Integer, [-1,16]:Integer, [-1,24]:Float@double}" +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_node{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs new file mode 100644 index 00000000000..78718f3a215 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs @@ -0,0 +1,9 @@ +//@ needs-enzyme +//@ ignore-cross-compile + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + llvm_filecheck().patterns("recursion.check").stdin_buf(rfs::read("test.ll")).run(); +} diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs new file mode 100644 index 00000000000..9d40bec1bf1 --- /dev/null +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/test.rs @@ -0,0 +1,100 @@ +#![feature(autodiff)] + +use std::autodiff::autodiff_reverse; + +// Self-referential struct to test recursion detection +#[derive(Clone)] +struct Node { + value: f64, + next: Option>, +} + +// Mutually recursive structs to test cycle detection +#[derive(Clone)] +struct GraphNodeA { + value: f64, + connections: Vec, +} + +#[derive(Clone)] +struct GraphNodeB { + weight: f64, + target: Option>, +} + +#[autodiff_reverse(d_test_node, Duplicated, Active)] +#[no_mangle] +fn test_node(node: &Node) -> f64 { + node.value * 2.0 +} + +#[autodiff_reverse(d_test_graph, Duplicated, Active)] +#[no_mangle] +fn test_graph(a: &GraphNodeA) -> f64 { + a.value * 3.0 +} + +// Simple depth test - deeply nested but not circular +#[derive(Clone)] +struct Level1 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level2 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level3 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level4 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level5 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level6 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level7 { + val: f64, + next: Option>, +} +#[derive(Clone)] +struct Level8 { + val: f64, +} + +#[autodiff_reverse(d_test_deep, Duplicated, Active)] +#[no_mangle] +fn test_deep(deep: &Level1) -> f64 { + deep.val * 4.0 +} + +fn main() { + let node = Node { value: 1.0, next: None }; + + let graph = GraphNodeA { value: 2.0, connections: vec![] }; + + let deep = Level1 { val: 5.0, next: None }; + + let mut d_node = Node { value: 0.0, next: None }; + + let mut d_graph = GraphNodeA { value: 0.0, connections: vec![] }; + + let mut d_deep = Level1 { val: 0.0, next: None }; + + let _result1 = d_test_node(&node, &mut d_node, 1.0); + let _result2 = d_test_graph(&graph, &mut d_graph, 1.0); + let _result3 = d_test_deep(&deep, &mut d_deep, 1.0); +} diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check index 733e46aa45a..23db64eea52 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/f128.check @@ -1,4 +1,4 @@ ; Check that f128 TypeTree metadata is correctly generated ; Should show Float@fp128 for f128 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@fp128}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@fp128}"{{.*}}@test_f128{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@fp128}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check index 9caca26b5cf..9adff68d36f 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/f16.check @@ -1,4 +1,4 @@ ; Check that f16 TypeTree metadata is correctly generated ; Should show Float@half for f16 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@half}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@half}"{{.*}}@test_f16{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@half}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check index ec12ba6b234..176630f57e8 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/f32.check @@ -1,4 +1,4 @@ ; Check that f32 TypeTree metadata is correctly generated ; Should show Float@float for f32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@float}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@float}"{{.*}}@test_f32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check index f1af270824d..929cd379694 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/f64.check @@ -1,4 +1,4 @@ ; Check that f64 TypeTree metadata is correctly generated ; Should show Float@double for f64 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Float@double}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Float@double}"{{.*}}@test_f64{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@double}" \ No newline at end of file diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check index fd9a94be810..dee4aa5bbb6 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/i32.check @@ -1,4 +1,4 @@ ; Check that i32 TypeTree metadata is correctly generated ; Should show Integer for i32 values and Pointer for references -CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,-1]:Integer}" \ No newline at end of file +CHECK: define{{.*}}"enzyme_type"="{[-1]:Integer}"{{.*}}@test_i32{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Integer}" \ No newline at end of file -- cgit 1.4.1-3-g733a5 From b1a9f231fea0459356f751ad6c1704b55f541118 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 14:41:18 +1000 Subject: Use `LLVMDIBuilderGetOrCreateSubrange` --- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +++++++-------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 8 insertions(+), 15 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index aa8b8bd152d..c3712e5b744 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -117,7 +117,7 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( .try_to_target_usize(cx.tcx) .expect("expected monomorphic const in codegen") as c_longlong; - let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; + let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; let subscripts = &[subrange]; let di_node = unsafe { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1124ebc3d44..d125c1c9c29 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -25,7 +25,7 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, - DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, + DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -890,7 +890,6 @@ pub(crate) mod debuginfo { pub(crate) type DIVariable = DIDescriptor; pub(crate) type DIGlobalVariableExpression = DIDescriptor; pub(crate) type DIArray = DIDescriptor; - pub(crate) type DISubrange = DIDescriptor; pub(crate) type DIEnumerator = DIDescriptor; pub(crate) type DITemplateTypeParameter = DIDescriptor; @@ -1989,6 +1988,12 @@ unsafe extern "C" { Scope: Option<&'ll Metadata>, AlignInBits: u32, // (optional; default is 0) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateSubrange<'ll>( + Builder: &DIBuilder<'ll>, + LowerBound: i64, + Count: i64, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2370,12 +2375,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( - Builder: &DIBuilder<'a>, - Lo: i64, - Count: i64, - ) -> &'a DISubrange; - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( Builder: &DIBuilder<'a>, Ptr: *const Option<&'a DIDescriptor>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321..de79c3e6861 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,12 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, - int64_t Count) { - return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, LLVMMetadataRef *Ptr, unsigned Count) { -- cgit 1.4.1-3-g733a5 From a6d261712ee546fc1fc1b7b7c0ee0782aa827b8b Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 14:44:54 +1000 Subject: Use `LLVMDIBuilderGetOrCreateArray` --- compiler/rustc_codegen_llvm/src/debuginfo/utils.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 ++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --------- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index cc1d504b430..7e1e49310f6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -28,7 +28,7 @@ pub(crate) fn create_DIArray<'ll>( builder: &DIBuilder<'ll>, arr: &[Option<&'ll DIDescriptor>], ) -> &'ll DIArray { - unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) } + unsafe { llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len()) } } #[inline] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d125c1c9c29..290dbda77f6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1994,6 +1994,12 @@ unsafe extern "C" { LowerBound: i64, Count: i64, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateArray<'ll>( + Builder: &DIBuilder<'ll>, + Data: *const Option<&'ll Metadata>, + NumElements: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2375,12 +2381,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( - Builder: &DIBuilder<'a>, - Ptr: *const Option<&'a DIDescriptor>, - Count: c_uint, - ) -> &'a DIArray; - pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( Builder: &DIBuilder<'a>, Val: &'a Value, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index de79c3e6861..b7878692176 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,15 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, - LLVMMetadataRef *Ptr, unsigned Count) { - Metadata **DataValue = unwrap(Ptr); - return wrap(unwrap(Builder) - ->getOrCreateArray(ArrayRef(DataValue, Count)) - .get()); -} - extern "C" void LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, uint64_t *AddrOps, -- cgit 1.4.1-3-g733a5 From 3ba5f19182bf7144c54cbbd0b7af3d4fe76b5317 Mon Sep 17 00:00:00 2001 From: Karan Janthe Date: Fri, 12 Sep 2025 06:11:18 +0000 Subject: autodiff: typetree recursive depth query from enzyme with fallback Signed-off-by: Karan Janthe --- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 - compiler/rustc_codegen_llvm/src/typetree.rs | 10 +++++----- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 12 ++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 21 +++++++-------------- src/llvm-project | 2 +- src/tools/cargo | 2 +- 6 files changed, 26 insertions(+), 22 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index b604f5139c8..e63043b2122 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -127,7 +127,6 @@ pub(crate) mod Enzyme_AD { ); pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); - pub(crate) fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; } unsafe extern "C" { diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 1a54884f6c5..7e263503700 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -1,5 +1,5 @@ use rustc_ast::expand::typetree::FncTree; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] use { crate::attributes, rustc_ast::expand::typetree::TypeTree as RustTypeTree, @@ -8,7 +8,7 @@ use { use crate::llvm::{self, Value}; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] fn to_enzyme_typetree( rust_typetree: RustTypeTree, _data_layout: &str, @@ -18,7 +18,7 @@ fn to_enzyme_typetree( process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); enzyme_tt } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] fn process_typetree_recursive( enzyme_tt: &mut llvm::TypeTree, rust_typetree: &RustTypeTree, @@ -56,7 +56,7 @@ fn process_typetree_recursive( } } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) fn add_tt<'ll>( llmod: &'ll llvm::Module, llcx: &'ll llvm::Context, @@ -111,7 +111,7 @@ pub(crate) fn add_tt<'ll>( } } -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) fn add_tt<'ll>( _llmod: &'ll llvm::Module, _llcx: &'ll llvm::Context, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 64151962321..c1a924a87e4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1847,3 +1847,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { MD.NoHWAddress = true; GV.setSanitizerMetadata(MD); } + +#ifdef ENZYME +extern "C" { +extern llvm::cl::opt EnzymeMaxTypeDepth; +} + +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return EnzymeMaxTypeDepth; } +#else +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { + return 6; // Default fallback depth +} +#endif diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7ca2355947a..ce4de6b95e0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -63,7 +63,7 @@ pub use rustc_type_ir::solve::SizedTraitKind; pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; pub use vtable::*; use {rustc_ast as ast, rustc_hir as hir}; @@ -2256,6 +2256,10 @@ pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { typetree_from_ty_inner(tcx, ty, 0, &mut visited) } +/// Maximum recursion depth for TypeTree generation to prevent stack overflow +/// from pathological deeply nested types. Combined with cycle detection. +const MAX_TYPETREE_DEPTH: usize = 6; + /// Internal recursive function for TypeTree generation with cycle detection and depth limiting. fn typetree_from_ty_inner<'tcx>( tcx: TyCtxt<'tcx>, @@ -2263,19 +2267,8 @@ fn typetree_from_ty_inner<'tcx>( depth: usize, visited: &mut Vec>, ) -> TypeTree { - #[cfg(llvm_enzyme)] - { - unsafe extern "C" { - fn EnzymeGetMaxTypeDepth() -> ::std::os::raw::c_uint; - } - let max_depth = unsafe { EnzymeGetMaxTypeDepth() } as usize; - if depth > max_depth { - return TypeTree::new(); - } - } - - #[cfg(not(llvm_enzyme))] - if depth > 6 { + if depth >= MAX_TYPETREE_DEPTH { + trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); return TypeTree::new(); } diff --git a/src/llvm-project b/src/llvm-project index 2a22c801439..333793696b0 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 2a22c8014390bdf387d8ed26005fee3187fb98bd +Subproject commit 333793696b0a08002acac6af983adc7a5233fc56 diff --git a/src/tools/cargo b/src/tools/cargo index a4bd03c92d3..966f94733bb 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a4bd03c92d3b977909953d06cc45e263bd7ca7d8 +Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 -- cgit 1.4.1-3-g733a5 From 9daa026cadca6de0d932ddb6f6ddbfa5411a25e3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 16:56:28 +1000 Subject: Use `LLVMDIBuilder(CreateExpression|InsertDeclareRecordAtEnd)` --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 15 +++++++------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 27 +++++++++++++++--------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 12 ----------- 3 files changed, 26 insertions(+), 28 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 126082aa3aa..dc36ed17875 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -191,18 +191,21 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { addr_ops.push((fragment.end - fragment.start).bits() as u64); } + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; unsafe { // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. - llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(self.cx()), + llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( + di_builder, variable_alloca, dbg_var, - addr_ops.as_ptr(), - addr_ops.len() as c_uint, + addr_expr, dbg_loc, self.llbb(), - ); - } + ) + }; } fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 290dbda77f6..11de4ed9653 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -806,6 +806,8 @@ unsafe extern "C" { pub(crate) type Metadata; pub(crate) type BasicBlock; pub(crate) type Comdat; + /// `&'ll DbgRecord` represents `LLVMDbgRecordRef`. + pub(crate) type DbgRecord; } #[repr(C)] pub(crate) struct Builder<'a>(InvariantOpaque<'a>); @@ -2000,6 +2002,21 @@ unsafe extern "C" { Data: *const Option<&'ll Metadata>, NumElements: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateExpression<'ll>( + Builder: &DIBuilder<'ll>, + Addr: *const u64, + Length: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderInsertDeclareRecordAtEnd<'ll>( + Builder: &DIBuilder<'ll>, + Storage: &'ll Value, + VarInfo: &'ll Metadata, + Expr: &'ll Metadata, + DebugLoc: &'ll Metadata, + Block: &'ll BasicBlock, + ) -> &'ll DbgRecord; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2381,16 +2398,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( - Builder: &DIBuilder<'a>, - Val: &'a Value, - VarInfo: &'a DIVariable, - AddrOps: *const u64, - AddrOpsCount: c_uint, - DL: &'a DILocation, - InsertAtEnd: &'a BasicBlock, - ); - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b7878692176..fc79cb9d473 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1142,18 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" void -LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, - LLVMMetadataRef VarInfo, uint64_t *AddrOps, - unsigned AddrOpsCount, LLVMMetadataRef DL, - LLVMBasicBlockRef InsertAtEnd) { - unwrap(Builder)->insertDeclare( - unwrap(V), unwrap(VarInfo), - unwrap(Builder)->createExpression( - llvm::ArrayRef(AddrOps, AddrOpsCount)), - DebugLoc(cast(unwrap(DL))), unwrap(InsertAtEnd)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], -- cgit 1.4.1-3-g733a5 From e39e5a0d157a56680cdc10a77551af225b797787 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 16:14:35 +1000 Subject: Use `LLVMDIBuilderCreate(Auto|Parameter)Variable` --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 58 ++++++++++++------------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 43 +++++++++++------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 18 -------- 3 files changed, 57 insertions(+), 62 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index dc36ed17875..66b963436af 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -52,15 +52,6 @@ mod utils; use self::create_scope_map::compute_mir_scopes; pub(crate) use self::metadata::build_global_var_di_node; -// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were -// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp` -// to decide which C++ API to call. Instead, we should just have two separate -// FFI functions and choose the correct one on the Rust side. -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; - /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, @@ -633,28 +624,39 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let type_metadata = spanned_type_di_node(self, variable_type, span); - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable => (0, DW_TAG_auto_variable), - }; let align = self.align_of(variable_type); let name = variable_name.as_str(); - unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(self), - dwarf_tag, - scope_metadata, - name.as_c_char_ptr(), - name.len(), - file_metadata, - loc.line, - type_metadata, - true, - DIFlags::FlagZero, - argument_index, - align.bits() as u32, - ) + + match variable_kind { + ArgumentVariable(arg_index) => unsafe { + llvm::LLVMDIBuilderCreateParameterVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + arg_index as c_uint, + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + ) + }, + LocalVariable => unsafe { + llvm::LLVMDIBuilderCreateAutoVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + align.bits() as u32, + ) + }, } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 11de4ed9653..b0388594f27 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -26,7 +26,7 @@ use super::RustString; use super::debuginfo::{ DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, - DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, + DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -2017,6 +2017,32 @@ unsafe extern "C" { DebugLoc: &'ll Metadata, Block: &'ll BasicBlock, ) -> &'ll DbgRecord; + + pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + AlignInBits: u32, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateParameterVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ArgNo: c_uint, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2383,21 +2409,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - AlwaysPreserve: bool, - Flags: DIFlags, - ArgNo: c_uint, - AlignInBits: u32, - ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index fc79cb9d473..c7fa26f18e1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1124,24 +1124,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( return wrap(VarExpr); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( - LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo, - uint32_t AlignInBits) { - if (Tag == 0x100) { // DW_TAG_auto_variable - return wrap(unwrap(Builder)->createAutoVariable( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, - fromRust(Flags), AlignInBits)); - } else { - return wrap(unwrap(Builder)->createParameterVariable( - unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, - unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, - fromRust(Flags))); - } -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], -- cgit 1.4.1-3-g733a5 From 272d336f0f89610bc18bc276b5f89dc163c69d24 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Sep 2025 17:25:16 +1000 Subject: Remove some unnecessary `as u64` casts In each of these casts, the LHS is already `u64`. --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 66b963436af..af64e4ebed0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -165,21 +165,21 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { if direct_offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(direct_offset.bytes()); } for &offset in indirect_offsets { addr_ops.push(DW_OP_deref); if offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(offset.bytes() as u64); + addr_ops.push(offset.bytes()); } } if let Some(fragment) = fragment { // `DW_OP_LLVM_fragment` takes as arguments the fragment's // offset and size, both of them in bits. addr_ops.push(DW_OP_LLVM_fragment); - addr_ops.push(fragment.start.bits() as u64); - addr_ops.push((fragment.end - fragment.start).bits() as u64); + addr_ops.push(fragment.start.bits()); + addr_ops.push((fragment.end - fragment.start).bits()); } let di_builder = DIB(self.cx()); -- cgit 1.4.1-3-g733a5 From 9acc63a48c8206cfe2c2d272600d538983308657 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Wed, 17 Sep 2025 22:29:49 -0700 Subject: port `#[debugger_visualizer]` to the new attribute system --- .../rustc_attr_parsing/src/attributes/debugger.rs | 60 +++++++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 + compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs | 2 +- compiler/rustc_codegen_ssa/src/base.rs | 4 +- compiler/rustc_hir/src/attrs/data_structures.rs | 19 +++- compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_middle/src/middle/debugger_visualizer.rs | 8 +- compiler/rustc_passes/messages.ftl | 9 -- compiler/rustc_passes/src/check_attr.rs | 16 +-- compiler/rustc_passes/src/debugger_visualizer.rs | 113 ++++++++++----------- compiler/rustc_passes/src/errors.rs | 17 ---- tests/ui/attributes/malformed-attrs.rs | 3 +- tests/ui/attributes/malformed-attrs.stderr | 53 +++++----- .../invalid/invalid-debugger-visualizer-option.rs | 2 +- .../invalid-debugger-visualizer-option.stderr | 22 ++-- .../invalid/invalid-debugger-visualizer-target.rs | 3 +- .../invalid-debugger-visualizer-target.stderr | 4 +- 18 files changed, 185 insertions(+), 154 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/debugger.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs new file mode 100644 index 00000000000..56ff10be426 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -0,0 +1,60 @@ +use rustc_hir::attrs::{DebugVisualizer, DebuggerVisualizerType}; + +use super::prelude::*; + +pub(crate) struct DebuggerViualizerParser; + +impl CombineAttributeParser for DebuggerViualizerParser { + const PATH: &[Symbol] = &[sym::debugger_visualizer]; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!( + List: &[r#"natvis_file = "...", gdb_script_file = "...""#], + "https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute" + ); + + type Item = DebugVisualizer; + const CONVERT: ConvertFn = |v, _| AttributeKind::DebuggerVisualizer(v); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator + 'c { + let Some(l) = args.list() else { + cx.expected_list(args.span().unwrap_or(cx.attr_span)); + return None; + }; + let Some(single) = l.single() else { + cx.expected_single_argument(l.span); + return None; + }; + let Some(mi) = single.meta_item() else { + cx.expected_name_value(single.span(), None); + return None; + }; + let path = mi.path().word_sym(); + let visualizer_type = match path { + Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, + Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, + _ => { + cx.expected_specific_argument( + mi.path().span(), + &[sym::natvis_file, sym::gdb_script_file], + ); + return None; + } + }; + + let Some(path) = mi.args().name_value() else { + cx.expected_name_value(single.span(), path); + return None; + }; + + let Some(path) = path.value_as_str() else { + cx.expected_string_literal(path.value_span, Some(path.value_as_lit())); + return None; + }; + + Some(DebugVisualizer { span: mi.span(), visualizer_type, path }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 4ed13d239b9..8dbf4c0ef32 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -36,6 +36,7 @@ pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod crate_level; +pub(crate) mod debugger; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4c32bb87a24..1f1f9d6d50e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -28,6 +28,7 @@ use crate::attributes::crate_level::{ CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser, }; +use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -163,6 +164,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 7a6dc008c7b..3081badb821 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -2,9 +2,9 @@ use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; -use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType; use rustc_session::config::{CrateType, DebugInfo}; use crate::builder::Builder; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 68a2f43ec67..422b06350e1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -11,12 +11,12 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::OptimizeAttr; +use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8ab43ff2582..5a9e644f970 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -363,6 +363,20 @@ pub struct LinkEntry { pub import_name_type: Option<(PeImportNameType, Span)>, } +#[derive(HashStable_Generic, PrintAttribute)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum DebuggerVisualizerType { + Natvis, + GdbPrettyPrinter, +} + +#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] +pub struct DebugVisualizer { + pub span: Span, + pub visualizer_type: DebuggerVisualizerType, + pub path: Symbol, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -485,7 +499,10 @@ pub enum AttributeKind { /// Represents `#[custom_mir]`. CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), - ///Represents `#[rustc_deny_explicit_impl]`. + /// Represents `#[debugger_visualizer]`. + DebuggerVisualizer(ThinVec), + + /// Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 8e443405074..7112daa327e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -37,6 +37,7 @@ impl AttributeKind { Coverage(..) => No, CrateName { .. } => No, CustomMir(_, _, _) => Yes, + DebuggerVisualizer(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index 5a811321f58..a7f0095dcdf 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,15 +1,9 @@ use std::path::PathBuf; use std::sync::Arc; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_macros::{Decodable, Encodable, HashStable}; -#[derive(HashStable)] -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] -pub enum DebuggerVisualizerType { - Natvis, - GdbPrettyPrinter, -} - /// A single debugger visualizer file. #[derive(HashStable)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6cd68380e46..870e0a90b54 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -93,15 +93,6 @@ passes_dead_codes = } } never {$participle} -passes_debug_visualizer_invalid = - invalid argument - .note_1 = expected: `natvis_file = "..."` - .note_2 = OR - .note_3 = expected: `gdb_script_file = "..."` - -passes_debug_visualizer_placement = - attribute should be applied to a module - passes_debug_visualizer_unreadable = couldn't read {$file}: {$error} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 94c9e71ce77..9f40e713e50 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -281,6 +281,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ObjcClass { .. } | AttributeKind::ObjcSelector { .. } | AttributeKind::RustcCoherenceIsCore(..) + | AttributeKind::DebuggerVisualizer(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -301,7 +302,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut doc_aliases, ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } @@ -1782,20 +1782,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. - fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) { - // Here we only check that the #[debugger_visualizer] attribute is attached - // to nothing other than a module. All other checks are done in the - // `debugger_visualizer` query where they need to be done for decoding - // anyway. - match target { - Target::Mod => {} - _ => { - self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() }); - } - } - } - /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7a7a8175e55..7211f3cf85b 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -1,67 +1,60 @@ //! Detecting usage of the `#[debugger_visualizer]` attribute. -use rustc_ast::Attribute; +use rustc_ast::ast::NodeId; +use rustc_ast::{HasNodeId, ItemKind, ast}; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::resolve_path; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, DebugVisualizer}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::sym; +use rustc_span::{DUMMY_SP, Span, sym}; -use crate::errors::{DebugVisualizerInvalid, DebugVisualizerUnreadable}; +use crate::errors::DebugVisualizerUnreadable; impl DebuggerVisualizerCollector<'_> { - fn check_for_debugger_visualizer(&mut self, attr: &Attribute) { - if attr.has_name(sym::debugger_visualizer) { - let Some(hints) = attr.meta_item_list() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; + fn check_for_debugger_visualizer( + &mut self, + attrs: &[ast::Attribute], + span: Span, + node_id: NodeId, + ) { + if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) = + AttributeParser::parse_limited( + &self.sess, + attrs, + sym::debugger_visualizer, + span, + node_id, + None, + ) + { + for DebugVisualizer { span, visualizer_type, path } in visualizers { + let file = match resolve_path(&self.sess, path.as_str(), span) { + Ok(file) => file, + Err(err) => { + err.emit(); + return; + } + }; - let [hint] = hints.as_slice() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let Some(meta_item) = hint.meta_item() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str()) - { - (Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value), - (Some(sym::gdb_script_file), Some(value)) => { - (DebuggerVisualizerType::GdbPrettyPrinter, value) - } - (_, _) => { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span }); - return; - } - }; - - let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) { - Ok(file) => file, - Err(err) => { - err.emit(); - return; - } - }; - - match self.sess.source_map().load_binary_file(&file) { - Ok((source, _)) => { - self.visualizers.push(DebuggerVisualizerFile::new( - source, - visualizer_type, - file, - )); - } - Err(error) => { - self.sess.dcx().emit_err(DebugVisualizerUnreadable { - span: meta_item.span, - file: &file, - error, - }); + match self.sess.source_map().load_binary_file(&file) { + Ok((source, _)) => { + self.visualizers.push(DebuggerVisualizerFile::new( + source, + visualizer_type, + file, + )); + } + Err(error) => { + self.sess.dcx().emit_err(DebugVisualizerUnreadable { + span, + file: &file, + error, + }); + } } } } @@ -74,9 +67,15 @@ struct DebuggerVisualizerCollector<'a> { } impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> { - fn visit_attribute(&mut self, attr: &'ast Attribute) { - self.check_for_debugger_visualizer(attr); - rustc_ast::visit::walk_attribute(self, attr); + fn visit_item(&mut self, item: &'ast rustc_ast::Item) -> Self::Result { + if let ItemKind::Mod(..) = item.kind { + self.check_for_debugger_visualizer(&item.attrs, item.span, item.node_id()); + } + rustc_ast::visit::walk_item(self, item); + } + fn visit_crate(&mut self, krate: &'ast ast::Crate) -> Self::Result { + self.check_for_debugger_visualizer(&krate.attrs, DUMMY_SP, krate.id); + rustc_ast::visit::walk_crate(self, krate); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index cd8935f6b2f..cfd6b9e6dff 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -475,23 +475,6 @@ pub(crate) struct MacroOnlyAttribute { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_debug_visualizer_placement)] -pub(crate) struct DebugVisualizerPlacement { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_debug_visualizer_invalid)] -#[note(passes_note_1)] -#[note(passes_note_2)] -#[note(passes_note_3)] -pub(crate) struct DebugVisualizerInvalid { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_debug_visualizer_unreadable)] pub(crate) struct DebugVisualizerUnreadable<'a> { diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index e30479b03b1..820484aa015 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -185,8 +185,7 @@ extern "C" { #[forbid] //~^ ERROR malformed #[debugger_visualizer] -//~^ ERROR invalid argument -//~| ERROR malformed `debugger_visualizer` attribute input +//~^ ERROR malformed `debugger_visualizer` attribute input #[automatically_derived = 18] //~^ ERROR malformed mod yooo { diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 246029ecf80..70ab3fb13c4 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -22,7 +22,7 @@ LL | #[cfg_attr(condition, attribute, other_attribute, ...)] | ++++++++++++++++++++++++++++++++++++++++++++ error[E0463]: can't find crate for `wloop` - --> $DIR/malformed-attrs.rs:210:1 + --> $DIR/malformed-attrs.rs:209:1 | LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate @@ -156,22 +156,14 @@ LL | #[forbid(lint1, lint2, ...)] LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ -error: malformed `debugger_visualizer` attribute input - --> $DIR/malformed-attrs.rs:187:1 - | -LL | #[debugger_visualizer] - | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` - | - = note: for more information, visit - error: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:202:1 + --> $DIR/malformed-attrs.rs:201:1 | LL | #[thread_local()] | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[thread_local]` error: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:206:1 + --> $DIR/malformed-attrs.rs:205:1 | LL | #[no_link()] | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]` @@ -197,7 +189,7 @@ LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint - --> $DIR/malformed-attrs.rs:215:1 + --> $DIR/malformed-attrs.rs:214:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,16 +218,6 @@ LL | #[doc] = note: for more information, see issue #57571 = note: for more information, visit -error: invalid argument - --> $DIR/malformed-attrs.rs:187:1 - | -LL | #[debugger_visualizer] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected: `natvis_file = "..."` - = note: OR - = note: expected: `gdb_script_file = "..."` - error[E0539]: malformed `export_name` attribute input --> $DIR/malformed-attrs.rs:29:1 | @@ -685,8 +667,19 @@ LL | #[linkage = "external"] | ++++++++++++ = and 5 other candidates +error[E0539]: malformed `debugger_visualizer` attribute input + --> $DIR/malformed-attrs.rs:187:1 + | +LL | #[debugger_visualizer] + | ^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` + | + = note: for more information, visit + error[E0565]: malformed `automatically_derived` attribute input - --> $DIR/malformed-attrs.rs:190:1 + --> $DIR/malformed-attrs.rs:189:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -695,7 +688,7 @@ LL | #[automatically_derived = 18] | help: must be of the form: `#[automatically_derived]` error[E0565]: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[non_exhaustive = 1] | ^^^^^^^^^^^^^^^^^---^ @@ -704,19 +697,19 @@ LL | #[non_exhaustive = 1] | help: must be of the form: `#[non_exhaustive]` error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` - --> $DIR/malformed-attrs.rs:208:1 + --> $DIR/malformed-attrs.rs:207:1 | LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` - --> $DIR/malformed-attrs.rs:213:1 + --> $DIR/malformed-attrs.rs:212:1 | LL | #[macro_export = 18] | ^^^^^^^^^^^^^^^^^^^^ error[E0565]: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:215:1 + --> $DIR/malformed-attrs.rs:214:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^---^ @@ -800,7 +793,7 @@ LL | #[ignore()] = note: for more information, see issue #57571 error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:222:1 + --> $DIR/malformed-attrs.rs:221:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ @@ -819,7 +812,7 @@ LL | #[coroutine = 63] || {} = note: expected unit type `()` found coroutine `{coroutine@$DIR/malformed-attrs.rs:110:23: 110:25}` -error: aborting due to 77 previous errors; 3 warnings emitted +error: aborting due to 76 previous errors; 3 warnings emitted Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. For more information about an error, try `rustc --explain E0308`. @@ -871,7 +864,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:222:1 + --> $DIR/malformed-attrs.rs:221:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ diff --git a/tests/ui/invalid/invalid-debugger-visualizer-option.rs b/tests/ui/invalid/invalid-debugger-visualizer-option.rs index 0f1cf15a687..166962866dc 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-option.rs +++ b/tests/ui/invalid/invalid-debugger-visualizer-option.rs @@ -1,6 +1,6 @@ //@ normalize-stderr: "foo.random:.*\(" -> "foo.random: $$FILE_NOT_FOUND_MSG (" //@ normalize-stderr: "os error \d+" -> "os error $$FILE_NOT_FOUND_CODE" -#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument +#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR malformed `debugger_visualizer` attribute input #![debugger_visualizer(natvis_file = "../foo.random")] //~ ERROR fn main() {} diff --git a/tests/ui/invalid/invalid-debugger-visualizer-option.stderr b/tests/ui/invalid/invalid-debugger-visualizer-option.stderr index 6fbb4d641e6..e877e39d8f1 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-option.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-option.stderr @@ -1,18 +1,20 @@ -error: invalid argument - --> $DIR/invalid-debugger-visualizer-option.rs:4:24 - | -LL | #![debugger_visualizer(random_file = "../foo.random")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expected: `natvis_file = "..."` - = note: OR - = note: expected: `gdb_script_file = "..."` - error: couldn't read $DIR/../foo.random: $FILE_NOT_FOUND_MSG (os error $FILE_NOT_FOUND_CODE) --> $DIR/invalid-debugger-visualizer-option.rs:5:24 | LL | #![debugger_visualizer(natvis_file = "../foo.random")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0539]: malformed `debugger_visualizer` attribute input + --> $DIR/invalid-debugger-visualizer-option.rs:4:1 + | +LL | #![debugger_visualizer(random_file = "../foo.random")] + | ^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^^^^^^^ + | | | + | | valid arguments are `natvis_file` or `gdb_script_file` + | help: must be of the form: `#![debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]` + | + = note: for more information, visit + error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.rs b/tests/ui/invalid/invalid-debugger-visualizer-target.rs index 1efb9555c24..48b04153214 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.rs +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.rs @@ -1,2 +1,3 @@ -#[debugger_visualizer(natvis_file = "./foo.natvis.xml")] //~ ERROR attribute should be applied to a module +#[debugger_visualizer(natvis_file = "./foo.natvis.xml")] +//~^ ERROR `#[debugger_visualizer]` attribute cannot be used on functions fn main() {} diff --git a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr index 1df34532533..629af94c5ef 100644 --- a/tests/ui/invalid/invalid-debugger-visualizer-target.stderr +++ b/tests/ui/invalid/invalid-debugger-visualizer-target.stderr @@ -1,8 +1,10 @@ -error: attribute should be applied to a module +error: `#[debugger_visualizer]` attribute cannot be used on functions --> $DIR/invalid-debugger-visualizer-target.rs:1:1 | LL | #[debugger_visualizer(natvis_file = "./foo.natvis.xml")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[debugger_visualizer]` can be applied to modules and crates error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From f5c6c9542ecfab74e654f9b7f1150d486560ae43 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 16 Sep 2025 02:23:24 -0400 Subject: Add an attribute to check the number of lanes in a SIMD vector after monomorphization Unify zero-length and oversized SIMD errors --- .../src/attributes/crate_level.rs | 36 --------------------- .../src/attributes/rustc_internal.rs | 18 +++++++++++ compiler/rustc_attr_parsing/src/attributes/util.rs | 37 +++++++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_codegen_cranelift/src/common.rs | 9 ++++-- compiler/rustc_codegen_cranelift/src/global_asm.rs | 5 ++- compiler/rustc_codegen_gcc/src/context.rs | 9 ++++-- compiler/rustc_codegen_llvm/src/context.rs | 11 +++++-- compiler/rustc_feature/src/builtin_attrs.rs | 6 ++++ compiler/rustc_hir/src/attrs/data_structures.rs | 3 ++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir_analysis/src/hir_ty_lowering/cmse.rs | 1 + compiler/rustc_middle/messages.ftl | 6 ++++ compiler/rustc_middle/src/error.rs | 6 ++++ compiler/rustc_middle/src/ty/layout.rs | 26 +++++++++++++++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_transmute/src/layout/tree.rs | 1 + compiler/rustc_ty_utils/src/errors.rs | 13 -------- compiler/rustc_ty_utils/src/layout.rs | 26 ++++++++++++--- src/librustdoc/html/templates/type_layout.html | 5 +++ tests/ui/simd/auxiliary/simd-lane-limit.rs | 5 +++ tests/ui/simd/monomorphize-too-long.rs | 4 +-- tests/ui/simd/monomorphize-too-long.stderr | 6 +++- tests/ui/simd/monomorphize-zero-length.rs | 4 +-- tests/ui/simd/monomorphize-zero-length.stderr | 6 +++- tests/ui/simd/simd-lane-limit-err-npow2.rs | 12 +++++++ tests/ui/simd/simd-lane-limit-err-npow2.stderr | 8 +++++ tests/ui/simd/simd-lane-limit-err.rs | 11 +++++++ tests/ui/simd/simd-lane-limit-err.stderr | 8 +++++ tests/ui/simd/simd-lane-limit-ok.rs | 14 ++++++++ .../ui/simd/type-generic-monomorphisation-empty.rs | 4 +-- .../type-generic-monomorphisation-empty.stderr | 6 +++- .../type-generic-monomorphisation-oversized.rs | 5 ++- .../type-generic-monomorphisation-oversized.stderr | 6 +++- 35 files changed, 245 insertions(+), 78 deletions(-) create mode 100644 tests/ui/simd/auxiliary/simd-lane-limit.rs create mode 100644 tests/ui/simd/simd-lane-limit-err-npow2.rs create mode 100644 tests/ui/simd/simd-lane-limit-err-npow2.stderr create mode 100644 tests/ui/simd/simd-lane-limit-err.rs create mode 100644 tests/ui/simd/simd-lane-limit-err.stderr create mode 100644 tests/ui/simd/simd-lane-limit-ok.rs (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 4611de44459..00edafecd9a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,40 +1,4 @@ -use std::num::IntErrorKind; - -use rustc_hir::limit::Limit; - use super::prelude::*; -use crate::session_diagnostics::LimitInvalid; - -impl AcceptContext<'_, '_, S> { - fn parse_limit_int(&self, nv: &NameValueParser) -> Option { - let Some(limit) = nv.value_as_str() else { - self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); - return None; - }; - - let error_str = match limit.as_str().parse() { - Ok(i) => return Some(Limit::new(i)), - Err(e) => match e.kind() { - IntErrorKind::PosOverflow => "`limit` is too large", - IntErrorKind::Empty => "`limit` must be a non-negative integer", - IntErrorKind::InvalidDigit => "not a valid integer", - IntErrorKind::NegOverflow => { - panic!( - "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" - ) - } - IntErrorKind::Zero => { - panic!("zero is a valid `limit` so should have returned Ok() when parsing") - } - kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), - }, - }; - - self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); - - None - } -} pub(crate) struct CrateNameParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index a995549fc7c..cf2f5c6c790 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -49,3 +49,21 @@ impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { Some(AttributeKind::RustcObjectLifetimeDefault) } } + +pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; + +impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser { + const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 77e8c32e59d..62b72798e96 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,11 +1,15 @@ +use std::num::IntErrorKind; + use rustc_ast::LitKind; use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; +use rustc_hir::limit::Limit; use rustc_span::{Symbol, sym}; use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::parser::{ArgParser, NameValueParser}; +use crate::session_diagnostics::LimitInvalid; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are @@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer( }; Some(num.0) } + +impl AcceptContext<'_, '_, S> { + pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option { + let Some(limit) = nv.value_as_str() else { + self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + let error_str = match limit.as_str().parse() { + Ok(i) => return Some(Limit::new(i)), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => "`limit` is too large", + IntErrorKind::Empty => "`limit` must be a non-negative integer", + IntErrorKind::InvalidDigit => "not a valid integer", + IntErrorKind::NegOverflow => { + panic!( + "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" + ) + } + IntErrorKind::Zero => { + panic!("zero is a valid `limit` so should have returned Ok() when parsing") + } + kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + }, + }; + + self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); + + None + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d7998048be5..72fe269b135 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -53,7 +53,7 @@ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, - RustcObjectLifetimeDefaultParser, + RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -195,6 +195,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 2fbe5c02802..81b1814605a 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.0.sess.dcx().span_fatal(span, err.to_string()) } else { self.0 @@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.0.sess.dcx().emit_fatal(Spanned { span, node: err }) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 203b443269f..1306c6aa517 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.sess.dcx().span_fatal(span, err.to_string()) } else { self.tcx diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 665cf22ddba..9815fb07eaa 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic())) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.tcx.dcx().emit_fatal(respan(span, err)) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a..e4528efaa37 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -991,7 +991,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::ReferencesError(_) + | LayoutError::InvalidSimd { .. } = err + { self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() }) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -1008,7 +1011,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { match err { - FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => { + FnAbiError::Layout( + LayoutError::SizeOverflow(_) + | LayoutError::Cycle(_) + | LayoutError::InvalidSimd { .. }, + ) => { self.tcx.dcx().emit_fatal(Spanned { span, node: err }); } _ => match fn_abi_request { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 129ab7eccb5..0d890b57de0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1203,6 +1203,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ niche optimizations in the standard library", ), + rustc_attr!( + rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \ + for better error messages", + ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ea11a99efbc..80cb6417684 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -642,6 +642,9 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. + RustcSimdMonomorphizeLaneLimit(Limit), + /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 55521c15854..968ea668efe 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -85,6 +85,7 @@ impl AttributeKind { RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e..76b0ce985f0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -213,6 +213,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError } Unknown(..) | SizeOverflow(..) + | InvalidSimd { .. } | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 279ab9a9d8f..82f3df8da3b 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -98,6 +98,12 @@ middle_layout_references_error = middle_layout_size_overflow = values of the type `{$ty}` are too big for the target architecture +middle_layout_simd_too_many = + the SIMD type `{$ty}` has more elements than the limit {$max_lanes} + +middle_layout_simd_zero_length = + the SIMD type `{$ty}` has zero elements + middle_layout_too_generic = the type `{$ty}` does not have a fixed layout middle_layout_unknown = diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dad402ec696..e15da827bda 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -141,6 +141,12 @@ pub enum LayoutError<'tcx> { #[diag(middle_layout_size_overflow)] Overflow { ty: Ty<'tcx> }, + #[diag(middle_layout_simd_too_many)] + SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 }, + + #[diag(middle_layout_simd_zero_length)] + SimdZeroLength { ty: Ty<'tcx> }, + #[diag(middle_layout_normalization_failure)] NormalizationFailure { ty: Ty<'tcx>, failure_ty: String }, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa..47e561d9d3c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -217,6 +217,15 @@ impl fmt::Display for ValidityRequirement { } } +#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] +pub enum SimdLayoutError { + /// The vector has 0 lanes. + ZeroLength, + /// The vector has more lanes than supported or permitted by + /// #\[rustc_simd_monomorphize_lane_limit\]. + TooManyLanes(u64), +} + #[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] pub enum LayoutError<'tcx> { /// A type doesn't have a sensible layout. @@ -229,6 +238,8 @@ pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), /// The size of a type exceeds [`TargetDataLayout::obj_size_bound`]. SizeOverflow(Ty<'tcx>), + /// A SIMD vector has invalid layout, such as zero-length or too many lanes. + InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError }, /// The layout can vary due to a generic parameter. /// /// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout @@ -256,6 +267,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(_) => middle_layout_unknown, SizeOverflow(_) => middle_layout_size_overflow, + InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => { + middle_layout_simd_too_many + } + InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length, TooGeneric(_) => middle_layout_too_generic, NormalizationFailure(_, _) => middle_layout_normalization_failure, Cycle(_) => middle_layout_cycle, @@ -270,6 +285,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(ty) => E::Unknown { ty }, SizeOverflow(ty) => E::Overflow { ty }, + InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + E::SimdTooManyLanes { ty, max_lanes } + } + InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty }, TooGeneric(ty) => E::TooGeneric { ty }, NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } @@ -292,6 +311,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}") + } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => { + write!(f, "the SIMD type `{ty}` has zero elements") + } LayoutError::NormalizationFailure(t, e) => write!( f, "unable to determine layout for `{}` because `{}` cannot be normalized", @@ -373,6 +398,7 @@ impl<'tcx> SizeSkeleton<'tcx> { e @ LayoutError::Cycle(_) | e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) + | e @ LayoutError::InvalidSimd { .. } | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), ) => return Err(e), diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2562d2e0b83..cabf2819741 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -254,6 +254,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MacroEscape( .. ) | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) + | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::ExportStable | AttributeKind::FfiConst(..) | AttributeKind::UnstableFeatureBound(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da..92a1fad4755 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1937,6 +1937,7 @@ symbols! { rustc_regions, rustc_reservation_impl, rustc_serialize, + rustc_simd_monomorphize_lane_limit, rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d7ea26a2ab1..a02e8ecf613 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -279,6 +279,7 @@ pub(crate) mod rustc { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) | LayoutError::TooGeneric(..) + | LayoutError::InvalidSimd { .. } | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index 0298e7e0e95..f92c405242c 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -78,19 +78,6 @@ pub(crate) struct UnexpectedFnPtrAssociatedItem { pub span: Span, } -#[derive(Diagnostic)] -#[diag(ty_utils_zero_length_simd_type)] -pub(crate) struct ZeroLengthSimdType<'tcx> { - pub ty: Ty<'tcx>, -} - -#[derive(Diagnostic)] -#[diag(ty_utils_oversized_simd_type)] -pub(crate) struct OversizedSimdType<'tcx> { - pub ty: Ty<'tcx>, - pub max_lanes: u64, -} - #[derive(Diagnostic)] #[diag(ty_utils_non_primitive_simd_type)] pub(crate) struct NonPrimitiveSimdType<'tcx> { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2a..5c4451ce264 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -7,11 +7,13 @@ use rustc_abi::{ VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, + FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -22,7 +24,7 @@ use rustc_span::{Symbol, sym}; use tracing::{debug, instrument}; use {rustc_abi as abi, rustc_hir as hir}; -use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType}; +use crate::errors::NonPrimitiveSimdType; mod invariant; @@ -120,11 +122,11 @@ fn map_error<'tcx>( } LayoutCalculatorError::ZeroLengthSimdType => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } } LayoutCalculatorError::OversizedSimdType { max_lanes } => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } } LayoutCalculatorError::NonPrimitiveSimdType(field) => { // This error isn't caught in typeck, e.g., if @@ -561,6 +563,22 @@ fn layout_of_uncached<'tcx>( let e_ly = cx.layout_of(e_ty)?; + // Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit + if let Some(limit) = find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit + ) { + if !limit.value_within_limit(e_len as usize) { + return Err(map_error( + &cx, + ty, + rustc_abi::LayoutCalculatorError::OversizedSimdType { + max_lanes: limit.0 as u64, + }, + )); + } + } + map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))? } diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index 0034552bdd3..49153d58fe9 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -65,5 +65,10 @@ Note: Encountered an error during type layout; {#+ #} the type's layout depended on the type's layout itself. {# #}

+ {% when Err(LayoutError::InvalidSimd {..}) %} +

{# #} + Note: Encountered an error during type layout; {#+ #} + the vector type had zero elements or too many elements. {# #} +

{% endmatch %} {# #} diff --git a/tests/ui/simd/auxiliary/simd-lane-limit.rs b/tests/ui/simd/auxiliary/simd-lane-limit.rs new file mode 100644 index 00000000000..dde6b880c62 --- /dev/null +++ b/tests/ui/simd/auxiliary/simd-lane-limit.rs @@ -0,0 +1,5 @@ +#![feature(rustc_attrs, repr_simd)] + +#[repr(simd, packed)] +#[rustc_simd_monomorphize_lane_limit = "8"] +pub struct Simd(pub [T; N]); diff --git a/tests/ui/simd/monomorphize-too-long.rs b/tests/ui/simd/monomorphize-too-long.rs index 4fac987b0b5..9c837415191 100644 --- a/tests/ui/simd/monomorphize-too-long.rs +++ b/tests/ui/simd/monomorphize-too-long.rs @@ -6,7 +6,5 @@ struct Simd([T; N]); fn main() { - let _too_big = Simd([1_u16; 54321]); + let _too_big = Simd([1_u16; 54321]); //~ ERROR the SIMD type `Simd` has more elements than the limit 32768 } - -//~? ERROR monomorphising SIMD type `Simd` of length greater than 32768 diff --git a/tests/ui/simd/monomorphize-too-long.stderr b/tests/ui/simd/monomorphize-too-long.stderr index 978eef307ab..71bc78ef5c9 100644 --- a/tests/ui/simd/monomorphize-too-long.stderr +++ b/tests/ui/simd/monomorphize-too-long.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd` of length greater than 32768 +error: the SIMD type `Simd` has more elements than the limit 32768 + --> $DIR/monomorphize-too-long.rs:9:9 + | +LL | let _too_big = Simd([1_u16; 54321]); + | ^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/monomorphize-zero-length.rs b/tests/ui/simd/monomorphize-zero-length.rs index d38870c572d..f956197a61c 100644 --- a/tests/ui/simd/monomorphize-zero-length.rs +++ b/tests/ui/simd/monomorphize-zero-length.rs @@ -6,7 +6,5 @@ struct Simd([T; N]); fn main() { - let _empty = Simd([1.0; 0]); + let _empty = Simd([1.0; 0]); //~ ERROR the SIMD type `Simd` has zero elements } - -//~? ERROR monomorphising SIMD type `Simd` of zero length diff --git a/tests/ui/simd/monomorphize-zero-length.stderr b/tests/ui/simd/monomorphize-zero-length.stderr index 738c20fe51a..66f26d95c9d 100644 --- a/tests/ui/simd/monomorphize-zero-length.stderr +++ b/tests/ui/simd/monomorphize-zero-length.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd` of zero length +error: the SIMD type `Simd` has zero elements + --> $DIR/monomorphize-zero-length.rs:9:9 + | +LL | let _empty = Simd([1.0; 0]); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/simd-lane-limit-err-npow2.rs b/tests/ui/simd/simd-lane-limit-err-npow2.rs new file mode 100644 index 00000000000..d5c5c92e953 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err-npow2.rs @@ -0,0 +1,12 @@ +//@ build-fail +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + // test non-power-of-two, since #[repr(simd, packed)] has unusual layout + let _x: Simd = Simd([0; 24]); + //~^ ERROR the SIMD type `simd::Simd` has more elements than the limit 8 +} diff --git a/tests/ui/simd/simd-lane-limit-err-npow2.stderr b/tests/ui/simd/simd-lane-limit-err-npow2.stderr new file mode 100644 index 00000000000..fff26c4c1c1 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err-npow2.stderr @@ -0,0 +1,8 @@ +error: the SIMD type `simd::Simd` has more elements than the limit 8 + --> $DIR/simd-lane-limit-err-npow2.rs:10:9 + | +LL | let _x: Simd = Simd([0; 24]); + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/simd-lane-limit-err.rs b/tests/ui/simd/simd-lane-limit-err.rs new file mode 100644 index 00000000000..00390bdbdaf --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err.rs @@ -0,0 +1,11 @@ +//@ build-fail +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + let _x: Simd = Simd([0; 16]); + //~^ ERROR the SIMD type `simd::Simd` has more elements than the limit 8 +} diff --git a/tests/ui/simd/simd-lane-limit-err.stderr b/tests/ui/simd/simd-lane-limit-err.stderr new file mode 100644 index 00000000000..3f2eaeda2d4 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-err.stderr @@ -0,0 +1,8 @@ +error: the SIMD type `simd::Simd` has more elements than the limit 8 + --> $DIR/simd-lane-limit-err.rs:9:9 + | +LL | let _x: Simd = Simd([0; 16]); + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/simd/simd-lane-limit-ok.rs b/tests/ui/simd/simd-lane-limit-ok.rs new file mode 100644 index 00000000000..52fd3158440 --- /dev/null +++ b/tests/ui/simd/simd-lane-limit-ok.rs @@ -0,0 +1,14 @@ +//@ build-pass +//@ aux-crate:simd=simd-lane-limit.rs + +extern crate simd; + +use simd::Simd; + +fn main() { + let _x: Simd = Simd([0; 4]); + let _y: Simd = Simd([0; 8]); + + // test non-power-of-two, since #[repr(simd, packed)] has unusual layout + let _z: Simd = Simd([0; 6]); +} diff --git a/tests/ui/simd/type-generic-monomorphisation-empty.rs b/tests/ui/simd/type-generic-monomorphisation-empty.rs index c08dc9fe3df..7c43b8914da 100644 --- a/tests/ui/simd/type-generic-monomorphisation-empty.rs +++ b/tests/ui/simd/type-generic-monomorphisation-empty.rs @@ -6,7 +6,5 @@ struct Simd([f32; N]); fn main() { - let _ = Simd::<0>([]); + let _empty = Simd::<0>([]); //~ ERROR the SIMD type `Simd<0>` has zero elements } - -//~? ERROR monomorphising SIMD type `Simd<0>` of zero length diff --git a/tests/ui/simd/type-generic-monomorphisation-empty.stderr b/tests/ui/simd/type-generic-monomorphisation-empty.stderr index fc294607ae3..450db7e47db 100644 --- a/tests/ui/simd/type-generic-monomorphisation-empty.stderr +++ b/tests/ui/simd/type-generic-monomorphisation-empty.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd<0>` of zero length +error: the SIMD type `Simd<0>` has zero elements + --> $DIR/type-generic-monomorphisation-empty.rs:9:9 + | +LL | let _empty = Simd::<0>([]); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/simd/type-generic-monomorphisation-oversized.rs b/tests/ui/simd/type-generic-monomorphisation-oversized.rs index efe3480317c..73a1f00e8c7 100644 --- a/tests/ui/simd/type-generic-monomorphisation-oversized.rs +++ b/tests/ui/simd/type-generic-monomorphisation-oversized.rs @@ -6,7 +6,6 @@ struct Simd([f32; N]); fn main() { - let _ = Simd::<65536>([0.; 65536]); + let _x = Simd::<65536>([0.; 65536]); + //~^ ERROR the SIMD type `Simd<65536>` has more elements than the limit 32768 } - -//~? ERROR monomorphising SIMD type `Simd<65536>` of length greater than 32768 diff --git a/tests/ui/simd/type-generic-monomorphisation-oversized.stderr b/tests/ui/simd/type-generic-monomorphisation-oversized.stderr index 39ff36799cc..0065049abd6 100644 --- a/tests/ui/simd/type-generic-monomorphisation-oversized.stderr +++ b/tests/ui/simd/type-generic-monomorphisation-oversized.stderr @@ -1,4 +1,8 @@ -error: monomorphising SIMD type `Simd<65536>` of length greater than 32768 +error: the SIMD type `Simd<65536>` has more elements than the limit 32768 + --> $DIR/type-generic-monomorphisation-oversized.rs:9:9 + | +LL | let _x = Simd::<65536>([0.; 65536]); + | ^^ error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From 60548ffaa3dafec019e52f39ab7a075d3f11e24e Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 23 Sep 2025 22:14:25 -0400 Subject: Including spans in layout errors for all ADTs --- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc3a84b6a15..7ecba13d727 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -469,8 +469,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id), ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id), ty::Adt(def, ..) => match def.adt_kind() { - AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), - AdtKind::Union => build_union_type_di_node(cx, unique_type_id), + AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span), + AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span), AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), @@ -1066,6 +1066,7 @@ fn visibility_di_flags<'ll, 'tcx>( fn build_struct_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let struct_type = unique_type_id.expect_ty(); let ty::Adt(adt_def, _) = struct_type.kind() else { @@ -1073,7 +1074,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( }; assert!(adt_def.is_struct()); let containing_scope = get_namespace_for_item(cx, adt_def.did()); - let struct_type_and_layout = cx.layout_of(struct_type); + let struct_type_and_layout = cx.spanned_layout_of(struct_type, span); let variant_def = adt_def.non_enum_variant(); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(adt_def.did()))) @@ -1266,6 +1267,7 @@ fn build_closure_env_di_node<'ll, 'tcx>( fn build_union_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let union_type = unique_type_id.expect_ty(); let (union_def_id, variant_def) = match union_type.kind() { @@ -1273,7 +1275,7 @@ fn build_union_type_di_node<'ll, 'tcx>( _ => bug!("build_union_type_di_node on a non-ADT"), }; let containing_scope = get_namespace_for_item(cx, union_def_id); - let union_ty_and_layout = cx.layout_of(union_type); + let union_ty_and_layout = cx.spanned_layout_of(union_type, span); let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(union_def_id))) -- cgit 1.4.1-3-g733a5 From 85018f09f67bd54868fe12a4632bbd637a474853 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 25 Sep 2025 18:10:55 +1000 Subject: Use `LLVMDisposeTargetMachine` --- compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs | 4 +--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 ++- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 4 ---- 3 files changed, 3 insertions(+), 8 deletions(-) (limited to 'compiler/rustc_codegen_llvm/src') diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index d5228f0e0de..903a882916e 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -95,8 +95,6 @@ impl Drop for OwnedTargetMachine { // SAFETY: constructing ensures we have a valid pointer created by // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no // double free or use after free. - unsafe { - llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr()); - } + unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) }; } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index fd972f371df..38a6a311954 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1053,6 +1053,8 @@ unsafe extern "C" { SLen: c_uint, ) -> MetadataKindId; + pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull); + // Create modules. pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, @@ -2499,7 +2501,6 @@ unsafe extern "C" { UseWasmEH: bool, ) -> *mut TargetMachine; - pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); pub(crate) fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, M: &'a Module, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 7518b40799b..013d68fa3e4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -359,10 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( return wrap(TM); } -extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { - delete unwrap(TM); -} - // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, -- cgit 1.4.1-3-g733a5