Skip to main content

rustc_lint/
gpukernel_abi.rs

1use std::iter;
2
3use rustc_abi::ExternAbi;
4use rustc_hir::{self as hir, find_attr};
5use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
6use rustc_session::{declare_lint, declare_lint_pass};
7use rustc_span::Span;
8use rustc_span::def_id::LocalDefId;
9
10use crate::lints::{ImproperGpuKernelArg, MissingGpuKernelExportName};
11use crate::{LateContext, LateLintPass, LintContext};
12
13#[doc =
r" The `improper_gpu_kernel_arg` lint detects incorrect use of types in `gpu-kernel`"]
#[doc = r" arguments."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r#" extern "gpu-kernel" fn kernel(_: [i32; 10]) {}"#]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" This will produce:"]
#[doc = r""]
#[doc = r" ```text"]
#[doc =
r#" warning: passing type `[i32; 10]` to a function with "gpu-kernel" ABI may have unexpected behavior"#]
#[doc = r"  --> t.rs:2:34"]
#[doc = r"   |"]
#[doc = r#" 2 | extern "gpu-kernel" fn kernel(_: [i32; 10]) {}"#]
#[doc = r"   |                                  ^^^^^^^^^"]
#[doc = r"   |"]
#[doc =
r"   = help: use primitive types and raw pointers to get reliable behavior"]
#[doc = r"   = note: `#[warn(improper_gpu_kernel_arg)]` on by default"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" The compiler has several checks to verify that types used as arguments in `gpu-kernel`"]
#[doc =
r" functions follow certain rules to ensure proper compatibility with the foreign interfaces."]
#[doc =
r" This lint is issued when it detects a probable mistake in a signature."]
static IMPROPER_GPU_KERNEL_ARG: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "IMPROPER_GPU_KERNEL_ARG",
            default_level: ::rustc_lint_defs::Warn,
            desc: "GPU kernel entry points have a limited ABI",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
14    /// The `improper_gpu_kernel_arg` lint detects incorrect use of types in `gpu-kernel`
15    /// arguments.
16    ///
17    /// ### Example
18    ///
19    /// ```rust,ignore (fails on non-GPU targets)
20    /// #[unsafe(no_mangle)]
21    /// extern "gpu-kernel" fn kernel(_: [i32; 10]) {}
22    /// ```
23    ///
24    /// This will produce:
25    ///
26    /// ```text
27    /// warning: passing type `[i32; 10]` to a function with "gpu-kernel" ABI may have unexpected behavior
28    ///  --> t.rs:2:34
29    ///   |
30    /// 2 | extern "gpu-kernel" fn kernel(_: [i32; 10]) {}
31    ///   |                                  ^^^^^^^^^
32    ///   |
33    ///   = help: use primitive types and raw pointers to get reliable behavior
34    ///   = note: `#[warn(improper_gpu_kernel_arg)]` on by default
35    /// ```
36    ///
37    /// ### Explanation
38    ///
39    /// The compiler has several checks to verify that types used as arguments in `gpu-kernel`
40    /// functions follow certain rules to ensure proper compatibility with the foreign interfaces.
41    /// This lint is issued when it detects a probable mistake in a signature.
42    IMPROPER_GPU_KERNEL_ARG,
43    Warn,
44    "GPU kernel entry points have a limited ABI"
45}
46
47#[doc =
r" The `missing_gpu_kernel_export_name` lint detects `gpu-kernel` functions that have a mangled name."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r#" extern "gpu-kernel" fn kernel() { }"#]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" This will produce:"]
#[doc = r""]
#[doc = r" ```text"]
#[doc = r#" warning: function with the "gpu-kernel" ABI has a mangled name"#]
#[doc = r"  --> t.rs:1:1"]
#[doc = r"   |"]
#[doc = r#" 1 | extern "gpu-kernel" fn kernel() {}"#]
#[doc = r"   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"]
#[doc = r"   |"]
#[doc =
r#"   = help: use `unsafe(no_mangle)` or `unsafe(export_name = "<name>")`"#]
#[doc =
r"   = note: mangled names make it hard to find the kernel, this is usually not intended"]
#[doc = r"   = note: `#[warn(missing_gpu_kernel_export_name)]` on by default"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" `gpu-kernel` functions are usually searched by name in the compiled file."]
#[doc =
r" A mangled name is usually unintentional as it would need to be searched by the mangled name."]
#[doc = r""]
#[doc =
r" To use an unmangled name for the kernel, either `no_mangle` or `export_name` can be used."]
#[doc = r" ```rust,ignore (fails on non-GPU targets)"]
#[doc = r#" // Can be found by the name "kernel""#]
#[doc = r" #[unsafe(no_mangle)]"]
#[doc = r#" extern "gpu-kernel" fn kernel() { }"#]
#[doc = r""]
#[doc = r#" // Can be found by the name "new_name""#]
#[doc = r#" #[unsafe(export_name = "new_name")]"#]
#[doc = r#" extern "gpu-kernel" fn other_kernel() { }"#]
#[doc = r" ```"]
static MISSING_GPU_KERNEL_EXPORT_NAME: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "MISSING_GPU_KERNEL_EXPORT_NAME",
            default_level: ::rustc_lint_defs::Warn,
            desc: "mangled gpu-kernel function",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
48    /// The `missing_gpu_kernel_export_name` lint detects `gpu-kernel` functions that have a mangled name.
49    ///
50    /// ### Example
51    ///
52    /// ```rust,ignore (fails on non-GPU targets)
53    /// extern "gpu-kernel" fn kernel() { }
54    /// ```
55    ///
56    /// This will produce:
57    ///
58    /// ```text
59    /// warning: function with the "gpu-kernel" ABI has a mangled name
60    ///  --> t.rs:1:1
61    ///   |
62    /// 1 | extern "gpu-kernel" fn kernel() {}
63    ///   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64    ///   |
65    ///   = help: use `unsafe(no_mangle)` or `unsafe(export_name = "<name>")`
66    ///   = note: mangled names make it hard to find the kernel, this is usually not intended
67    ///   = note: `#[warn(missing_gpu_kernel_export_name)]` on by default
68    /// ```
69    ///
70    /// ### Explanation
71    ///
72    /// `gpu-kernel` functions are usually searched by name in the compiled file.
73    /// A mangled name is usually unintentional as it would need to be searched by the mangled name.
74    ///
75    /// To use an unmangled name for the kernel, either `no_mangle` or `export_name` can be used.
76    /// ```rust,ignore (fails on non-GPU targets)
77    /// // Can be found by the name "kernel"
78    /// #[unsafe(no_mangle)]
79    /// extern "gpu-kernel" fn kernel() { }
80    ///
81    /// // Can be found by the name "new_name"
82    /// #[unsafe(export_name = "new_name")]
83    /// extern "gpu-kernel" fn other_kernel() { }
84    /// ```
85    MISSING_GPU_KERNEL_EXPORT_NAME,
86    Warn,
87    "mangled gpu-kernel function"
88}
89
90pub struct ImproperGpuKernelLint;
#[automatically_derived]
impl ::core::marker::Copy for ImproperGpuKernelLint { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ImproperGpuKernelLint { }
#[automatically_derived]
impl ::core::clone::Clone for ImproperGpuKernelLint {
    #[inline]
    fn clone(&self) -> ImproperGpuKernelLint { *self }
}
impl ::rustc_lint_defs::LintPass for ImproperGpuKernelLint {
    fn name(&self) -> &'static str { "ImproperGpuKernelLint" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [IMPROPER_GPU_KERNEL_ARG, MISSING_GPU_KERNEL_EXPORT_NAME]))
    }
}
impl ImproperGpuKernelLint {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [IMPROPER_GPU_KERNEL_ARG, MISSING_GPU_KERNEL_EXPORT_NAME]))
    }
}declare_lint_pass!(ImproperGpuKernelLint => [
91    IMPROPER_GPU_KERNEL_ARG,
92    MISSING_GPU_KERNEL_EXPORT_NAME,
93]);
94
95/// Check for valid and invalid types.
96struct CheckGpuKernelTypes<'tcx> {
97    tcx: TyCtxt<'tcx>,
98    // If one or more invalid types were encountered while folding.
99    has_invalid: bool,
100}
101
102impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CheckGpuKernelTypes<'tcx> {
103    fn cx(&self) -> TyCtxt<'tcx> {
104        self.tcx
105    }
106
107    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
108        match ty.kind() {
109            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
110            // Thin pointers are allowed but fat pointers with metadata are not
111            ty::RawPtr(_, _) => {
112                if !ty.pointee_metadata_ty_or_projection(self.tcx).is_unit() {
113                    self.has_invalid = true;
114                }
115            }
116
117            ty::Adt(_, _)
118            | ty::Alias(_, _)
119            | ty::Array(_, _)
120            | ty::Bound(_, _)
121            | ty::Closure(_, _)
122            | ty::Coroutine(_, _)
123            | ty::CoroutineClosure(_, _)
124            | ty::CoroutineWitness(..)
125            | ty::Dynamic(_, _)
126            | ty::FnDef(_, _)
127            | ty::FnPtr(..)
128            | ty::Foreign(_)
129            | ty::Never
130            | ty::Pat(_, _)
131            | ty::Placeholder(_)
132            | ty::Ref(_, _, _)
133            | ty::Slice(_)
134            | ty::Str
135            | ty::Tuple(_) => self.has_invalid = true,
136
137            _ => return ty.super_fold_with(self),
138        }
139        ty
140    }
141}
142
143/// `ImproperGpuKernelLint` checks `gpu-kernel` function definitions:
144///
145/// - `extern "gpu-kernel" fn` arguments should be primitive types.
146/// - `extern "gpu-kernel" fn` should have an unmangled name.
147impl<'tcx> LateLintPass<'tcx> for ImproperGpuKernelLint {
148    fn check_fn(
149        &mut self,
150        cx: &LateContext<'tcx>,
151        kind: hir::intravisit::FnKind<'tcx>,
152        decl: &'tcx hir::FnDecl<'_>,
153        _: &'tcx hir::Body<'_>,
154        span: Span,
155        id: LocalDefId,
156    ) {
157        use hir::intravisit::FnKind;
158
159        let abi = match kind {
160            FnKind::ItemFn(_, _, header, ..) => header.abi,
161            FnKind::Method(_, sig, ..) => sig.header.abi,
162            _ => return,
163        };
164
165        if abi != ExternAbi::GpuKernel {
166            return;
167        }
168
169        let sig = cx.tcx.fn_sig(id).instantiate_identity();
170        let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
171
172        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
173            let mut checker = CheckGpuKernelTypes { tcx: cx.tcx, has_invalid: false };
174            input_ty.fold_with(&mut checker);
175            if checker.has_invalid {
176                cx.tcx.emit_node_span_lint(
177                    IMPROPER_GPU_KERNEL_ARG,
178                    input_hir.hir_id,
179                    input_hir.span,
180                    ImproperGpuKernelArg { ty: *input_ty },
181                );
182            }
183        }
184
185        // Check for no_mangle/export_name, so the kernel can be found when querying the compiled object for the kernel function by name
186        if !{

        #[allow(deprecated)]
        {
            {
                'done:
                    {
                    for i in cx.tcx.get_all_attrs(id) {
                        #[allow(unused_imports)]
                        use rustc_hir::attrs::AttributeKind::*;
                        let i: &rustc_hir::Attribute = i;
                        match i {
                            rustc_hir::Attribute::Parsed(NoMangle(..) | ExportName { ..
                                }) => {
                                break 'done Some(());
                            }
                            rustc_hir::Attribute::Unparsed(..) =>
                                {}
                                #[deny(unreachable_patterns)]
                                _ => {}
                        }
                    }
                    None
                }
            }
        }
    }.is_some()find_attr!(cx.tcx, id, NoMangle(..) | ExportName { .. }) {
187            cx.emit_span_lint(MISSING_GPU_KERNEL_EXPORT_NAME, span, MissingGpuKernelExportName);
188        }
189    }
190}