diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 809b8b7f6a74d..d4306ab27a106 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1624,6 +1624,7 @@ impl Expr { | ExprKind::While(..) | ExprKind::Yield(YieldKind::Postfix(..)) | ExprKind::DirectConstArg(..) + | ExprKind::BtfFieldInfo(..) | ExprKind::Err(_) | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } @@ -1923,6 +1924,9 @@ pub enum ExprKind { /// An mGCA `direct_const_arg!()` expression. DirectConstArg(Box), + /// A BTF field metadata query. + BtfFieldInfo(BtfRelocKind, Box, ThinVec), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -2195,6 +2199,50 @@ impl YieldKind { } } +/// The kind of [BPF Type Format (BTF)][btf] relocation. +/// +/// [BTF][btf] is the type metadata format used by the Linux kernel and eBPF +/// tooling for relocations: the compiled program records which field or array +/// element it intended to access, and the loader rewrites the bytecode to +/// match the layout of the kernel it is about to run on. +/// +/// The following variants are a subset of the relocation kinds defined by +/// Linux's [`bpf_core_relo_kind`]. +/// +/// [btf]: https://docs.kernel.org/bpf/btf.html +/// [`bpf_core_relo_kind`]: https://docs.kernel.org/bpf/llvm_reloc.html#relocation-kinds +#[derive(Clone, Copy, Encodable, Decodable, Debug, Eq, PartialEq, StableHash, Walkable)] +pub enum BtfRelocKind { + /// Offset of the field. + ByteOffset, + /// Size of the field. + ByteSize, + /// Whether the field exists. + Exists, +} + +impl BtfRelocKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::ByteOffset => "btf_field_byte_offset", + Self::ByteSize => "btf_field_byte_size", + Self::Exists => "btf_field_exists", + } + } + + /// Returns a code number associated with the given relocation kind that matches Linux's + /// [`bpf_core_relo_kind`]. + /// + /// [`bpf_core_relo_kind`]: https://docs.kernel.org/bpf/llvm_reloc.html#relocation-kinds + pub fn code(&self) -> u32 { + match self { + Self::ByteOffset => 0, + Self::ByteSize => 1, + Self::Exists => 2, + } + } +} + /// A literal in a meta item. #[derive(Clone, Copy, Encodable, Decodable, Debug, StableHash)] pub struct MetaItemLit { diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index e799f73ff544f..4765fa0bb53c8 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -159,6 +159,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { | Yield(..) | UnsafeBinderCast(..) | DirectConstArg(..) + | BtfFieldInfo(..) | Err(..) | Dummy => return false, } @@ -218,7 +219,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac)); } - InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => { + InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) | BtfFieldInfo(..) => { // These should have been denied pre-expansion. break None; } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index c12f24a7eff87..79c0586d4d89b 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -501,6 +501,7 @@ macro_rules! common_visitor_and_walkers { BoundAsyncness, BoundConstness, BoundPolarity, + BtfRelocKind, ByRef, Closure, Const, @@ -1128,6 +1129,8 @@ macro_rules! common_visitor_and_walkers { visit_visitable!(vis, kind, expr, ty), ExprKind::DirectConstArg(expr) => visit_visitable!(vis, expr), + ExprKind::BtfFieldInfo(kind, container, fields) => + visit_visitable!($($mut)? vis, kind, container, fields), ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 630c65ea0a450..5e35f153f6c2a 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -547,6 +547,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let e = self.emit_bad_direct_const_arg(e.span, expr, "expression"); hir::ExprKind::Err(e) } + + ExprKind::BtfFieldInfo(kind, container, fields) => hir::ExprKind::BtfFieldInfo( + *kind, + self.lower_ty_alloc( + container, + ImplTraitContext::Disallowed(ImplTraitPosition::BtfFieldInfo), + ), + self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), + ), }; hir::Expr { hir_id: expr_hir_id, kind, span } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1a351cc1420f3..b8fc917795de0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -527,6 +527,7 @@ enum ImplTraitPosition { Cast, ImplSelf, OffsetOf, + BtfFieldInfo, } impl std::fmt::Display for ImplTraitPosition { @@ -553,6 +554,7 @@ impl std::fmt::Display for ImplTraitPosition { ImplTraitPosition::Cast => "cast expression types", ImplTraitPosition::ImplSelf => "impl headers", ImplTraitPosition::OffsetOf => "`offset_of!` parameters", + ImplTraitPosition::BtfFieldInfo => "BTF field info query parameters", }; write!(f, "{name}") diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index b6c22e7da9cb1..cfd1b80d5521b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -889,6 +889,24 @@ impl<'a> State<'a> { self.print_expr(expr, FixupContext::default()); self.pclose() } + ast::ExprKind::BtfFieldInfo(kind, container, fields) => { + self.word("builtin # "); + self.word(kind.as_str()); + self.popen(); + let ib = self.ibox(0); + self.print_type(container); + self.word(","); + self.space(); + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); + for &field in rest { + self.word("."); + self.print_ident(field); + } + } + self.end(ib); + self.pclose(); + } } self.ann.post(self, AnnNode::Expr(expr)); diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index d722d515582dc..72bcbe7508ecd 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -791,6 +791,9 @@ pub enum AttributeKind { /// Represents `#[automatically_derived]` AutomaticallyDerived, + /// Represents `#[btf_relocatable]`. + BtfRelocatable(Span), + /// Represents the trace attribute of `#[cfg_attr]` CfgAttrTrace(ThinVec<(CfgEntry, Span)>), diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 6f05f763f2ada..5187f541753a4 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -22,6 +22,7 @@ impl AttributeKind { AllowInternalUnstable(..) => Yes, AlwaysGca => Yes, AutomaticallyDerived => Yes, + BtfRelocatable(..) => Yes, CfgAttrTrace(..) => Yes, CfgTrace(..) => Yes, CfiEncoding { .. } => Yes, diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index c1ad05dc8e4a8..a464300a9c892 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -164,6 +164,8 @@ language_item_table! { AlignOf, sym::mem_align_const, align_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); SizeOf, sym::mem_size_const, size_const, Target::AssocConst(AssocCtxt::Trait), GenericRequirement::Exact(0); OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1); + BtfPreserveAccessIndex, sym::btf_preserve_access_index, btf_preserve_access_index, Target::Fn, GenericRequirement::Exact(1); + BtfPreserveFieldInfo, sym::btf_preserve_field_info, btf_preserve_field_info, Target::Fn, GenericRequirement::Exact(0); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_attr_parsing/src/attributes/btf_relocatable.rs b/compiler/rustc_attr_parsing/src/attributes/btf_relocatable.rs new file mode 100644 index 0000000000000..e669dac5392a6 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/btf_relocatable.rs @@ -0,0 +1,22 @@ +use rustc_feature::AttributeStability; +use rustc_target::spec::Arch; + +use super::prelude::*; +use crate::diagnostics::BtfRelocatableOnNonBpfArch; + +pub(crate) struct BtfRelocatableParser; + +impl NoArgsAttributeParser for BtfRelocatableParser { + const PATH: &[Symbol] = &[sym::btf_relocatable]; + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Struct), Allow(Target::Union)]); + const STABILITY: AttributeStability = unstable!(btf_relocations); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::BtfRelocatable; + + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { + // `#[btf_relocatable]` may be only applied on BPF architecture. + if cx.shared.cx.sess().target.arch != Arch::Bpf { + cx.shared.cx.dcx().emit_err(BtfRelocatableOnNonBpfArch { span: attr_span }); + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 242b4a73b06a6..05fa44b008adc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -38,6 +38,7 @@ mod prelude; pub(crate) mod allow_unstable; pub(crate) mod autodiff; pub(crate) mod body; +pub(crate) mod btf_relocatable; pub(crate) mod cfg; pub(crate) mod cfg_select; pub(crate) mod cfi_encoding; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f936f5aab8265..662be89fe5529 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,6 +24,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use crate::attributes::allow_unstable::*; use crate::attributes::autodiff::*; use crate::attributes::body::*; +use crate::attributes::btf_relocatable::*; use crate::attributes::cfi_encoding::*; use crate::attributes::codegen_attrs::*; use crate::attributes::confusables::*; @@ -261,6 +262,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index e5f49690f71dc..d2c310da5ad03 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2056,3 +2056,10 @@ pub(crate) struct UnusedDuplicate { )] pub warning: bool, } + +#[derive(Diagnostic)] +#[diag("the `btf_relocatable` attribute can only be used on BPF architecture")] +pub(crate) struct BtfRelocatableOnNonBpfArch { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 80ea2e3d877fc..88a42f8901bc4 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -324,7 +324,8 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Become(_) | ExprKind::Yield(_) | ExprKind::DirectConstArg(_) - | ExprKind::UnsafeBinderCast(..) => {} + | ExprKind::UnsafeBinderCast(..) + | ExprKind::BtfFieldInfo(..) => {} } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 9d4602e49968d..b59aa721c689d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,7 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi::{self as abi, Align, CanonAbi, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, CanonAbi, FieldIdx, Size, VariantIdx, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -16,6 +16,7 @@ use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::attrs::{AttributeKind, UnrollAttr}; use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -34,6 +35,7 @@ use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common::Funclet; use crate::context::{CodegenCx, FullCx, GenericCx, SCx}; +use crate::debuginfo::metadata::type_di_node; use crate::llvm::{ self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, FromGeneric, GEPNoWrapFlags, Metadata, TRUE, ToLlvmBool, Type, Value, @@ -1582,6 +1584,78 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let cold_inline = llvm::AttributeKind::Cold.create_attr(self.llcx); attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[cold_inline]); } + + fn btf_preserve_access_index( + &mut self, + base: &'ll Value, + container_ty: Ty<'tcx>, + variant: VariantIdx, + field: FieldIdx, + ) -> &'ll Value { + fn llvm_struct_field_index<'ll, 'tcx>( + bx: &Builder<'_, 'll, 'tcx>, + layout: TyAndLayout<'tcx>, + field_index: usize, + ) -> usize { + let mut llvm_index = 0; + let mut offset = Size::ZERO; + + for i in layout.fields.index_by_increasing_offset() { + let target_offset = layout.fields.offset(i as usize); + if target_offset != offset { + llvm_index += 1; + } + if i as usize == field_index { + return llvm_index; + } + + let field = layout.field(bx.cx(), i); + llvm_index += 1; + offset = target_offset + field.size; + } + + bug!("field index {field_index} not found in layout {layout:#?}") + } + + let layout_cx = ty::layout::LayoutCx::new(self.tcx, self.typing_env()); + let layout = self.layout_of(container_ty).for_variant(&layout_cx, variant); + match container_ty.kind() { + ty::Adt(adt, _) if adt.is_union() => { + let dbg_info: &'ll Metadata = type_di_node(self.cx, container_ty); + unsafe { + llvm::LLVMRustBuildPreserveUnionAccessIndex( + self.llbuilder, + base, + field.index() as c_uint, + Some(dbg_info), + ) + } + } + ty::Adt(..) | ty::Tuple(..) => { + let llvm_index = llvm_struct_field_index(self, layout, field.index()); + let dbg_info: &'ll Metadata = type_di_node(self.cx, container_ty); + unsafe { + llvm::LLVMRustBuildPreserveStructAccessIndex( + self.llbuilder, + self.cx().backend_type(layout), + base, + llvm_index as c_uint, + field.index() as c_uint, + Some(dbg_info), + ) + } + } + _ => bug!("BTF field info query has unsupported container type: {container_ty:?}"), + } + } + + fn btf_preserve_field_info(&mut self, field: &'ll Value, kind: u64) -> &'ll Value { + self.call_intrinsic( + "llvm.bpf.preserve.field.info", + &[self.val_ty(field)], + &[field, self.const_u64(kind)], + ) + } } impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d1cdf7bada0b1..35679a385e523 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1711,6 +1711,22 @@ unsafe extern "C" { NumBundles: c_uint, Name: *const c_char, ) -> &'a Value; + + // BTF relocations + pub(crate) fn LLVMRustBuildPreserveUnionAccessIndex<'a>( + B: &Builder<'a>, + Base: &'a Value, + FieldIndex: c_uint, + DbgInfo: Option<&'a Metadata>, + ) -> &'a Value; + pub(crate) fn LLVMRustBuildPreserveStructAccessIndex<'a>( + B: &Builder<'a>, + ElTy: &'a Type, + Base: &'a Value, + Index: c_uint, + FieldIndex: c_uint, + DbgInfo: Option<&'a Metadata>, + ) -> &'a Value; } // FFI bindings for `DIBuilder` functions in the LLVM-C API. diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 5964f5b858ac0..bd91c4cea4505 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,4 +1,4 @@ -use rustc_abi::{Align, FieldIdx, WrappingRange}; +use rustc_abi::{Align, FieldIdx, VariantIdx, WrappingRange}; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -174,6 +174,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta, span); OperandValue::Immediate(llalign) } + sym::btf_preserve_access_index => { + let base = args[0].immediate(); + let Some(variant) = bx.const_to_opt_uint(args[1].immediate()) else { + span_bug!(span, "BTF variant index is not a constant") + }; + let Some(field) = bx.const_to_opt_uint(args[2].immediate()) else { + span_bug!(span, "BTF field index is not a constant") + }; + OperandValue::Immediate(bx.btf_preserve_access_index( + base, + fn_args.type_at(0), + VariantIdx::from_u32(variant as u32), + FieldIdx::from_u32(field as u32), + )) + } + sym::btf_preserve_field_info => { + let field = args[0].immediate(); + let Some(kind) = bx.const_to_opt_uint(args[1].immediate()) else { + span_bug!(span, "BTF field information kind is not a constant") + }; + OperandValue::Immediate(bx.btf_preserve_field_info(field, kind)) + } sym::vtable_size | sym::vtable_align => { let vtable = args[0].immediate(); let idx = match name { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b7b694922bcfa..a12598151d62e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -371,6 +371,18 @@ pub trait BuilderMethods<'a, 'tcx>: fn inbounds_ptradd(&mut self, ptr: Self::Value, offset: Self::Value) -> Self::Value { self.inbounds_gep(self.cx().type_i8(), ptr, &[offset]) } + fn btf_preserve_access_index( + &mut self, + _base: Self::Value, + _container_ty: Ty<'tcx>, + _variant: rustc_abi::VariantIdx, + _field: rustc_abi::FieldIdx, + ) -> Self::Value { + self.tcx().dcx().fatal("the selected codegen backend does not support BTF relocations") + } + fn btf_preserve_field_info(&mut self, _field: Self::Value, _kind: u64) -> Self::Value { + self.tcx().dcx().fatal("the selected codegen backend does not support BTF relocations") + } fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; /// Produces the same value as [`Self::trunc`] (and defaults to that), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bb35a3281ccdc..1e2de980c6a80 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -182,6 +182,9 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // RFC 2412 sym::optimize, + // BTF CO-RE relocation support. + sym::btf_relocatable, + sym::ffi_pure, sym::ffi_const, sym::register_attribute_tool, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a7138a88ee399..6882f1beb63d7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -399,6 +399,10 @@ declare_features! ( (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Target features on bpf. (unstable, bpf_target_feature, "1.54.0", Some(150247)), + // no-tracking-issue-start + /// Allows BTF CO-RE field relocation queries. + (unstable, btf_relocations, "CURRENT_RUSTC_VERSION", Some(160616)), + // no-tracking-issue-end /// Allows defining c-variadic functions on targets where this feature has not yet /// undergone sufficient testing for stabilization. (unstable, c_variadic_experimental_arch, "1.97.0", Some(155973)), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee79680d7d1e9..e4e7e9e2b875e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -6,8 +6,8 @@ use std::ops::Not; use rustc_abi::ExternAbi; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{ - self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, - LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, + self as ast, BtfRelocKind, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, + LitIntType, LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, }; pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, @@ -2267,6 +2267,7 @@ impl Expr<'_> { | ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) | ExprKind::Use(..) + | ExprKind::BtfFieldInfo(..) | ExprKind::Err(_) => prefix_attrs_precedence(), ExprKind::DropTemps(expr, ..) => expr.precedence(has_attr), @@ -2340,7 +2341,8 @@ impl Expr<'_> { | ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::Cast(..) - | ExprKind::DropTemps(..) => false, + | ExprKind::DropTemps(..) + | ExprKind::BtfFieldInfo(..) => false, } } @@ -2393,9 +2395,11 @@ impl Expr<'_> { pub fn can_have_side_effects(&self) -> bool { match self.peel_drop_temps().kind { - ExprKind::Path(_) | ExprKind::Lit(_) | ExprKind::OffsetOf(..) | ExprKind::Use(..) => { - false - } + ExprKind::Path(_) + | ExprKind::Lit(_) + | ExprKind::OffsetOf(..) + | ExprKind::Use(..) + | ExprKind::BtfFieldInfo(..) => false, ExprKind::Type(base, _) | ExprKind::Unary(_, base) | ExprKind::Field(base, _) @@ -2696,6 +2700,16 @@ pub enum ExprKind<'hir> { /// e.g. `unsafe<'a> &'a i32` <=> `&i32`. UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>), + /// [BPF Type Format (BTF)][btf] relocation. + /// + /// [BTF][btf] is the type metadata format used by the Linux kernel and + /// eBPF tooling for relocations: the compiled program records which field + /// or array element it intended to access, and the loader rewrites the + /// bytecode to match the layout of the kernel it is about to run on. + /// + /// [btf]: https://docs.kernel.org/bpf/btf.html + BtfFieldInfo(BtfRelocKind, &'hir Ty<'hir>, &'hir [Ident]), + /// A placeholder for an expression that wasn't syntactically well formed in some way. Err(rustc_span::ErrorGuaranteed), } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9cd4b5d7d001f..559ea5e73b5d3 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -949,7 +949,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::InlineAsm(ref asm) => { try_visit!(visitor.visit_inline_asm(asm, *hir_id)); } - ExprKind::OffsetOf(ref container, ref fields) => { + ExprKind::OffsetOf(ref container, ref fields) + | ExprKind::BtfFieldInfo(_, ref container, ref fields) => { try_visit!(visitor.visit_ty_unambig(container)); walk_list!(visitor, visit_ident, fields.iter().copied()); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cca93e8aef0ec..e0aa4558a4bcd 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -79,6 +79,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::black_box | sym::breakpoint | sym::bswap + | sym::btf_preserve_access_index + | sym::btf_preserve_field_info | sym::caller_location | sym::carrying_mul_add | sym::carryless_mul @@ -298,6 +300,15 @@ pub(crate) fn check_intrinsic_type( } sym::size_of_type_id => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, tcx.types.usize)), sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize), + sym::btf_preserve_access_index => ( + 1, + 0, + vec![Ty::new_imm_ptr(tcx, tcx.types.unit), tcx.types.u32, tcx.types.u32], + Ty::new_imm_ptr(tcx, tcx.types.unit), + ), + sym::btf_preserve_field_info => { + (0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit), tcx.types.u32], tcx.types.u32) + } sym::field_offset => (1, 0, vec![], tcx.types.usize), sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 6d1ae563a9fa2..8a02ce03bbff8 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1786,6 +1786,23 @@ impl<'a> State<'a> { self.word_space("yield"); self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } + hir::ExprKind::BtfFieldInfo(kind, container, fields) => { + self.word(format!("{}!(", kind.as_str())); + self.print_type(container); + self.word(","); + self.space(); + + if let Some((&first, rest)) = fields.split_first() { + self.print_ident(first); + + for &field in rest { + self.word("."); + self.print_ident(field); + } + } + + self.word(")"); + } hir::ExprKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 06dbd06eb191b..a102678a9b0b7 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -5,8 +5,9 @@ //! //! See [`rustc_hir_analysis::check`] for more context on type checking in general. -use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_ast as ast; +use rustc_ast::BtfRelocKind; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::ThinVec; @@ -29,10 +30,12 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; +use rustc_session::config::DebugInfo; use rustc_session::diagnostics::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::{Ident, Span, Spanned, Symbol, kw, sym}; +use rustc_target::spec::Arch; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use tracing::{debug, instrument, trace}; @@ -399,6 +402,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::UnsafeBinderCast(kind, inner_expr, ty) => { self.check_expr_unsafe_binder_cast(expr.span, kind, inner_expr, ty, expected) } + ExprKind::BtfFieldInfo(kind, container, fields) => { + self.check_expr_btf_field_info(kind, container, fields, expr) + } ExprKind::Err(guar) => Ty::new_error(tcx, guar), } } @@ -2798,6 +2804,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if let Some((idx, field)) = self.find_adt_field(*base_def, ident) { + if find_attr!(self.tcx, base_def.did(), BtfRelocatable(..)) { + let mut err = self.dcx().struct_span_err( + expr.span, + "cannot access fields of a `btf_relocatable` type directly", + ); + err.span_label( + ident.span, + "direct field access is forbidden for BTF-relocatable types", + ); + return Ty::new_error(self.tcx, err.emit()); + } + self.write_field_index(expr.hir_id, idx); let adjustments = self.adjust_steps(&autoderef); @@ -3821,6 +3839,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { + let field_indices = self.resolve_field_path(container, fields, false, expr); + self.typeck_results.borrow_mut().offset_of_data_mut().insert(expr.hir_id, field_indices); + self.tcx.types.usize + } + + fn check_expr_btf_field_info( + &self, + kind: BtfRelocKind, + container: &'tcx hir::Ty<'tcx>, + fields: &[Ident], + expr: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + if self.tcx.sess.target.arch != Arch::Bpf { + self.dcx() + .struct_span_err( + expr.span, + "BTF field relocation queries are only supported for BPF targets", + ) + .emit(); + } else if self.tcx.sess.opts.debuginfo == DebugInfo::None { + let mut err = self + .dcx() + .struct_span_err(expr.span, "BTF field relocation queries require debug info"); + err.help("compile with `-C debuginfo=2`"); + err.emit(); + } + let field_indices = self.resolve_field_path(container, fields, true, expr); + self.typeck_results + .borrow_mut() + .btf_field_info_data_mut() + .insert(expr.hir_id, field_indices); + match kind { + BtfRelocKind::Exists => self.tcx.types.bool, + BtfRelocKind::ByteOffset | BtfRelocKind::ByteSize => self.tcx.types.usize, + } + } + + fn resolve_field_path( + &self, + container: &'tcx hir::Ty<'tcx>, + fields: &[Ident], + allow_btf_relocatable: bool, + expr: &'tcx hir::Expr<'tcx>, + ) -> Vec<(Ty<'tcx>, VariantIdx, FieldIdx)> { let mut current_container = self.lower_ty(container).normalized; let mut field_indices = Vec::with_capacity(fields.len()); let mut fields = fields.into_iter(); @@ -3917,6 +3979,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } ty::Adt(container_def, args) => { + if find_attr!(self.tcx, container_def.did(), BtfRelocatable(..)) + && !allow_btf_relocatable + { + let mut err = self.dcx().struct_span_err( + expr.span, + "cannot use `offset_of!` with a `btf_relocatable` type", + ); + err.span_label(field.span, "this field requires BTF relocation"); + err.emit(); + break; + } + let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope( field, container_def.did(), @@ -3984,8 +4058,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break; } - self.typeck_results.borrow_mut().offset_of_data_mut().insert(expr.hir_id, field_indices); - - self.tcx.types.usize + field_indices } } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index df5f0899b7fce..8249ba4eccb14 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -507,6 +507,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | hir::ExprKind::Lit(..) | hir::ExprKind::ConstBlock(..) | hir::ExprKind::OffsetOf(..) + | hir::ExprKind::BtfFieldInfo(..) | hir::ExprKind::Err(_) => {} hir::ExprKind::Loop(blk, ..) => { @@ -1387,6 +1388,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | hir::ExprKind::InlineAsm(..) | hir::ExprKind::OffsetOf(..) | hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, ..) + | hir::ExprKind::BtfFieldInfo(..) | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)), } } diff --git a/compiler/rustc_hir_typeck/src/naked_functions.rs b/compiler/rustc_hir_typeck/src/naked_functions.rs index ddeec25acad7a..effff0c2b911d 100644 --- a/compiler/rustc_hir_typeck/src/naked_functions.rs +++ b/compiler/rustc_hir_typeck/src/naked_functions.rs @@ -160,7 +160,8 @@ impl CheckInlineAssembly { | ExprKind::Become(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) - | ExprKind::Yield(..) => { + | ExprKind::Yield(..) + | ExprKind::BtfFieldInfo(..) => { self.items.push((ItemKind::NonAsm, span)); } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f4fe38924351a..f7643f236fc70 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -78,6 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_transmutes(); wbcx.visit_offloads(); wbcx.visit_offset_of_container_types(); + wbcx.visit_btf_field_info_container_types(); wbcx.visit_potentially_region_dependent_goals(); let used_trait_imports = @@ -803,6 +804,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_btf_field_info_container_types(&mut self) { + let fcx_typeck_results = self.fcx.typeck_results.borrow(); + assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); + let common_hir_owner = fcx_typeck_results.hir_owner; + + for (local_id, indices) in fcx_typeck_results.btf_field_info_data().items_in_stable_order() + { + let hir_id = HirId { owner: common_hir_owner, local_id }; + let indices = indices + .iter() + .map(|&(ty, variant, field)| (self.resolve(ty, &hir_id), variant, field)) + .collect(); + self.typeck_results.btf_field_info_data_mut().insert(hir_id, indices); + } + } + fn visit_potentially_region_dependent_goals(&mut self) { let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); if self.fcx.tainted_by_errors().is_none() { diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index c272dd4496984..8b6e4e383a931 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -319,7 +319,10 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Yield(..) => false, // Compiler-magic macros - ExprKind::AddrOf(..) | ExprKind::OffsetOf(..) | ExprKind::InlineAsm(..) => false, + ExprKind::AddrOf(..) + | ExprKind::OffsetOf(..) + | ExprKind::InlineAsm(..) + | ExprKind::BtfFieldInfo(..) => false, // We are not interested in these ExprKind::Cast(..) diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 161b5bdb952d3..81ab98709ce91 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1784,6 +1784,22 @@ extern "C" LLVMValueRef LLVMRustConstPtrAuth(LLVMValueRef Ptr, uint32_t Key, #endif } +extern "C" LLVMValueRef +LLVMRustBuildPreserveUnionAccessIndex(LLVMBuilderRef B, LLVMValueRef Base, + unsigned FieldIndex, + LLVMMetadataRef DbgInfo) { + return wrap(unwrap(B)->CreatePreserveUnionAccessIndex( + unwrap(Base), FieldIndex, unwrapDI(DbgInfo))); +} + +extern "C" LLVMValueRef LLVMRustBuildPreserveStructAccessIndex( + LLVMBuilderRef B, LLVMTypeRef ElTy, LLVMValueRef Base, unsigned Index, + unsigned FieldIndex, LLVMMetadataRef DbgInfo) { + return wrap(unwrap(B)->CreatePreserveStructAccessIndex( + unwrap(ElTy), unwrap(Base), Index, FieldIndex, + unwrapDI(DbgInfo))); +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 15a24ffea6700..c3c0b977c21c3 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -297,6 +297,7 @@ impl<'tcx> TyCtxt<'tcx> { | ExprKind::Path(_) | ExprKind::Continue(_) | ExprKind::OffsetOf(_, _) + | ExprKind::BtfFieldInfo(..) | ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind), } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index eaccfc552873f..ba88edee14ea7 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -809,7 +809,6 @@ macro_rules! make_mir_visitor { self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 45fe03499ed6f..974e8f08d0a92 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -228,6 +228,9 @@ pub struct TypeckResults<'tcx> { /// Container types and field indices of `offset_of!` expressions offset_of_data: ItemLocalMap, VariantIdx, FieldIdx)>>, + + /// Container types and field indices of BTF field info expressions. + btf_field_info_data: ItemLocalMap, VariantIdx, FieldIdx)>>, } impl<'tcx> TypeckResults<'tcx> { @@ -261,6 +264,7 @@ impl<'tcx> TypeckResults<'tcx> { transmutes_to_check: Default::default(), offloads_to_check: Default::default(), offset_of_data: Default::default(), + btf_field_info_data: Default::default(), } } @@ -596,6 +600,18 @@ impl<'tcx> TypeckResults<'tcx> { ) -> LocalTableInContextMut<'_, Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data } } + + pub fn btf_field_info_data( + &self, + ) -> LocalTableInContext<'_, Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.btf_field_info_data } + } + + pub fn btf_field_info_data_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.btf_field_info_data } + } } /// A resolved splatted function call. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 070ce5a3d6e24..ccae8181b1057 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,6 +1,6 @@ use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, Size, VariantIdx}; -use rustc_ast::UnsafeBinderCastKind; +use rustc_ast::{BtfRelocKind, UnsafeBinderCastKind}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::lang_items::LangItem; @@ -1184,6 +1184,100 @@ impl<'tcx> ThirBuildCx<'tcx> { hir::ExprKind::DropTemps(source) => { ExprKind::ValueExpr { source: self.mirror_expr(source) } } + + hir::ExprKind::BtfFieldInfo(kind, _, _) => { + let indices = self.typeck_results.btf_field_info_data().get(expr.hir_id).unwrap(); + if indices.is_empty() { + return match kind { + BtfRelocKind::ByteOffset | BtfRelocKind::ByteSize => mk_expr( + ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_target_usize(0u128, tcx).unwrap(), + user_ty: None, + }, + tcx.types.usize, + ), + BtfRelocKind::Exists => mk_expr( + ExprKind::NonHirLiteral { lit: false.into(), user_ty: None }, + tcx.types.bool, + ), + }; + } + + let preserve_access_index = + tcx.require_lang_item(LangItem::BtfPreserveAccessIndex, expr.span); + let preserve_field_info = + tcx.require_lang_item(LangItem::BtfPreserveFieldInfo, expr.span); + let unit_ptr_ty = Ty::new_imm_ptr(tcx, tcx.types.unit); + let mk_u32_kind = |value: u32| ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_uint(value, Size::from_bits(32)).unwrap(), + user_ty: None, + }; + + // The access-index intrinsics use an opaque pointer only to carry the field path + // from one call to the next. No memory is accessed through this null pointer. + let zero = self.thir.exprs.push(mk_expr( + ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_target_usize(0u128, tcx).unwrap(), + user_ty: None, + }, + tcx.types.usize, + )); + let mut field_ptr = + self.thir.exprs.push(mk_expr(ExprKind::Cast { source: zero }, unit_ptr_ty)); + + for &(container_ty, variant, field) in indices { + let fun_ty = tcx + .type_of(preserve_access_index) + .instantiate(tcx, &[container_ty.into()]) + .skip_norm_wip(); + let fun = self + .thir + .exprs + .push(mk_expr(ExprKind::ZstLiteral { user_ty: None }, fun_ty)); + let variant = + self.thir.exprs.push(mk_expr(mk_u32_kind(variant.as_u32()), tcx.types.u32)); + let field = + self.thir.exprs.push(mk_expr(mk_u32_kind(field.as_u32()), tcx.types.u32)); + field_ptr = self.thir.exprs.push(mk_expr( + ExprKind::Call { + ty: fun_ty, + fun, + args: Box::new([field_ptr, variant, field]), + from_hir_call: false, + fn_span: expr.span, + }, + unit_ptr_ty, + )); + } + + let fun_ty = + tcx.type_of(preserve_field_info).instantiate_identity().skip_norm_wip(); + let fun = + self.thir.exprs.push(mk_expr(ExprKind::ZstLiteral { user_ty: None }, fun_ty)); + let info_kind = + self.thir.exprs.push(mk_expr(mk_u32_kind(kind.code()), tcx.types.u32)); + let info = self.thir.exprs.push(mk_expr( + ExprKind::Call { + ty: fun_ty, + fun, + args: Box::new([field_ptr, info_kind]), + from_hir_call: false, + fn_span: expr.span, + }, + tcx.types.u32, + )); + + match kind { + BtfRelocKind::ByteOffset | BtfRelocKind::ByteSize => { + ExprKind::Cast { source: info } + } + BtfRelocKind::Exists => { + let zero = self.thir.exprs.push(mk_expr(mk_u32_kind(0), tcx.types.u32)); + ExprKind::Binary { op: BinOp::Ne, lhs: info, rhs: zero } + } + } + } + hir::ExprKind::Array(fields) => ExprKind::Array { fields: self.mirror_exprs(fields) }, hir::ExprKind::Tup(fields) => ExprKind::Tuple { fields: self.mirror_exprs(fields) }, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 12de4957e99c2..7d195588d8c16 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -13,9 +13,9 @@ use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutine use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, - BlockCheckMode, CaptureBy, ClosureBinder, CoroutineKind, DUMMY_NODE_ID, Expr, ExprField, - ExprKind, FnDecl, FnRetTy, ForLoop, Guard, Label, MacCall, MetaItemLit, Movability, Param, - RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, + BlockCheckMode, BtfRelocKind, CaptureBy, ClosureBinder, CoroutineKind, DUMMY_NODE_ID, Expr, + ExprField, ExprKind, FnDecl, FnRetTy, ForLoop, Guard, Label, MacCall, MetaItemLit, Movability, + Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; @@ -2013,6 +2013,21 @@ impl<'a> Parser<'a> { sym::unwrap_binder => { Some(this.parse_expr_unsafe_binder_cast(lo, UnsafeBinderCastKind::Unwrap)?) } + sym::btf_field_byte_offset => Some(this.parse_expr_btf_field_info( + lo, + ident.as_str(), + BtfRelocKind::ByteOffset, + )?), + sym::btf_field_byte_size => Some(this.parse_expr_btf_field_info( + lo, + ident.as_str(), + BtfRelocKind::ByteSize, + )?), + sym::btf_field_exists => Some(this.parse_expr_btf_field_info( + lo, + ident.as_str(), + BtfRelocKind::Exists, + )?), _ => None, }) }) @@ -2098,6 +2113,37 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::UnsafeBinderCast(kind, expr, ty))) } + pub(crate) fn parse_expr_btf_field_info( + &mut self, + lo: Span, + name: &str, + kind: BtfRelocKind, + ) -> PResult<'a, Box> { + let container = self.parse_ty()?; + self.expect(exp!(Comma))?; + + let fields = self.parse_floating_field_access()?; + let trailing_comma = self.eat_noexpect(&TokenKind::Comma); + + if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { + if trailing_comma { + e.note(format!("unexpected third argument to {name}")); + } else { + e.note(format!("{name} expects dot-separated field names")); + } + } + + // Eat tokens until the macro call ends. + if self.may_recover() { + while !self.token.kind.is_close_delim_or_eof() { + self.bump(); + } + } + + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::BtfFieldInfo(kind, container, fields))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -4495,6 +4541,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::FormatArgs(_) | ExprKind::Err(_) | ExprKind::DirectConstArg(_) + | ExprKind::BtfFieldInfo(..) | ExprKind::Dummy => { // These would forbid any let expressions they contain already. } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 597c920976ede..033304f518b82 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -243,6 +243,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::AllowInternalUnstable(..) => (), AttributeKind::AlwaysGca => (), AttributeKind::AutomaticallyDerived => (), + AttributeKind::BtfRelocatable(..) => (), AttributeKind::CfgAttrTrace(..) => (), AttributeKind::CfgTrace(..) => (), AttributeKind::CfiEncoding { .. } => (), diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f2a7cb6e46fca..172c2aece485a 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -380,6 +380,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Repeat, Yield, UnsafeBinderCast, + BtfFieldInfo, Err ] ); @@ -661,7 +662,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { If, While, ForLoop, Loop, Match, Closure, Block, Await, Move, Use, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, - Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy, DirectConstArg + Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy, DirectConstArg, BtfFieldInfo ] ); ast_visit::walk_expr(self, e) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4c1b9c78b7963..d192851fedb94 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -574,6 +574,13 @@ symbols! { breg, bridge, bswap, + btf_field_byte_offset, + btf_field_byte_size, + btf_field_exists, + btf_preserve_access_index, + btf_preserve_field_info, + btf_relocatable, + btf_relocations, built, builtin_syntax, bundle, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 6db234fd886ca..b8ba9016f7828 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -208,7 +208,6 @@ fn recurse_build<'tcx>( ExprKind::InlineAsm { .. } => { error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? } - // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 2b7eb0b9afbcf..24dd18bc9e50b 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -19,6 +19,7 @@ // ignore-tidy-file-linelength #![feature( + allow_internal_unstable, no_core, intrinsics, lang_items, @@ -357,6 +358,20 @@ trait Drop { #[rustc_intrinsic] pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); +#[rustc_nounwind] +#[rustc_intrinsic] +#[lang = "btf_preserve_access_index"] +pub fn btf_preserve_access_index( + base: *const (), + variant: u32, + field: u32, +) -> *const (); + +#[rustc_nounwind] +#[rustc_intrinsic] +#[lang = "btf_preserve_field_info"] +pub fn btf_preserve_field_info(field: *const (), kind: u32) -> u32; + pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] @@ -368,6 +383,12 @@ pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] pub const fn align_of() -> usize; + + #[allow_internal_unstable(builtin_syntax)] + pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { + // The `{}` is for better error messages + {builtin # offset_of($Container, $($fields)+)} + } } pub mod ptr { diff --git a/tests/codegen-llvm/btf-field-info-minicore.rs b/tests/codegen-llvm/btf-field-info-minicore.rs new file mode 100644 index 0000000000000..d49d7833ebf75 --- /dev/null +++ b/tests/codegen-llvm/btf-field-info-minicore.rs @@ -0,0 +1,103 @@ +//@ add-minicore +//@ needs-llvm-components: bpf +//@ compile-flags: --target bpfel-unknown-none -Cdebuginfo=2 + +#![feature(allow_internal_unstable, btf_relocations, decl_macro, no_core)] +#![no_core] +#![no_std] +#![no_main] + +extern crate minicore; +use minicore::*; + +#[allow_internal_unstable(builtin_syntax)] +pub macro field_byte_offset($Container:ty, $($fields:expr)+ $(,)?) {{ + if builtin # btf_field_exists($Container, $($fields)+) { + ::minicore::Option::Some(builtin # btf_field_byte_offset($Container, $($fields)+)) + } else { + ::minicore::Option::None + } +}} + +#[allow_internal_unstable(builtin_syntax)] +pub macro field_byte_size($Container:ty, $($fields:expr)+ $(,)?) {{ + if builtin # btf_field_exists($Container, $($fields)+) { + ::minicore::Option::Some(builtin # btf_field_byte_size($Container, $($fields)+)) + } else { + ::minicore::Option::None + } +}} + +#[btf_relocatable] +#[repr(C)] +pub struct Inner { + pub x: u32, + pub y: u64, +} + +#[btf_relocatable] +#[repr(C)] +pub union Payload { + pub word: u64, + pub half: u32, +} + +#[btf_relocatable] +#[repr(C)] +pub struct Outer { + pub pad: u32, + pub inner: Inner, + pub payload: Payload, +} + +// Each `Option` query emits a `BPF_CORE_FIELD_EXISTS` relocation (kind 2), followed by either +// `BPF_CORE_FIELD_BYTE_OFFSET` (kind 0) or `BPF_CORE_FIELD_BYTE_SIZE` (kind 1). The value between +// the second and third colons is the compile-time fallback. +// +// CHECK-DAG: @"llvm.Outer:2:1$0:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:0:8$0:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:1:16$0:1" = external global i32, !llvm.preserve.access.index +// +// CHECK-DAG: @"llvm.Outer:2:1$0:1:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:0:16$0:1:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:1:8$0:1:1" = external global i32, !llvm.preserve.access.index +// +// CHECK-DAG: @"llvm.Outer:2:1$0:2:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:0:24$0:2:1" = external global i32, !llvm.preserve.access.index +// CHECK-DAG: @"llvm.Outer:1:4$0:2:1" = external global i32, !llvm.preserve.access.index + +// CHECK-LABEL: define{{.*}} @field_offset( +#[unsafe(no_mangle)] +pub fn field_offset() -> Option { + field_byte_offset!(Outer, inner) +} + +// CHECK-LABEL: define{{.*}} @field_size( +#[unsafe(no_mangle)] +pub fn field_size() -> Option { + field_byte_size!(Outer, inner) +} + +// CHECK-LABEL: define{{.*}} @nested_field_offset( +#[unsafe(no_mangle)] +pub fn nested_field_offset() -> Option { + field_byte_offset!(Outer, inner.y) +} + +// CHECK-LABEL: define{{.*}} @nested_field_size( +#[unsafe(no_mangle)] +pub fn nested_field_size() -> Option { + field_byte_size!(Outer, inner.y) +} + +// CHECK-LABEL: define{{.*}} @union_field_offset( +#[unsafe(no_mangle)] +pub fn union_field_offset() -> Option { + field_byte_offset!(Outer, payload.half) +} + +// CHECK-LABEL: define{{.*}} @union_field_size( +#[unsafe(no_mangle)] +pub fn union_field_size() -> Option { + field_byte_size!(Outer, payload.half) +} diff --git a/tests/ui/README.md b/tests/ui/README.md index 8df2769996f41..c807109931d00 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -185,6 +185,13 @@ See: - [`std::box::Boxed`](https://doc.rust-lang.org/std/boxed/struct.Box.html) +## `tests/ui/btf-relocations/`: BTF relocations + +Tests for [Compile Once, Run Everywhere (CO-RE)][co-re] relocations based on the [BPF Type Format (BTF)][btf]. + +[co-re]: https://nakryiko.com/posts/bpf-portability-and-co-re/ +[btf]: https://docs.kernel.org/bpf/btf.html + ## `tests/ui/builtin-superkinds/`: Built-in Trait Hierarchy Tests Tests for built-in trait hierarchy (Send, Sync, Sized, etc.) and their supertrait relationships. E.g. auto traits and marker trait constraints. diff --git a/tests/ui/btf-relocations/attribute-arch-check.rs b/tests/ui/btf-relocations/attribute-arch-check.rs new file mode 100644 index 0000000000000..4df06da0e8095 --- /dev/null +++ b/tests/ui/btf-relocations/attribute-arch-check.rs @@ -0,0 +1,48 @@ +//@ add-minicore +//@ needs-llvm-components: x86 +//@ compile-flags: --target x86_64-unknown-none + +#![feature(btf_relocations)] +#![feature(no_core)] +#![no_core] + +extern crate minicore; + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +struct ValidStructInner { + field: u32, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +struct ValidStruct { + field: u32, + inner: ValidStructInner, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +union ValidUnion { + word: u64, + half: u32, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +//~| ERROR the `btf_relocatable` attribute cannot be used on enums +enum InvalidEnum { + A, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +//~| ERROR the `btf_relocatable` attribute cannot be used on functions +fn invalid_function() {} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute can only be used on BPF architecture +//~| ERROR the `btf_relocatable` attribute cannot be used on traits +trait InvalidTrait {} + +fn main() {} diff --git a/tests/ui/btf-relocations/attribute-arch-check.stderr b/tests/ui/btf-relocations/attribute-arch-check.stderr new file mode 100644 index 0000000000000..c815cac8a1397 --- /dev/null +++ b/tests/ui/btf-relocations/attribute-arch-check.stderr @@ -0,0 +1,62 @@ +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:11:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:17:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:24:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: the `btf_relocatable` attribute cannot be used on enums + --> $DIR/attribute-arch-check.rs:31:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can be applied to structs and unions + +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:31:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: the `btf_relocatable` attribute cannot be used on functions + --> $DIR/attribute-arch-check.rs:38:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can only be applied to data types + +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:38:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: the `btf_relocatable` attribute cannot be used on traits + --> $DIR/attribute-arch-check.rs:43:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can only be applied to data types + +error: the `btf_relocatable` attribute can only be used on BPF architecture + --> $DIR/attribute-arch-check.rs:43:1 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui/btf-relocations/attribute.rs b/tests/ui/btf-relocations/attribute.rs new file mode 100644 index 0000000000000..157ff8a7fa4b0 --- /dev/null +++ b/tests/ui/btf-relocations/attribute.rs @@ -0,0 +1,42 @@ +//@ add-minicore +//@ needs-llvm-components: bpf +//@ compile-flags: --target bpfel-unknown-none + +#![feature(btf_relocations)] +#![feature(no_core)] +#![no_core] + +extern crate minicore; + +#[btf_relocatable] +struct ValidStructInner { + field: u32, +} + +#[btf_relocatable] +struct ValidStruct { + field: u32, + inner: ValidStructInner, +} + +#[btf_relocatable] +union ValidUnion { + word: u64, + half: u32, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute cannot be used on enums +enum InvalidEnum { + A, +} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute cannot be used on functions +fn invalid_function() {} + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute cannot be used on traits +trait InvalidTrait {} + +fn main() {} diff --git a/tests/ui/btf-relocations/attribute.stderr b/tests/ui/btf-relocations/attribute.stderr new file mode 100644 index 0000000000000..6e0e411594073 --- /dev/null +++ b/tests/ui/btf-relocations/attribute.stderr @@ -0,0 +1,26 @@ +error: the `btf_relocatable` attribute cannot be used on enums + --> $DIR/attribute.rs:28:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can be applied to structs and unions + +error: the `btf_relocatable` attribute cannot be used on functions + --> $DIR/attribute.rs:34:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can only be applied to data types + +error: the `btf_relocatable` attribute cannot be used on traits + --> $DIR/attribute.rs:38:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: the `btf_relocatable` attribute can only be applied to data types + +error: aborting due to 3 previous errors + diff --git a/tests/ui/btf-relocations/field-access.rs b/tests/ui/btf-relocations/field-access.rs new file mode 100644 index 0000000000000..4e9d9087efa7c --- /dev/null +++ b/tests/ui/btf-relocations/field-access.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ needs-llvm-components: bpf +//@ compile-flags: --target bpfel-unknown-none + +#![feature(btf_relocations)] +#![feature(no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[btf_relocatable] +#[repr(C)] +struct Inner { + value: u32, +} + +#[btf_relocatable] +#[repr(C)] +struct Outer { + inner: Inner, +} + +fn direct(inner: &Inner) -> u32 { + inner.value + //~^ ERROR cannot access fields of a `btf_relocatable` type directly +} + +fn nested(outer: &Outer) -> u32 { + outer.inner.value + //~^ ERROR cannot access fields of a `btf_relocatable` type directly +} + +fn offset() -> usize { + mem::offset_of!(Inner, value) + //~^ ERROR cannot use `offset_of!` with a `btf_relocatable` type +} + +fn nested_offset() -> usize { + mem::offset_of!(Outer, inner.value) + //~^ ERROR cannot use `offset_of!` with a `btf_relocatable` type +} + +fn main() {} diff --git a/tests/ui/btf-relocations/field-access.stderr b/tests/ui/btf-relocations/field-access.stderr new file mode 100644 index 0000000000000..a00088e96a74e --- /dev/null +++ b/tests/ui/btf-relocations/field-access.stderr @@ -0,0 +1,38 @@ +error: cannot access fields of a `btf_relocatable` type directly + --> $DIR/field-access.rs:25:5 + | +LL | inner.value + | ^^^^^^----- + | | + | direct field access is forbidden for BTF-relocatable types + +error: cannot access fields of a `btf_relocatable` type directly + --> $DIR/field-access.rs:30:5 + | +LL | outer.inner.value + | ^^^^^^----- + | | + | direct field access is forbidden for BTF-relocatable types + +error: cannot use `offset_of!` with a `btf_relocatable` type + --> $DIR/field-access.rs:35:5 + | +LL | mem::offset_of!(Inner, value) + | ^^^^^^^^^^^^^^^^^^^^^^^-----^ + | | + | this field requires BTF relocation + | + = note: this error originates in the macro `mem::offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: cannot use `offset_of!` with a `btf_relocatable` type + --> $DIR/field-access.rs:40:5 + | +LL | mem::offset_of!(Outer, inner.value) + | ^^^^^^^^^^^^^^^^^^^^^^^-----^^^^^^^ + | | + | this field requires BTF relocation + | + = note: this error originates in the macro `mem::offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/feature-gates/feature-gate-btf-relocations.rs b/tests/ui/feature-gates/feature-gate-btf-relocations.rs new file mode 100644 index 0000000000000..f61fb8a07ea1c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-btf-relocations.rs @@ -0,0 +1,16 @@ +//@ add-minicore +//@ needs-llvm-components: bpf +//@ compile-flags: --target bpfel-unknown-none + +#![feature(no_core)] +#![no_core] + +extern crate minicore; + +#[btf_relocatable] +//~^ ERROR the `btf_relocatable` attribute is an experimental feature +struct KernelType { + field: u32, +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-btf-relocations.stderr b/tests/ui/feature-gates/feature-gate-btf-relocations.stderr new file mode 100644 index 0000000000000..c1de395496515 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-btf-relocations.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `btf_relocatable` attribute is an experimental feature + --> $DIR/feature-gate-btf-relocations.rs:10:3 + | +LL | #[btf_relocatable] + | ^^^^^^^^^^^^^^^ + | + = help: add `#![feature(btf_relocations)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 242a91cab7e77..7ef057d3baf97 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -337,6 +337,8 @@ fn test_expr() { // ExprKind::FormatArgs: untestable because this test works pre-expansion. + // ExprKind::BtfFieldInfo: untestable because this test works pre-expansion. + // ExprKind::Err: untestable. // Ones involving attributes.