vyre_driver/
param_inlining.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ParamInliningPolicy {
19 pub max_inline_bytes: u32,
22 pub align_bytes: u32,
26 pub allow_padding_to_align: bool,
31}
32
33impl ParamInliningPolicy {
34 #[must_use]
39 pub const fn large_inline_default() -> Self {
40 Self {
41 max_inline_bytes: 3 * 1024,
42 align_bytes: 4,
43 allow_padding_to_align: true,
44 }
45 }
46
47 #[must_use]
52 pub const fn small_inline_default() -> Self {
53 Self {
54 max_inline_bytes: 128,
55 align_bytes: 4,
56 allow_padding_to_align: true,
57 }
58 }
59
60 #[must_use]
64 pub const fn disabled() -> Self {
65 Self {
66 max_inline_bytes: 0,
67 align_bytes: 4,
68 allow_padding_to_align: false,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum ParamInliningDecision {
76 Inline {
79 padded_bytes: u32,
82 },
83 UniformBuffer,
86}
87
88impl ParamInliningDecision {
89 #[must_use]
91 pub fn is_inline(&self) -> bool {
92 matches!(self, Self::Inline { .. })
93 }
94}
95
96#[must_use]
101pub fn decide_param_inlining(bytes_len: u32, policy: ParamInliningPolicy) -> ParamInliningDecision {
102 if policy.max_inline_bytes == 0 {
103 return ParamInliningDecision::UniformBuffer;
104 }
105 if policy.align_bytes == 0 {
106 return ParamInliningDecision::UniformBuffer;
109 }
110
111 let needs_padding = bytes_len % policy.align_bytes != 0;
112 let padded_bytes = if needs_padding {
113 if !policy.allow_padding_to_align {
114 return ParamInliningDecision::UniformBuffer;
115 }
116 let remainder = bytes_len % policy.align_bytes;
120 let padding = policy.align_bytes - remainder;
121 let padded = u64::from(bytes_len) + u64::from(padding);
122 if padded > u64::from(policy.max_inline_bytes) {
123 return ParamInliningDecision::UniformBuffer;
124 }
125 padded as u32
126 } else {
127 bytes_len
128 };
129
130 if padded_bytes <= policy.max_inline_bytes {
131 ParamInliningDecision::Inline { padded_bytes }
132 } else {
133 ParamInliningDecision::UniformBuffer
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn small_aligned_payload_inlines_under_large_inline_default() {
143 let policy = ParamInliningPolicy::large_inline_default();
144 let decision = decide_param_inlining(64, policy);
145 assert_eq!(decision, ParamInliningDecision::Inline { padded_bytes: 64 });
146 assert!(decision.is_inline());
147 }
148
149 #[test]
150 fn payload_at_inline_ceiling_still_inlines() {
151 let policy = ParamInliningPolicy::large_inline_default();
152 let decision = decide_param_inlining(3 * 1024, policy);
154 assert_eq!(
155 decision,
156 ParamInliningDecision::Inline {
157 padded_bytes: 3 * 1024
158 }
159 );
160 }
161
162 #[test]
163 fn payload_above_inline_ceiling_falls_back_to_uniform() {
164 let policy = ParamInliningPolicy::large_inline_default();
165 let decision = decide_param_inlining(3 * 1024 + 1, policy);
166 assert_eq!(decision, ParamInliningDecision::UniformBuffer);
168 }
169
170 #[test]
171 fn unaligned_payload_pads_when_allowed() {
172 let policy = ParamInliningPolicy::large_inline_default();
173 let decision = decide_param_inlining(17, policy);
175 assert_eq!(decision, ParamInliningDecision::Inline { padded_bytes: 20 });
176 }
177
178 #[test]
179 fn unaligned_payload_falls_back_when_padding_disallowed() {
180 let policy = ParamInliningPolicy {
181 max_inline_bytes: 64,
182 align_bytes: 4,
183 allow_padding_to_align: false,
184 };
185 let decision = decide_param_inlining(17, policy);
186 assert_eq!(decision, ParamInliningDecision::UniformBuffer);
187 }
188
189 #[test]
190 fn padded_size_must_also_fit_under_ceiling() {
191 let policy = ParamInliningPolicy {
192 max_inline_bytes: 16,
193 align_bytes: 8,
194 allow_padding_to_align: true,
195 };
196 assert_eq!(
198 decide_param_inlining(13, policy),
199 ParamInliningDecision::Inline { padded_bytes: 16 }
200 );
201 assert_eq!(
203 decide_param_inlining(17, policy),
204 ParamInliningDecision::UniformBuffer
205 );
206 }
207
208 #[test]
209 fn disabled_policy_always_uses_uniform() {
210 let policy = ParamInliningPolicy::disabled();
211 assert_eq!(
212 decide_param_inlining(0, policy),
213 ParamInliningDecision::UniformBuffer
214 );
215 assert_eq!(
216 decide_param_inlining(8, policy),
217 ParamInliningDecision::UniformBuffer
218 );
219 assert_eq!(
220 decide_param_inlining(1024, policy),
221 ParamInliningDecision::UniformBuffer
222 );
223 }
224
225 #[test]
226 fn small_inline_default_inlines_tiny_payloads_only() {
227 let policy = ParamInliningPolicy::small_inline_default();
228 assert_eq!(
230 decide_param_inlining(64, policy),
231 ParamInliningDecision::Inline { padded_bytes: 64 }
232 );
233 assert_eq!(
235 decide_param_inlining(256, policy),
236 ParamInliningDecision::UniformBuffer
237 );
238 }
239
240 #[test]
241 fn zero_byte_payload_inlines_with_zero_padded_bytes() {
242 let policy = ParamInliningPolicy::large_inline_default();
243 assert_eq!(
247 decide_param_inlining(0, policy),
248 ParamInliningDecision::Inline { padded_bytes: 0 }
249 );
250 }
251
252 #[test]
253 fn zero_align_policy_falls_back_safely() {
254 let policy = ParamInliningPolicy {
257 max_inline_bytes: 1024,
258 align_bytes: 0,
259 allow_padding_to_align: true,
260 };
261 assert_eq!(
262 decide_param_inlining(64, policy),
263 ParamInliningDecision::UniformBuffer
264 );
265 }
266
267 #[test]
268 fn adversarial_padding_overflow_cannot_inline() {
269 let policy = ParamInliningPolicy {
270 max_inline_bytes: u32::MAX,
271 align_bytes: 256,
272 allow_padding_to_align: true,
273 };
274 assert_eq!(
275 decide_param_inlining(u32::MAX - 1, policy),
276 ParamInliningDecision::UniformBuffer
277 );
278 }
279
280 #[test]
281 fn source_has_no_saturating_padding_math() {
282 let source = include_str!("param_inlining.rs");
283 assert!(
284 !source.contains(concat!(".", "saturating_add")),
285 "param inlining cannot silently clamp launch-param padding"
286 );
287 }
288}