From d658986713be187363364941f49e76c09d728c9d Mon Sep 17 00:00:00 2001 From: vad Date: Tue, 4 Aug 2026 17:48:45 +0200 Subject: [PATCH 1/5] Add the `btf_relocations` feature and attribute Register the unstable `btf_relocations` feature and add the `#[btf_relocatable]` built-in attribute for structs and unions, that can be used only on BPF architecture. Preserve the attribute in crate metadata, add UI coverage for feature gating and attribute target validation. --- compiler/rustc_attr_ir/src/data_structures.rs | 3 + .../rustc_attr_ir/src/encode_cross_crate.rs | 1 + .../src/attributes/btf_relocatable.rs | 22 +++++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_attr_parsing/src/diagnostics.rs | 7 +++ compiler/rustc_feature/src/builtin_attrs.rs | 3 + compiler/rustc_feature/src/unstable.rs | 4 ++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 + tests/ui/README.md | 7 +++ .../btf-relocations/attribute-arch-check.rs | 48 ++++++++++++++ .../attribute-arch-check.stderr | 62 +++++++++++++++++++ tests/ui/btf-relocations/attribute.rs | 42 +++++++++++++ tests/ui/btf-relocations/attribute.stderr | 26 ++++++++ .../feature-gate-btf-relocations.rs | 16 +++++ .../feature-gate-btf-relocations.stderr | 12 ++++ 17 files changed, 259 insertions(+) create mode 100644 compiler/rustc_attr_parsing/src/attributes/btf_relocatable.rs create mode 100644 tests/ui/btf-relocations/attribute-arch-check.rs create mode 100644 tests/ui/btf-relocations/attribute-arch-check.stderr create mode 100644 tests/ui/btf-relocations/attribute.rs create mode 100644 tests/ui/btf-relocations/attribute.stderr create mode 100644 tests/ui/feature-gates/feature-gate-btf-relocations.rs create mode 100644 tests/ui/feature-gates/feature-gate-btf-relocations.stderr diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 81887e0176ee7..3578d0a91df6c 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -1002,6 +1002,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 6a9f37f80868a..f88eda9c388f4 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -21,6 +21,7 @@ impl AttributeKind { AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, AutomaticallyDerived => Yes, + BtfRelocatable(..) => Yes, CfgAttrTrace(..) => Yes, CfgTrace(..) => Yes, CfiEncoding { .. } => Yes, 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 73195c7b77b10..1f2d0b692d8b2 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -23,6 +23,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 9d72bdcb75ce3..e5a5113ca4fc5 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -2035,3 +2035,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_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bc6f87a2a7f17..eae9defdd0f62 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -183,6 +183,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 3a80145e2897d..f078ca438e385 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -426,6 +426,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", None), + // 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_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 44c34a2abddd1..019ebb900acb2 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -236,6 +236,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::AllowInternalUnsafe(..) => (), AttributeKind::AllowInternalUnstable(..) => (), AttributeKind::AutomaticallyDerived => (), + AttributeKind::BtfRelocatable(..) => (), AttributeKind::CfgAttrTrace(..) => (), AttributeKind::CfgTrace(..) => (), AttributeKind::CfiEncoding { .. } => (), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cacb8582ae3c6..397fb13705a1d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -553,6 +553,8 @@ symbols! { breg, bridge, bswap, + btf_relocatable, + btf_relocations, built, builtin_syntax, bundle, diff --git a/tests/ui/README.md b/tests/ui/README.md index a3617fb6b07c9..eff0b3440c2a3 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -186,6 +186,13 @@ See: - [`std::box::Boxed`](https://doc.rust-lang.org/std/boxed/struct.Box.html) - [Tracking issue for `box_patterns` feature #29641](https://github.com/rust-lang/rust/issues/29641) +## `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/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`. From f787ca4b9c5da96f96ec868f0212f35cd42d14cc Mon Sep 17 00:00:00 2001 From: vad Date: Fri, 14 Aug 2026 14:35:25 +0200 Subject: [PATCH 2/5] Reject direct field access on BTF-relocatable types Reject field projection and `offset_off!` usage on `#[btf_relocatable]` types. --- compiler/rustc_hir_typeck/src/expr.rs | 22 ++++++++++ tests/auxiliary/minicore.rs | 7 ++++ tests/ui/btf-relocations/field-access.rs | 44 ++++++++++++++++++++ tests/ui/btf-relocations/field-access.stderr | 38 +++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 tests/ui/btf-relocations/field-access.rs create mode 100644 tests/ui/btf-relocations/field-access.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index cbbd66f648eb8..2f302e35d563b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2781,6 +2781,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); @@ -3900,6 +3912,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } ty::Adt(container_def, args) => { + if find_attr!(self.tcx, container_def.did(), BtfRelocatable(..)) { + 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(), diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index e8bfdf80c98e8..c7e8c3eba8e22 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, @@ -368,6 +369,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/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 + From 15ee3409b71f9ee2492609c7ecf9bc0020520653 Mon Sep 17 00:00:00 2001 From: vad Date: Fri, 14 Aug 2026 10:11:05 +0200 Subject: [PATCH 3/5] Add the backend-neutral BTF field info pipeline Add builtin macros for requesting BTF field information from the Rust compiler's frontend perspective: * `btf_field_byte_offset` * `btf_field_byte_size` * `btf_field_exists` Parse them as `BtfFieldInfo` expressions, that carry the kind of requested information (offset, size, exists), base type and the field path. This mechanism supports nested field accesses in one query. Compile these expressions to `BtfFieldInfo` RValue. Add `btf_field_info` method to the `BuilderMethods` trait in codegen SSA, and use it for lowering the `BtfFieldInfo` RValue. Backends without BTF relocation support report an error. Support in backends will be added in follow-up changes. This change does not expose the functionality to the users. A user-facing API will also be added in a follow-up change. --- compiler/rustc_ast/src/ast.rs | 25 ++++++++ compiler/rustc_ast/src/util/classify.rs | 3 +- compiler/rustc_ast/src/visit.rs | 3 + compiler/rustc_ast_lowering/src/expr.rs | 9 +++ compiler/rustc_ast_lowering/src/lib.rs | 2 + .../rustc_ast_pretty/src/pprust/state/expr.rs | 18 ++++++ compiler/rustc_borrowck/src/lib.rs | 2 +- .../src/polonius/legacy/loan_invalidations.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 6 +- .../src/assert/context.rs | 3 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 25 ++++++++ .../rustc_codegen_ssa/src/traits/builder.rs | 8 +++ .../src/check_consts/check.rs | 9 +++ .../src/check_consts/qualifs.rs | 2 + .../src/check_consts/resolver.rs | 3 +- .../rustc_const_eval/src/interpret/step.rs | 4 ++ compiler/rustc_hir/src/hir.rs | 19 ++++-- compiler/rustc_hir/src/intravisit.rs | 3 +- compiler/rustc_hir_pretty/src/lib.rs | 17 ++++++ compiler/rustc_hir_typeck/src/expr.rs | 60 +++++++++++++++++-- .../rustc_hir_typeck/src/expr_use_visitor.rs | 2 + .../rustc_hir_typeck/src/naked_functions.rs | 3 +- compiler/rustc_hir_typeck/src/writeback.rs | 17 ++++++ compiler/rustc_lint/src/dangling.rs | 5 +- compiler/rustc_middle/src/hir/mod.rs | 1 + compiler/rustc_middle/src/mir/pretty.rs | 4 ++ compiler/rustc_middle/src/mir/statement.rs | 7 ++- compiler/rustc_middle/src/mir/syntax.rs | 51 ++++++++++++++++ compiler/rustc_middle/src/mir/visit.rs | 10 +++- compiler/rustc_middle/src/thir.rs | 8 ++- compiler/rustc_middle/src/thir/visit.rs | 1 + .../rustc_middle/src/ty/typeck_results.rs | 16 +++++ .../src/builder/expr/as_place.rs | 3 +- .../src/builder/expr/as_rvalue.rs | 3 + .../src/builder/expr/category.rs | 3 +- .../rustc_mir_build/src/builder/expr/into.rs | 3 +- .../rustc_mir_build/src/check_unsafety.rs | 3 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 32 +++++++++- .../src/thir/pattern/check_match.rs | 3 +- compiler/rustc_mir_build/src/thir/print.rs | 7 +++ .../src/impls/borrowed_locals.rs | 3 +- .../src/move_paths/builder.rs | 3 +- .../src/dataflow_const_prop.rs | 3 +- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../src/known_panics_lint.rs | 5 +- compiler/rustc_mir_transform/src/lint.rs | 3 +- .../rustc_mir_transform/src/promote_consts.rs | 2 + compiler/rustc_mir_transform/src/validate.rs | 6 ++ compiler/rustc_parse/src/parser/expr.rs | 53 +++++++++++++++- compiler/rustc_passes/src/input_stats.rs | 3 +- .../src/unstable/convert/stable/mir.rs | 3 + compiler/rustc_span/src/symbol.rs | 3 + compiler/rustc_ty_utils/src/consts.rs | 7 +++ tests/ui/macros/stringify.rs | 2 + 54 files changed, 461 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ee6150322a442..0e1640b769a6a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1621,6 +1621,7 @@ impl Expr { | ExprKind::While(..) | ExprKind::Yield(YieldKind::Postfix(..)) | ExprKind::DirectConstArg(..) + | ExprKind::BtfFieldInfo(..) | ExprKind::Err(_) | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } @@ -1920,6 +1921,9 @@ pub enum ExprKind { /// An mGCA `direct_const_arg!()` expression. DirectConstArg(Box), + /// A BTF field metadata query. + BtfFieldInfo(BtfFieldInfoKind, Box, ThinVec), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -2185,6 +2189,27 @@ impl YieldKind { } } +/// The kind of BTF field metadata query. +#[derive(Clone, Copy, Encodable, Decodable, Debug, Eq, PartialEq, StableHash, Walkable)] +pub enum BtfFieldInfoKind { + /// Offset of the field. + ByteOffset, + /// Size of the field. + ByteSize, + /// Whether the field exists. + Exists, +} + +impl BtfFieldInfoKind { + 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", + } + } +} + /// 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 9d4c32825e1e4..884ff0028298f 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -493,6 +493,7 @@ macro_rules! common_visitor_and_walkers { YieldKind, EiiDecl, EiiImpl, + BtfFieldInfoKind, ); /// Each method of this trait is a hook to be potentially @@ -1074,6 +1075,8 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, kind, expr, ty), ExprKind::DirectConstArg(expr) => visit_visitable!($($mut)? 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 003ca39a5bda9..0a4c34c5ab9ca 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -483,6 +483,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 3111546c6e198..e4b5759e8075b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -437,6 +437,7 @@ enum ImplTraitPosition { Cast, ImplSelf, OffsetOf, + BtfFieldInfo, } impl std::fmt::Display for ImplTraitPosition { @@ -463,6 +464,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 325756e90ec66..69a9768c94a2a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -888,6 +888,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_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index cc61ba92da280..10ca3c0c8b37b 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1580,7 +1580,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - Rvalue::ThreadLocalRef(_) => {} + Rvalue::ThreadLocalRef(_) | Rvalue::BtfFieldInfo { .. } => {} Rvalue::Use(operand, _) | Rvalue::Repeat(operand, _) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 26036aae114bf..f685f47ff4787 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -298,7 +298,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } - Rvalue::ThreadLocalRef(_) => {} + Rvalue::ThreadLocalRef(_) | Rvalue::BtfFieldInfo { .. } => {} Rvalue::Use(operand, _) | Rvalue::Repeat(operand, _) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 14b1c9b31ef9f..e55ae48adaf98 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1702,7 +1702,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::BtfFieldInfo { .. } => {} } } @@ -2280,7 +2281,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) - | Rvalue::WrapUnsafeBinder(..) => None, + | Rvalue::WrapUnsafeBinder(..) + | Rvalue::BtfFieldInfo { .. } => None, Rvalue::Aggregate(aggregate, _) => match **aggregate { AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 1bc2bc8342559..711ac33378f9c 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_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 344a4834862e4..9eb30f4db313a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -852,6 +852,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: operand.val, layout, move_annotation: None } } + mir::Rvalue::BtfFieldInfo { base_ty, ref path, kind } => { + let base_ty = self.monomorphize(base_ty); + debug_assert_eq!(path.first().map(|step| step.container_ty), Some(base_ty)); + let path = path.iter().map(|step| mir::BtfFieldStep { + container_ty: self.monomorphize(step.container_ty), + variant: step.variant, + field: step.field, + }); + let base = bx.const_null(bx.type_ptr()); + let llval = bx.btf_field_info(base, path, kind); + let (val, ty) = match kind { + mir::BtfFieldInfoKind::ByteOffset | mir::BtfFieldInfoKind::ByteSize => { + (bx.zext(llval, bx.type_isize()), bx.tcx().types.usize) + } + mir::BtfFieldInfoKind::Exists => { + (bx.icmp(IntPredicate::IntNE, llval, bx.const_u32(0)), bx.tcx().types.bool) + } + }; + OperandRef { + val: OperandValue::Immediate(val), + layout: bx.cx().layout_of(ty), + move_annotation: None, + } + } + mir::Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in codegen"), } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index caf63abeef3ad..2aac71615591c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -354,6 +354,14 @@ 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_field_info( + &mut self, + _base: Self::Value, + _path: impl Iterator>, + _kind: mir::BtfFieldInfoKind, + ) -> Self::Value { + self.tcx().dcx().fatal("the selected codegen backed 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_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 7648bf4eb241d..3fa229e454e70 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -712,6 +712,15 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::WrapUnsafeBinder(..) => { // Unsafe binders are always trivial to create. } + + Rvalue::BtfFieldInfo { kind, .. } => { + let name = match kind { + BtfFieldInfoKind::ByteOffset => sym::btf_field_byte_offset, + BtfFieldInfoKind::ByteSize => sym::btf_field_byte_size, + BtfFieldInfoKind::Exists => sym::btf_field_exists, + }; + self.check_op(ops::IntrinsicNonConst { name }); + } } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index b2b8a567860e0..b1c9724a5ecf6 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -275,6 +275,8 @@ where // Otherwise, proceed structurally... operands.iter().any(|o| in_operand::(cx, in_local, o)) } + + Rvalue::BtfFieldInfo { .. } => false, } } diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 29b6e26d950d5..9980a9ec5f184 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -213,7 +213,8 @@ where | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::Aggregate(..) - | mir::Rvalue::WrapUnsafeBinder(..) => {} + | mir::Rvalue::WrapUnsafeBinder(..) + | mir::Rvalue::BtfFieldInfo { .. } => {} } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 836f542ee94ff..23a0676ad8166 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -293,6 +293,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let op = self.eval_operand(op, None)?; self.copy_op_allow_transmute(&op, &dest)?; } + + BtfFieldInfo { .. } => { + throw_unsup_format!("BTF field relocation queries cannot be interpreted"); + } } trace!("{:?}", self.dump_place(&dest)); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 112f784825975..5841d1721f65e 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, BtfFieldInfoKind, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, + LitIntType, LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, }; pub use rustc_ast::{ AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, @@ -2260,6 +2260,7 @@ impl Expr<'_> { | ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) | ExprKind::Use(..) + | ExprKind::BtfFieldInfo(..) | ExprKind::Err(_) => prefix_attrs_precedence(), ExprKind::DropTemps(expr, ..) => expr.precedence(has_attr), @@ -2333,7 +2334,8 @@ impl Expr<'_> { | ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::Cast(..) - | ExprKind::DropTemps(..) => false, + | ExprKind::DropTemps(..) + | ExprKind::BtfFieldInfo(..) => false, } } @@ -2386,9 +2388,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, _) @@ -2689,6 +2693,9 @@ pub enum ExprKind<'hir> { /// e.g. `unsafe<'a> &'a i32` <=> `&i32`. UnsafeBinderCast(UnsafeBinderCastKind, &'hir Expr<'hir>, Option<&'hir Ty<'hir>>), + /// BTF field field metadata query. + BtfFieldInfo(BtfFieldInfoKind, &'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 83b6e08e22b3c..2884a3894721a 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -930,7 +930,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_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index f2f485a30300a..b540cd2eebc1a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1778,6 +1778,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 2f302e35d563b..02187a1dd74a8 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::BtfFieldInfoKind; 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), } } @@ -3816,6 +3822,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: BtfFieldInfoKind, + 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 { + BtfFieldInfoKind::Exists => self.tcx.types.bool, + BtfFieldInfoKind::ByteOffset | BtfFieldInfoKind::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(); @@ -3912,7 +3962,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } ty::Adt(container_def, args) => { - if find_attr!(self.tcx, container_def.did(), BtfRelocatable(..)) { + 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", @@ -3989,8 +4041,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 b2255c8d9679a..d504347ac7423 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, ..) => { @@ -1394,6 +1395,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 7b1f38f882747..261541b3bc5f9 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 de061ceb6fd8d..d2d17ba7dce1e 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_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 5099859218187..03bbbaf95f05d 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/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 021c1c176d788..d4a4fcc9947d3 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1301,6 +1301,10 @@ impl<'tcx> Debug for Rvalue<'tcx> { WrapUnsafeBinder(ref op, ty) => { with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) } + + BtfFieldInfo { ref base_ty, ref path, kind } => { + write!(fmt, "btf_field_info({base_ty:?}, {path:?}, {kind:?})") + } } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 3f13f12713396..ee88569db026d 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -796,7 +796,8 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) - | Rvalue::WrapUnsafeBinder(_, _) => true, + | Rvalue::WrapUnsafeBinder(_, _) + | Rvalue::BtfFieldInfo { .. } => true, } } @@ -853,6 +854,10 @@ impl<'tcx> Rvalue<'tcx> { }, Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, Rvalue::WrapUnsafeBinder(_, ty) => ty, + Rvalue::BtfFieldInfo { kind, .. } => match kind { + BtfFieldInfoKind::ByteOffset | BtfFieldInfoKind::ByteSize => tcx.types.usize, + BtfFieldInfoKind::Exists => tcx.types.bool, + }, } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 4e2d16625266c..cdf2a3dd45d68 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1464,6 +1464,9 @@ pub enum Rvalue<'tcx> { /// /// [`ExprKind::Reborrow`]: crate::thir::ExprKind::Reborrow Reborrow(Ty<'tcx>, Mutability, Place<'tcx>), + + /// Queries BTF metadata for a statically resolved field path. + BtfFieldInfo { base_ty: Ty<'tcx>, path: Box<[BtfFieldStep<'tcx>]>, kind: BtfFieldInfoKind }, } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, StableHash)] @@ -1733,6 +1736,54 @@ impl From for BinOp { } } +// The step in BTF field path traversal. +#[derive( + Clone, + Debug, + TyEncodable, + TyDecodable, + StableHash, + PartialEq, + TypeFoldable, + TypeVisitable +)] +pub struct BtfFieldStep<'tcx> { + pub container_ty: Ty<'tcx>, + pub variant: VariantIdx, + pub field: FieldIdx, +} + +/// The kind of BTF field metadata query. +#[derive( + Clone, + Copy, + Debug, + TyEncodable, + TyDecodable, + StableHash, + PartialEq, + TypeFoldable, + TypeVisitable +)] +pub enum BtfFieldInfoKind { + /// Offset of the field. + ByteOffset, + /// Size of the field. + ByteSize, + /// Whether the field exists. + Exists, +} + +impl From for BtfFieldInfoKind { + fn from(kind: rustc_ast::BtfFieldInfoKind) -> Self { + match kind { + rustc_ast::BtfFieldInfoKind::ByteOffset => Self::ByteOffset, + rustc_ast::BtfFieldInfoKind::ByteSize => Self::ByteSize, + rustc_ast::BtfFieldInfoKind::Exists => Self::Exists, + } + } +} + // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 0ae59e99c2b5a..5b6f3ed49a79e 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -808,7 +808,15 @@ macro_rules! make_mir_visitor { self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } - + Rvalue::BtfFieldInfo { base_ty, path, kind: _ } => { + self.visit_ty($(& $mutability)? *base_ty, TyContext::Location(location)); + for step in path { + self.visit_ty( + $(& $mutability)? step.container_ty, + TyContext::Location(location), + ); + } + } } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 8e7d3d0d9c656..ee5f778fd39d5 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -14,7 +14,7 @@ use std::ops::Index; use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; -use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece, Mutability}; +use rustc_ast::{AsmMacro, BtfFieldInfoKind, InlineAsmOptions, InlineAsmTemplatePiece, Mutability}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; @@ -563,6 +563,12 @@ pub enum ExprKind<'tcx> { mutability: Mutability, target: Ty<'tcx>, }, + /// A BTF field metadata query. + BtfFieldInfo { + kind: BtfFieldInfoKind, + base_ty: Ty<'tcx>, + path: Box<[mir::BtfFieldStep<'tcx>]>, + }, } /// Represents the association of a field identifier and an expression. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 24aa4ac513d45..3f828c37854ca 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -190,6 +190,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( Reborrow { source, mutability: _, target: _ } => { visitor.visit_expr(&visitor.thir()[source]) } + BtfFieldInfo { .. } => {} } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index ff7cef3613437..9307c89ce9836 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/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index e92f74722626b..f6cad08453a01 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -587,7 +587,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the rvalue in a temporary instead of treating the reborrow // expression itself as an assignable place. | ExprKind::Reborrow { .. } - | ExprKind::WrapUnsafeBinder { .. } => { + | ExprKind::WrapUnsafeBinder { .. } + | ExprKind::BtfFieldInfo { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); let temp_lifetime = this.region_scope_tree.temporary_scope(expr.temp_scope_id); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index ea484bd05878b..58916ea678080 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -438,6 +438,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp = unpack!(block = this.as_temp(block, scope, source, mutability)); block.and(Rvalue::Reborrow(target, mutability, temp.into())) } + ExprKind::BtfFieldInfo { base_ty, ref path, kind } => { + block.and(Rvalue::BtfFieldInfo { base_ty, path: path.clone(), kind: kind.into() }) + } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index 1a2f0a791b697..e5e3fd674432b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -74,7 +74,8 @@ impl Category { // expression itself does not denote an assignable place. | ExprKind::Reborrow { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::WrapUnsafeBinder { .. } + | ExprKind::BtfFieldInfo { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 13a64346c36c4..8b9be21bb8dff 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -894,7 +894,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstParam { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::StaticRef { .. } - | ExprKind::WrapUnsafeBinder { .. } => { + | ExprKind::WrapUnsafeBinder { .. } + | ExprKind::BtfFieldInfo { .. } => { debug_assert!(match Category::of(&expr.kind).unwrap() { // should be handled above Category::Rvalue(RvalueFunc::Into) => false, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 29a93e2994d0d..b7441c4698a36 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -394,7 +394,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::InlineAsm { .. } | ExprKind::LogicalOp { .. } | ExprKind::Use { .. } - | ExprKind::Reborrow { .. } => { + | ExprKind::Reborrow { .. } + | ExprKind::BtfFieldInfo { .. } => { // We don't need to save the old value and restore it // because all the place expressions can't have more // than one child. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 3cade7d6a0a7a..f15d919d64eb6 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::{BtfFieldInfoKind, UnsafeBinderCastKind}; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::lang_items::LangItem; @@ -12,7 +12,7 @@ use rustc_middle::hir::place::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, }; use rustc_middle::middle::region; -use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; +use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, BtfFieldStep, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, @@ -1182,6 +1182,34 @@ impl<'tcx> ThirBuildCx<'tcx> { ExprKind::WrapUnsafeBinder { source: mirrored } } + hir::ExprKind::BtfFieldInfo(kind, _, _) => { + let indices = self.typeck_results.btf_field_info_data().get(expr.hir_id).unwrap(); + let Some(&(base_ty, _, _)) = indices.first() else { + return match kind { + BtfFieldInfoKind::ByteOffset | BtfFieldInfoKind::ByteSize => mk_expr( + ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_target_usize(0u128, tcx).unwrap(), + user_ty: None, + }, + tcx.types.usize, + ), + BtfFieldInfoKind::Exists => mk_expr( + ExprKind::NonHirLiteral { lit: false.into(), user_ty: None }, + tcx.types.bool, + ), + }; + }; + let path = indices + .iter() + .map(|&(container_ty, variant, field)| BtfFieldStep { + container_ty, + variant, + field, + }) + .collect(); + ExprKind::BtfFieldInfo { base_ty, path, kind } + } + hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) }, 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_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 62a0cbc24fd73..19f3cfef9b5dc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -361,7 +361,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | VarRef { .. } | ZstLiteral { .. } | Yield { .. } - | Reborrow { .. } => true, + | Reborrow { .. } + | BtfFieldInfo { .. } => true, } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ddb56a04c308d..fdd18a5383ac4 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -608,6 +608,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "ty:", depth_lvl + 1); print_indented!(self, "}", depth_lvl); } + BtfFieldInfo { base_ty, path, kind } => { + print_indented!(self, "BtfFieldInfo {", depth_lvl); + print_indented!(self, format!("base_ty: {:?}", base_ty), depth_lvl + 1); + print_indented!(self, format!("path: {:?}", path), depth_lvl + 1); + print_indented!(self, format!("kind: {:?}", kind), depth_lvl + 1); + print_indented!(self, "}", depth_lvl); + } } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index c5b69c563b2fe..a4dbaca9f58d1 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -94,7 +94,8 @@ where | Rvalue::Discriminant(..) | Rvalue::Aggregate(..) | Rvalue::CopyForDeref(..) - | Rvalue::WrapUnsafeBinder(..) => {} + | Rvalue::WrapUnsafeBinder(..) + | Rvalue::BtfFieldInfo { .. } => {} } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 74aaa19bf2373..e8a3de4b55128 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -441,7 +441,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::BtfFieldInfo { .. } => {} } } diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 2af68a9046e5a..b72ad143175d2 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -480,7 +480,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { | Rvalue::Cast(..) | Rvalue::BinaryOp(..) | Rvalue::Aggregate(..) - | Rvalue::WrapUnsafeBinder(..) => { + | Rvalue::WrapUnsafeBinder(..) + | Rvalue::BtfFieldInfo { .. } => { // No modification is possible through these r-values. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 9d751a7cc5bd0..a14dce96a1c63 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1121,7 +1121,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } // Unsupported values. - Rvalue::ThreadLocalRef(..) => return None, + Rvalue::ThreadLocalRef(..) | Rvalue::BtfFieldInfo { .. } => return None, Rvalue::CopyForDeref(_) => { bug!("forbidden in runtime MIR: {rvalue:?}") } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index a2f2f8fa063c2..c4334a29b4694 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -448,7 +448,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Repeat(..) | Rvalue::Cast(..) | Rvalue::Discriminant(..) - | Rvalue::WrapUnsafeBinder(..) => {} + | Rvalue::WrapUnsafeBinder(..) + | Rvalue::BtfFieldInfo { .. } => {} } // FIXME we need to revisit this for #67176 @@ -548,7 +549,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { trace!(?layout); let val: Value<'_> = match *rvalue { - ThreadLocalRef(_) => return None, + ThreadLocalRef(_) | BtfFieldInfo { .. } => return None, Use(ref operand, _) | WrapUnsafeBinder(ref operand, _) => { self.eval_operand(operand)?.into() diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index 41614462a81d4..693025574defa 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -98,7 +98,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { | Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) - | Rvalue::Discriminant(..) => false, + | Rvalue::Discriminant(..) + | Rvalue::BtfFieldInfo { .. } => false, }; // The sides of an assignment must not alias. if forbid_aliasing { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index ae2028f1c62ea..b147f4bcc2a6a 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -593,6 +593,8 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(o)?; } } + + Rvalue::BtfFieldInfo { .. } => return Err(Unpromotable), } Ok(()) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index b9c55439f0597..63a1057111adb 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1460,6 +1460,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + + Rvalue::BtfFieldInfo { path, .. } => { + if path.is_empty() { + self.fail(location, "BTF field info path must not be empty"); + } + } } self.super_rvalue(rvalue, location); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3e03730ab632b..7a600dbdc9e20 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, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, - FnRetTy, ForLoop, Guard, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, - Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, + BlockCheckMode, BtfFieldInfoKind, CaptureBy, ClosureBinder, 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}; @@ -2050,6 +2050,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(), + BtfFieldInfoKind::ByteOffset, + )?), + sym::btf_field_byte_size => Some(this.parse_expr_btf_field_info( + lo, + ident.as_str(), + BtfFieldInfoKind::ByteSize, + )?), + sym::btf_field_exists => Some(this.parse_expr_btf_field_info( + lo, + ident.as_str(), + BtfFieldInfoKind::Exists, + )?), _ => None, }) }) @@ -2135,6 +2150,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: BtfFieldInfoKind, + ) -> 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. @@ -4539,6 +4585,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/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 87193b73a1a95..58edf8542e495 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_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 124329526028d..c5e25fc5fcac2 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -264,6 +264,9 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { } CopyForDeref(place) => crate::mir::Rvalue::CopyForDeref(place.stable(tables, cx)), WrapUnsafeBinder(..) => unimplemented!("FIXME(unsafe_binders):"), + BtfFieldInfo { .. } => { + unimplemented!("BTF field info builtins are not exposed to the users") + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 397fb13705a1d..8ec12e39b2c9a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -553,6 +553,9 @@ symbols! { breg, bridge, bswap, + btf_field_byte_offset, + btf_field_byte_size, + btf_field_exists, btf_relocatable, btf_relocations, built, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index cd35423c5ef14..f92d632198982 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -201,6 +201,9 @@ fn recurse_build<'tcx>( ExprKind::InlineAsm { .. } => { error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? } + ExprKind::BtfFieldInfo { .. } => { + error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? + } // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen ExprKind::VarRef { .. } @@ -309,6 +312,10 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::InlineAsm(_) | thir::ExprKind::ThreadLocalRef(_) | thir::ExprKind::Yield { .. } => false, + thir::ExprKind::BtfFieldInfo { base_ty, ref path, kind: _ } => { + base_ty.has_non_region_param() + || path.iter().any(|step| step.container_ty.has_non_region_param()) + } thir::ExprKind::Reborrow { .. } => { unimplemented!(); } diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 1a65ef7200f52..b167a27bbdd8d 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -338,6 +338,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. From 93801e5d58a254321cebdd6ef150be4e37533b28 Mon Sep 17 00:00:00 2001 From: vad Date: Fri, 14 Aug 2026 16:54:33 +0200 Subject: [PATCH 4/5] Add LLVM support for BTF relocations Expose wrappers for the `llvm.preserve.struct.access.index` and `llvm.preserve.union.access.index` intrinsics. Lower backend-neutral BTF field paths by mapping Rust field indices to LLVM aggregate indices and emitting the corresponding intrinsic calls (`llvm.preserve.{struct,union}.access.index`). Pass the resulting field pointer to `llvm.bpf.preserve.field.info`. --- compiler/rustc_codegen_llvm/src/builder.rs | 79 +++++++++++++++++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 16 ++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 16 ++++ compiler/rustc_middle/src/mir/syntax.rs | 10 +++ 4 files changed, 121 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index b8e2b0029167a..ba11ed0f785fc 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -22,6 +22,7 @@ use rustc_middle::ty::layout::{ TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::{bug, mir}; use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; @@ -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, @@ -1545,6 +1547,83 @@ 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]); } + + // BTF relocations + fn btf_field_info( + &mut self, + base: &'ll Value, + path: impl Iterator>, + kind: mir::BtfFieldInfoKind, + ) -> &'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 mut field_ptr = base; + for step in path { + let layout = self.layout_of(step.container_ty); + let layout = layout.for_variant(&layout_cx, step.variant); + field_ptr = match step.container_ty.kind() { + ty::Adt(adt, _) if adt.is_union() => { + let dbg_info: &'ll Metadata = type_di_node(self.cx, step.container_ty); + unsafe { + llvm::LLVMRustBuildPreserveUnionAccessIndex( + self.llbuilder, + field_ptr, + step.field.index() as c_uint, + Some(dbg_info), + ) + } + } + ty::Adt(..) | ty::Tuple(..) => { + let llvm_index = llvm_struct_field_index(self, layout, step.field.index()); + let dbg_info: &'ll Metadata = type_di_node(self.cx, step.container_ty); + unsafe { + llvm::LLVMRustBuildPreserveStructAccessIndex( + self.llbuilder, + self.cx().backend_type(layout), + field_ptr, + llvm_index as c_uint, + step.field.index() as c_uint, + Some(dbg_info), + ) + } + } + _ => bug!( + "BTF field info queries are unsupported for container: {:?}", + step.container_ty + ), + }; + } + + self.call_intrinsic( + "llvm.bpf.preserve.field.info", + &[self.val_ty(field_ptr)], + &[field_ptr, self.const_u64(kind.as_u64())], + ) + } } 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 1a60b59a93525..36e4b657173ea 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_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 983a506bd4ac6..9b07148d32fce 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1776,6 +1776,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/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index cdf2a3dd45d68..2b596559dff88 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1774,6 +1774,16 @@ pub enum BtfFieldInfoKind { Exists, } +impl BtfFieldInfoKind { + pub fn as_u64(&self) -> u64 { + match self { + Self::ByteOffset => 0, + Self::ByteSize => 1, + Self::Exists => 2, + } + } +} + impl From for BtfFieldInfoKind { fn from(kind: rustc_ast::BtfFieldInfoKind) -> Self { match kind { From b42b86941a641fe98ab02b369625e654c96e4b4e Mon Sep 17 00:00:00 2001 From: vad Date: Fri, 14 Aug 2026 18:09:56 +0200 Subject: [PATCH 5/5] Add codegen test for BTF relocations Test the `btf_field_exists`, `btf_field_byte_offset` and `btf_field_byte_size` builtins and make sure they emit correct LLVM intrinsic calls. --- tests/codegen-llvm/btf-field-info-minicore.rs | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/codegen-llvm/btf-field-info-minicore.rs 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) +}