1use std::ffi::c_void;
4use std::sync::Arc;
5
6use cudarc::driver::LaunchConfig;
7use xlog_core::{Result, XlogError};
8use xlog_cuda::memory::TrackedCudaSlice;
9use xlog_cuda::provider::{cnf_kernels, CNF_MODULE};
10use xlog_cuda::{AsKernelParam, CudaKernelProvider, LaunchAsync};
11use xlog_solve::GpuCnf;
12
13use crate::compilation::gpu_pir::GpuPirGraph;
14use crate::compilation::gpu_pir::GpuPirRoots;
15
16pub struct GpuCnfVarTables {
18 pub node_var: TrackedCudaSlice<u32>,
20 pub leaf_var: TrackedCudaSlice<u32>,
22 pub choice_var: TrackedCudaSlice<u32>,
24 pub max_var: u32,
26}
27
28pub struct GpuCnfEncoding {
30 pub cnf: GpuCnf,
32 pub vars: GpuCnfVarTables,
34 pub decision_var_limit: TrackedCudaSlice<u32>,
40}
41
42const MAX_GRID_X: u64 = 65_535;
43
44fn checked_grid_dim(n: u32, block: u32, context: &str) -> Result<u32> {
45 if block == 0 {
46 return Err(XlogError::Kernel(format!(
47 "{context}: CUDA launch block size must be nonzero"
48 )));
49 }
50 let grid = if n == 0 {
51 1
52 } else {
53 u64::from(n).div_ceil(u64::from(block))
54 };
55 if grid > MAX_GRID_X {
56 return Err(XlogError::Kernel(format!(
57 "{context}: launch grid {grid} exceeds x-dimension limit {MAX_GRID_X} \
58 for {n} elements with block size {block}"
59 )));
60 }
61 Ok(grid as u32)
62}
63
64pub fn encode_cnf_gpu(
66 pir: &GpuPirGraph,
67 roots: &GpuPirRoots,
68 provider: &Arc<CudaKernelProvider>,
69) -> Result<GpuCnfEncoding> {
70 if roots.roots.is_empty() {
71 return Err(XlogError::Compilation(
72 "Cannot encode CNF for empty PIR root set".to_string(),
73 ));
74 }
75 let num_nodes = pir.node_type.len();
76 if num_nodes == 0 {
77 return Err(XlogError::Compilation(
78 "Cannot encode CNF for empty PIR graph".to_string(),
79 ));
80 }
81
82 let num_nodes_u32 = u32::try_from(num_nodes)
83 .map_err(|_| XlogError::Compilation("PIR node count overflow".to_string()))?;
84 let num_roots_u32 = u32::try_from(roots.roots.len())
85 .map_err(|_| XlogError::Compilation("PIR root count exceeds u32::MAX".to_string()))?;
86
87 let num_edges = pir.children.len();
88 let n64 = num_nodes as u64;
89 let e64 = num_edges as u64;
90
91 let var_cap = u32::try_from(
92 n64.checked_mul(3)
93 .ok_or_else(|| XlogError::Kernel("CNF var capacity overflow".to_string()))?,
94 )
95 .map_err(|_| XlogError::Kernel("CNF var capacity exceeds u32::MAX".to_string()))?;
96 let clause_cap = u32::try_from(
97 e64.checked_add(
98 n64.checked_mul(4)
99 .ok_or_else(|| XlogError::Kernel("CNF clause capacity overflow".to_string()))?,
100 )
101 .ok_or_else(|| XlogError::Kernel("CNF clause capacity overflow".to_string()))?,
102 )
103 .map_err(|_| XlogError::Kernel("CNF clause capacity exceeds u32::MAX".to_string()))?;
104 let lit_cap =
105 u32::try_from(
106 e64.checked_mul(3)
107 .ok_or_else(|| XlogError::Kernel("CNF literal capacity overflow".to_string()))?
108 .checked_add(n64.checked_mul(12).ok_or_else(|| {
109 XlogError::Kernel("CNF literal capacity overflow".to_string())
110 })?)
111 .ok_or_else(|| XlogError::Kernel("CNF literal capacity overflow".to_string()))?,
112 )
113 .map_err(|_| XlogError::Kernel("CNF literal capacity exceeds u32::MAX".to_string()))?;
114
115 let leaf_cap = num_nodes_u32;
116 let choice_cap = num_nodes_u32;
117
118 let memory = provider.memory();
119 let device = provider.device().inner();
120
121 let mut reachable = memory.alloc::<u32>(num_nodes)?;
122 let mut queue = memory.alloc::<u32>(num_nodes)?;
123 let mut queue_ready = memory.alloc::<u32>(num_nodes)?;
124 let mut head = memory.alloc::<u32>(1)?;
125 let mut tail = memory.alloc::<u32>(1)?;
126 let mut in_flight = memory.alloc::<u32>(1)?;
127
128 let mut leaf_used = memory.alloc::<u32>(leaf_cap as usize)?;
129 let mut choice_used = memory.alloc::<u32>(choice_cap as usize)?;
130 let mut leaf_var = memory.alloc::<u32>(leaf_cap as usize)?;
131 let mut choice_var = memory.alloc::<u32>(choice_cap as usize)?;
132
133 let mut node_needs_var = memory.alloc::<u32>(num_nodes)?;
134 let mut node_var = memory.alloc::<u32>(num_nodes)?;
135
136 let mut clause_counts = memory.alloc::<u32>(num_nodes)?;
137 let mut lit_counts = memory.alloc::<u32>(num_nodes)?;
138
139 let mut leaf_prefix = memory.alloc::<u32>(leaf_cap as usize)?;
140 let mut choice_prefix = memory.alloc::<u32>(choice_cap as usize)?;
141
142 let mut node_last = memory.alloc::<u32>(1)?;
143 let mut clause_last = memory.alloc::<u32>(1)?;
144 let mut lit_last = memory.alloc::<u32>(1)?;
145
146 let mut num_leaf = memory.alloc::<u32>(1)?;
147 let mut num_choice = memory.alloc::<u32>(1)?;
148 let mut base_choice = memory.alloc::<u32>(1)?;
149 let mut base_node = memory.alloc::<u32>(1)?;
150 let mut decision_var_limit = memory.alloc::<u32>(1)?;
151
152 let d_num_vars = memory.alloc::<u32>(1)?;
153 let d_num_clauses = memory.alloc::<u32>(1)?;
154 let d_num_lits = memory.alloc::<u32>(1)?;
155
156 let mut d_offsets = memory.alloc::<u32>((clause_cap as usize) + 1)?;
157 let d_lits = memory.alloc::<i32>(lit_cap as usize)?;
158
159 device
160 .memset_zeros(&mut reachable)
161 .map_err(|e| XlogError::Kernel(format!("zero reachable: {}", e)))?;
162 device
163 .memset_zeros(&mut queue)
164 .map_err(|e| XlogError::Kernel(format!("zero queue: {}", e)))?;
165 device
166 .memset_zeros(&mut queue_ready)
167 .map_err(|e| XlogError::Kernel(format!("zero queue_ready: {}", e)))?;
168 device
169 .memset_zeros(&mut head)
170 .map_err(|e| XlogError::Kernel(format!("zero head: {}", e)))?;
171 device
172 .memset_zeros(&mut tail)
173 .map_err(|e| XlogError::Kernel(format!("zero tail: {}", e)))?;
174 device
175 .memset_zeros(&mut in_flight)
176 .map_err(|e| XlogError::Kernel(format!("zero in_flight: {}", e)))?;
177 device
178 .memset_zeros(&mut leaf_used)
179 .map_err(|e| XlogError::Kernel(format!("zero leaf_used: {}", e)))?;
180 device
181 .memset_zeros(&mut choice_used)
182 .map_err(|e| XlogError::Kernel(format!("zero choice_used: {}", e)))?;
183 device
184 .memset_zeros(&mut leaf_var)
185 .map_err(|e| XlogError::Kernel(format!("zero leaf_var: {}", e)))?;
186 device
187 .memset_zeros(&mut choice_var)
188 .map_err(|e| XlogError::Kernel(format!("zero choice_var: {}", e)))?;
189 device
190 .memset_zeros(&mut node_needs_var)
191 .map_err(|e| XlogError::Kernel(format!("zero node_needs_var: {}", e)))?;
192 device
193 .memset_zeros(&mut node_var)
194 .map_err(|e| XlogError::Kernel(format!("zero node_var: {}", e)))?;
195 device
196 .memset_zeros(&mut clause_counts)
197 .map_err(|e| XlogError::Kernel(format!("zero clause_counts: {}", e)))?;
198 device
199 .memset_zeros(&mut lit_counts)
200 .map_err(|e| XlogError::Kernel(format!("zero lit_counts: {}", e)))?;
201
202 let reach_init_fn = device
203 .get_func(CNF_MODULE, cnf_kernels::CNF_REACHABILITY_INIT)
204 .ok_or_else(|| XlogError::Kernel("cnf_reachability_init kernel not found".to_string()))?;
205 let reach_bfs_fn = device
206 .get_func(CNF_MODULE, cnf_kernels::CNF_REACHABILITY_BFS)
207 .ok_or_else(|| XlogError::Kernel("cnf_reachability_bfs kernel not found".to_string()))?;
208 let mark_leaf_choice_fn = device
209 .get_func(CNF_MODULE, cnf_kernels::CNF_MARK_LEAF_CHOICE)
210 .ok_or_else(|| XlogError::Kernel("cnf_mark_leaf_choice kernel not found".to_string()))?;
211 let assign_leaf_var_fn = device
212 .get_func(CNF_MODULE, cnf_kernels::CNF_ASSIGN_LEAF_VAR)
213 .ok_or_else(|| XlogError::Kernel("cnf_assign_leaf_var kernel not found".to_string()))?;
214 let assign_choice_var_fn = device
215 .get_func(CNF_MODULE, cnf_kernels::CNF_ASSIGN_CHOICE_VAR)
216 .ok_or_else(|| XlogError::Kernel("cnf_assign_choice_var kernel not found".to_string()))?;
217 let mark_node_vars_fn = device
218 .get_func(CNF_MODULE, cnf_kernels::CNF_MARK_NODE_VARS)
219 .ok_or_else(|| XlogError::Kernel("cnf_mark_node_vars kernel not found".to_string()))?;
220 let count_clauses_fn = device
221 .get_func(CNF_MODULE, cnf_kernels::CNF_COUNT_CLAUSES)
222 .ok_or_else(|| XlogError::Kernel("cnf_count_clauses kernel not found".to_string()))?;
223 let capture_last_fn = device
224 .get_func(CNF_MODULE, cnf_kernels::CNF_CAPTURE_LAST_COUNTS)
225 .ok_or_else(|| XlogError::Kernel("cnf_capture_last_counts kernel not found".to_string()))?;
226 let leaf_choice_totals_fn = device
227 .get_func(CNF_MODULE, cnf_kernels::CNF_COMPUTE_LEAF_CHOICE_TOTALS)
228 .ok_or_else(|| {
229 XlogError::Kernel("cnf_compute_leaf_choice_totals kernel not found".to_string())
230 })?;
231 let compute_totals_fn = device
232 .get_func(CNF_MODULE, cnf_kernels::CNF_COMPUTE_TOTALS)
233 .ok_or_else(|| XlogError::Kernel("cnf_compute_totals kernel not found".to_string()))?;
234 let assign_node_var_fn = device
235 .get_func(CNF_MODULE, cnf_kernels::CNF_ASSIGN_NODE_VAR)
236 .ok_or_else(|| XlogError::Kernel("cnf_assign_node_var kernel not found".to_string()))?;
237 let emit_clauses_fn = device
238 .get_func(CNF_MODULE, cnf_kernels::CNF_EMIT_CLAUSES)
239 .ok_or_else(|| XlogError::Kernel("cnf_emit_clauses kernel not found".to_string()))?;
240 let set_clause_end_fn = device
241 .get_func(CNF_MODULE, cnf_kernels::CNF_SET_CLAUSE_END)
242 .ok_or_else(|| XlogError::Kernel("cnf_set_clause_end kernel not found".to_string()))?;
243
244 let block = 256u32;
245
246 let grid_roots = checked_grid_dim(num_roots_u32, block, "cnf_reachability_init")?;
247 unsafe {
249 reach_init_fn.clone().launch(
250 LaunchConfig {
251 grid_dim: (grid_roots, 1, 1),
252 block_dim: (block, 1, 1),
253 shared_mem_bytes: 0,
254 },
255 (
256 &roots.roots,
257 num_roots_u32,
258 num_nodes_u32,
259 &mut reachable,
260 &mut queue,
261 &mut queue_ready,
262 &mut head,
263 &mut tail,
264 &mut in_flight,
265 ),
266 )
267 }
268 .map_err(|e| XlogError::Kernel(format!("cnf_reachability_init failed: {}", e)))?;
269
270 let grid_nodes = checked_grid_dim(num_nodes_u32, block, "cnf node kernels")?;
271 unsafe {
273 reach_bfs_fn.clone().launch(
274 LaunchConfig {
275 grid_dim: (grid_nodes, 1, 1),
276 block_dim: (block, 1, 1),
277 shared_mem_bytes: 0,
278 },
279 (
280 &pir.node_type,
281 &pir.child_offsets,
282 &pir.children,
283 &pir.decision_child_false,
284 &pir.decision_child_true,
285 num_nodes_u32,
286 &mut reachable,
287 &mut queue,
288 &mut queue_ready,
289 &mut head,
290 &mut tail,
291 &mut in_flight,
292 ),
293 )
294 }
295 .map_err(|e| XlogError::Kernel(format!("cnf_reachability_bfs failed: {}", e)))?;
296
297 unsafe {
299 mark_leaf_choice_fn.clone().launch(
300 LaunchConfig {
301 grid_dim: (grid_nodes, 1, 1),
302 block_dim: (block, 1, 1),
303 shared_mem_bytes: 0,
304 },
305 (
306 &pir.node_type,
307 &pir.leaf_id,
308 &pir.decision_var,
309 &reachable,
310 num_nodes_u32,
311 leaf_cap,
312 choice_cap,
313 &mut leaf_used,
314 &mut choice_used,
315 ),
316 )
317 }
318 .map_err(|e| XlogError::Kernel(format!("cnf_mark_leaf_choice failed: {}", e)))?;
319
320 if leaf_cap > 0 {
321 device
322 .dtod_copy(&leaf_used, &mut leaf_prefix)
323 .map_err(|e| XlogError::Kernel(format!("copy leaf_used: {}", e)))?;
324 provider.exclusive_scan_u32_inplace(&mut leaf_prefix, leaf_cap)?;
325 }
326 if choice_cap > 0 {
327 device
328 .dtod_copy(&choice_used, &mut choice_prefix)
329 .map_err(|e| XlogError::Kernel(format!("copy choice_used: {}", e)))?;
330 provider.exclusive_scan_u32_inplace(&mut choice_prefix, choice_cap)?;
331 }
332
333 unsafe {
335 leaf_choice_totals_fn.clone().launch(
336 LaunchConfig {
337 grid_dim: (1, 1, 1),
338 block_dim: (1, 1, 1),
339 shared_mem_bytes: 0,
340 },
341 (
342 &leaf_prefix,
343 &leaf_used,
344 leaf_cap,
345 &choice_prefix,
346 &choice_used,
347 choice_cap,
348 &mut num_leaf,
349 &mut num_choice,
350 &mut base_choice,
351 &mut base_node,
352 &mut decision_var_limit,
353 ),
354 )
355 }
356 .map_err(|e| XlogError::Kernel(format!("cnf_compute_leaf_choice_totals failed: {}", e)))?;
357
358 if leaf_cap > 0 {
359 let grid_leaf = checked_grid_dim(leaf_cap, block, "cnf_assign_leaf_var")?;
360 unsafe {
362 assign_leaf_var_fn.clone().launch(
363 LaunchConfig {
364 grid_dim: (grid_leaf, 1, 1),
365 block_dim: (block, 1, 1),
366 shared_mem_bytes: 0,
367 },
368 (&leaf_used, &leaf_prefix, leaf_cap, &mut leaf_var),
369 )
370 }
371 .map_err(|e| XlogError::Kernel(format!("cnf_assign_leaf_var failed: {}", e)))?;
372 }
373 if choice_cap > 0 {
374 let grid_choice = checked_grid_dim(choice_cap, block, "cnf_assign_choice_var")?;
375 unsafe {
377 assign_choice_var_fn.clone().launch(
378 LaunchConfig {
379 grid_dim: (grid_choice, 1, 1),
380 block_dim: (block, 1, 1),
381 shared_mem_bytes: 0,
382 },
383 (
384 &choice_used,
385 &choice_prefix,
386 choice_cap,
387 &base_choice,
388 &mut choice_var,
389 ),
390 )
391 }
392 .map_err(|e| XlogError::Kernel(format!("cnf_assign_choice_var failed: {}", e)))?;
393 }
394
395 unsafe {
397 mark_node_vars_fn.clone().launch(
398 LaunchConfig {
399 grid_dim: (grid_nodes, 1, 1),
400 block_dim: (block, 1, 1),
401 shared_mem_bytes: 0,
402 },
403 (
404 &pir.node_type,
405 &reachable,
406 num_nodes_u32,
407 &mut node_needs_var,
408 ),
409 )
410 }
411 .map_err(|e| XlogError::Kernel(format!("cnf_mark_node_vars failed: {}", e)))?;
412
413 unsafe {
415 count_clauses_fn.clone().launch(
416 LaunchConfig {
417 grid_dim: (grid_nodes, 1, 1),
418 block_dim: (block, 1, 1),
419 shared_mem_bytes: 0,
420 },
421 (
422 &pir.node_type,
423 &pir.child_offsets,
424 &reachable,
425 num_nodes_u32,
426 &mut clause_counts,
427 &mut lit_counts,
428 ),
429 )
430 }
431 .map_err(|e| XlogError::Kernel(format!("cnf_count_clauses failed: {}", e)))?;
432
433 unsafe {
435 capture_last_fn.clone().launch(
436 LaunchConfig {
437 grid_dim: (1, 1, 1),
438 block_dim: (1, 1, 1),
439 shared_mem_bytes: 0,
440 },
441 (
442 &node_needs_var,
443 &clause_counts,
444 &lit_counts,
445 num_nodes_u32,
446 &mut node_last,
447 &mut clause_last,
448 &mut lit_last,
449 ),
450 )
451 }
452 .map_err(|e| XlogError::Kernel(format!("cnf_capture_last_counts failed: {}", e)))?;
453
454 provider.exclusive_scan_u32_inplace(&mut node_needs_var, num_nodes_u32)?;
455 provider.exclusive_scan_u32_inplace(&mut clause_counts, num_nodes_u32)?;
456 provider.exclusive_scan_u32_inplace(&mut lit_counts, num_nodes_u32)?;
457
458 let mut totals_params: Vec<*mut c_void> = vec![
459 (&node_needs_var).as_kernel_param(),
460 (&clause_counts).as_kernel_param(),
461 (&lit_counts).as_kernel_param(),
462 (&node_last).as_kernel_param(),
463 (&clause_last).as_kernel_param(),
464 (&lit_last).as_kernel_param(),
465 num_nodes_u32.as_kernel_param(),
466 (&base_node).as_kernel_param(),
467 var_cap.as_kernel_param(),
468 clause_cap.as_kernel_param(),
469 lit_cap.as_kernel_param(),
470 (&d_num_vars).as_kernel_param(),
471 (&d_num_clauses).as_kernel_param(),
472 (&d_num_lits).as_kernel_param(),
473 ];
474 unsafe {
476 compute_totals_fn.clone().launch(
477 LaunchConfig {
478 grid_dim: (1, 1, 1),
479 block_dim: (1, 1, 1),
480 shared_mem_bytes: 0,
481 },
482 &mut totals_params,
483 )
484 }
485 .map_err(|e| XlogError::Kernel(format!("cnf_compute_totals failed: {}", e)))?;
486
487 unsafe {
489 assign_node_var_fn.clone().launch(
490 LaunchConfig {
491 grid_dim: (grid_nodes, 1, 1),
492 block_dim: (block, 1, 1),
493 shared_mem_bytes: 0,
494 },
495 (
496 &pir.node_type,
497 &pir.leaf_id,
498 &reachable,
499 &node_needs_var,
500 &base_node,
501 num_nodes_u32,
502 leaf_cap,
503 &leaf_var,
504 &mut node_var,
505 ),
506 )
507 }
508 .map_err(|e| XlogError::Kernel(format!("cnf_assign_node_var failed: {}", e)))?;
509
510 let mut emit_params: Vec<*mut c_void> = vec![
511 (&pir.node_type).as_kernel_param(),
512 (&pir.child_offsets).as_kernel_param(),
513 (&pir.children).as_kernel_param(),
514 (&pir.leaf_id).as_kernel_param(),
515 (&pir.decision_var).as_kernel_param(),
516 (&pir.decision_child_false).as_kernel_param(),
517 (&pir.decision_child_true).as_kernel_param(),
518 (&reachable).as_kernel_param(),
519 (&node_var).as_kernel_param(),
520 (&leaf_var).as_kernel_param(),
521 (&choice_var).as_kernel_param(),
522 (&clause_counts).as_kernel_param(),
523 (&lit_counts).as_kernel_param(),
524 num_nodes_u32.as_kernel_param(),
525 leaf_cap.as_kernel_param(),
526 choice_cap.as_kernel_param(),
527 (&d_offsets).as_kernel_param(),
528 (&d_lits).as_kernel_param(),
529 ];
530
531 unsafe {
533 emit_clauses_fn.clone().launch(
534 LaunchConfig {
535 grid_dim: (grid_nodes, 1, 1),
536 block_dim: (block, 1, 1),
537 shared_mem_bytes: 0,
538 },
539 &mut emit_params,
540 )
541 }
542 .map_err(|e| XlogError::Kernel(format!("cnf_emit_clauses failed: {}", e)))?;
543
544 unsafe {
546 set_clause_end_fn.clone().launch(
547 LaunchConfig {
548 grid_dim: (1, 1, 1),
549 block_dim: (1, 1, 1),
550 shared_mem_bytes: 0,
551 },
552 (&mut d_offsets, &d_num_clauses, &d_num_lits),
553 )
554 }
555 .map_err(|e| XlogError::Kernel(format!("cnf_set_clause_end failed: {}", e)))?;
556 Ok(GpuCnfEncoding {
558 cnf: GpuCnf {
559 var_cap,
560 clause_cap,
561 lit_cap,
562 num_vars: d_num_vars,
563 num_clauses: d_num_clauses,
564 num_lits: d_num_lits,
565 clause_offsets: d_offsets,
566 literals: d_lits,
567 },
568 vars: GpuCnfVarTables {
569 node_var,
570 leaf_var,
571 choice_var,
572 max_var: var_cap,
573 },
574 decision_var_limit,
575 })
576}