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