1use std::collections::HashMap;
6
7use std::collections::{HashSet, VecDeque};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum ImageFormat {
12 Unknown,
13 Rgba32f,
14 Rgba16f,
15 R32f,
16 Rgba8,
17 R32i,
18 R32ui,
19}
20#[derive(Debug, Clone, PartialEq)]
22pub enum Decoration {
23 RelaxedPrecision,
24 SpecId(u32),
25 Block,
26 BufferBlock,
27 RowMajor,
28 ColMajor,
29 ArrayStride(u32),
30 MatrixStride(u32),
31 GlslShared,
32 GlslPacked,
33 CPacked,
34 BuiltIn(BuiltIn),
35 NoPerspective,
36 Flat,
37 Patch,
38 Centroid,
39 Sample,
40 Invariant,
41 Restrict,
42 Aliased,
43 Volatile,
44 Constant,
45 Coherent,
46 NonWritable,
47 NonReadable,
48 Uniform,
49 UniformId(u32),
50 SaturatedConversion,
51 Stream(u32),
52 Location(u32),
53 Component(u32),
54 Index(u32),
55 Binding(u32),
56 DescriptorSet(u32),
57 Offset(u32),
58 XfbBuffer(u32),
59 XfbStride(u32),
60 FuncParamAttr(u32),
61 FPRoundingMode(u32),
62 FPFastMathMode(u32),
63 LinkageAttributes(String, u32),
64 NoContraction,
65 InputAttachmentIndex(u32),
66 Alignment(u32),
67 MaxByteOffset(u64),
68 AlignmentId(u32),
69 MaxByteOffsetId(u32),
70}
71#[derive(Debug, Clone, PartialEq)]
73pub enum AddressingModel {
74 Logical,
75 Physical32,
76 Physical64,
77 PhysicalStorageBuffer64,
78}
79#[allow(dead_code)]
80#[derive(Debug, Clone)]
81pub struct SPIRVPassConfig {
82 pub phase: SPIRVPassPhase,
83 pub enabled: bool,
84 pub max_iterations: u32,
85 pub debug_output: bool,
86 pub pass_name: String,
87}
88impl SPIRVPassConfig {
89 #[allow(dead_code)]
90 pub fn new(name: impl Into<String>, phase: SPIRVPassPhase) -> Self {
91 SPIRVPassConfig {
92 phase,
93 enabled: true,
94 max_iterations: 10,
95 debug_output: false,
96 pass_name: name.into(),
97 }
98 }
99 #[allow(dead_code)]
100 pub fn disabled(mut self) -> Self {
101 self.enabled = false;
102 self
103 }
104 #[allow(dead_code)]
105 pub fn with_debug(mut self) -> Self {
106 self.debug_output = true;
107 self
108 }
109 #[allow(dead_code)]
110 pub fn max_iter(mut self, n: u32) -> Self {
111 self.max_iterations = n;
112 self
113 }
114}
115#[derive(Debug, Clone, PartialEq)]
121pub struct SpirVInstruction {
122 pub result_id: Option<u32>,
124 pub result_type_id: Option<u32>,
126 pub opcode: SpirVOp,
128 pub operands: Vec<u32>,
130}
131impl SpirVInstruction {
132 pub fn with_result(
134 result_id: u32,
135 result_type_id: u32,
136 opcode: SpirVOp,
137 operands: Vec<u32>,
138 ) -> Self {
139 Self {
140 result_id: Some(result_id),
141 result_type_id: Some(result_type_id),
142 opcode,
143 operands,
144 }
145 }
146 pub fn no_result(opcode: SpirVOp, operands: Vec<u32>) -> Self {
148 Self {
149 result_id: None,
150 result_type_id: None,
151 opcode,
152 operands,
153 }
154 }
155 pub fn emit_text(&self) -> String {
157 let result_part = match (self.result_id, self.result_type_id) {
158 (Some(rid), Some(rtid)) => format!("%{} = %{} ", rid, rtid),
159 _ => String::new(),
160 };
161 let ops: Vec<String> = self.operands.iter().map(|o| format!("%{}", o)).collect();
162 if ops.is_empty() {
163 format!("{}{}", result_part, self.opcode)
164 } else {
165 format!("{}{} {}", result_part, self.opcode, ops.join(" "))
166 }
167 }
168 pub fn word_count(&self) -> u32 {
170 let base = 1;
171 let result_words = if self.result_id.is_some() { 2 } else { 0 };
172 base + result_words + self.operands.len() as u32
173 }
174}
175#[allow(dead_code)]
176pub struct SPIRVPassRegistry {
177 pub(super) configs: Vec<SPIRVPassConfig>,
178 pub(super) stats: std::collections::HashMap<String, SPIRVPassStats>,
179}
180impl SPIRVPassRegistry {
181 #[allow(dead_code)]
182 pub fn new() -> Self {
183 SPIRVPassRegistry {
184 configs: Vec::new(),
185 stats: std::collections::HashMap::new(),
186 }
187 }
188 #[allow(dead_code)]
189 pub fn register(&mut self, config: SPIRVPassConfig) {
190 self.stats
191 .insert(config.pass_name.clone(), SPIRVPassStats::new());
192 self.configs.push(config);
193 }
194 #[allow(dead_code)]
195 pub fn enabled_passes(&self) -> Vec<&SPIRVPassConfig> {
196 self.configs.iter().filter(|c| c.enabled).collect()
197 }
198 #[allow(dead_code)]
199 pub fn get_stats(&self, name: &str) -> Option<&SPIRVPassStats> {
200 self.stats.get(name)
201 }
202 #[allow(dead_code)]
203 pub fn total_passes(&self) -> usize {
204 self.configs.len()
205 }
206 #[allow(dead_code)]
207 pub fn enabled_count(&self) -> usize {
208 self.enabled_passes().len()
209 }
210 #[allow(dead_code)]
211 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
212 if let Some(stats) = self.stats.get_mut(name) {
213 stats.record_run(changes, time_ms, iter);
214 }
215 }
216}
217#[derive(Debug, Clone, PartialEq)]
219pub enum ExecutionModel {
220 Vertex,
221 TessellationControl,
222 TessellationEvaluation,
223 Geometry,
224 Fragment,
225 GLCompute,
226 Kernel,
227 TaskNV,
228 MeshNV,
229 RayGenerationKHR,
230 IntersectionKHR,
231 AnyHitKHR,
232 ClosestHitKHR,
233 MissKHR,
234 CallableKHR,
235}
236#[allow(dead_code)]
237#[derive(Debug, Clone)]
238pub struct SPIRVCacheEntry {
239 pub key: String,
240 pub data: Vec<u8>,
241 pub timestamp: u64,
242 pub valid: bool,
243}
244#[derive(Debug, Clone)]
246pub struct SpirVBasicBlock {
247 pub label_id: u32,
249 pub instructions: Vec<SpirVInstruction>,
251}
252impl SpirVBasicBlock {
253 pub fn new(label_id: u32) -> Self {
255 Self {
256 label_id,
257 instructions: Vec::new(),
258 }
259 }
260 pub fn push(&mut self, instr: SpirVInstruction) {
262 self.instructions.push(instr);
263 }
264 pub fn emit_text(&self) -> String {
266 let mut lines = Vec::new();
267 lines.push(format!("%{} = OpLabel", self.label_id));
268 for instr in &self.instructions {
269 lines.push(format!(" {}", instr.emit_text()));
270 }
271 lines.join("\n")
272 }
273 pub fn instr_count(&self) -> usize {
275 self.instructions.len()
276 }
277}
278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
280pub enum SpirVCapability {
281 Matrix,
282 Shader,
283 Geometry,
284 Tessellation,
285 Addresses,
286 Linkage,
287 Kernel,
288 Vector16,
289 Float16Buffer,
290 Float16,
291 Float64,
292 Int64,
293 Int64Atomics,
294 ImageBasic,
295 ImageReadWrite,
296 ImageMipmap,
297 Sampled1D,
298 Image1D,
299 SampledCubeArray,
300 SampledBuffer,
301 ImageBuffer,
302 ImageMSArray,
303 StorageImageExtendedFormats,
304 ImageQuery,
305 DerivativeControl,
306 InterpolationFunction,
307 TransformFeedback,
308 GeometryStreams,
309 StorageImageReadWithoutFormat,
310 StorageImageWriteWithoutFormat,
311 MultiViewport,
312 SubgroupDispatch,
313 NamedBarrier,
314 PipeStorage,
315 GroupNonUniform,
316 GroupNonUniformVote,
317 GroupNonUniformArithmetic,
318 GroupNonUniformBallot,
319 GroupNonUniformShuffle,
320 GroupNonUniformShuffleRelative,
321 VulkanMemoryModel,
322 PhysicalStorageBufferAddresses,
323 DemoteToHelperInvocation,
324 AtomicFloat32AddExt,
325 AtomicFloat64AddExt,
326}
327#[allow(dead_code)]
328#[derive(Debug, Clone)]
329pub struct SPIRVAnalysisCache {
330 pub(super) entries: std::collections::HashMap<String, SPIRVCacheEntry>,
331 pub(super) max_size: usize,
332 pub(super) hits: u64,
333 pub(super) misses: u64,
334}
335impl SPIRVAnalysisCache {
336 #[allow(dead_code)]
337 pub fn new(max_size: usize) -> Self {
338 SPIRVAnalysisCache {
339 entries: std::collections::HashMap::new(),
340 max_size,
341 hits: 0,
342 misses: 0,
343 }
344 }
345 #[allow(dead_code)]
346 pub fn get(&mut self, key: &str) -> Option<&SPIRVCacheEntry> {
347 if self.entries.contains_key(key) {
348 self.hits += 1;
349 self.entries.get(key)
350 } else {
351 self.misses += 1;
352 None
353 }
354 }
355 #[allow(dead_code)]
356 pub fn insert(&mut self, key: String, data: Vec<u8>) {
357 if self.entries.len() >= self.max_size {
358 if let Some(oldest) = self.entries.keys().next().cloned() {
359 self.entries.remove(&oldest);
360 }
361 }
362 self.entries.insert(
363 key.clone(),
364 SPIRVCacheEntry {
365 key,
366 data,
367 timestamp: 0,
368 valid: true,
369 },
370 );
371 }
372 #[allow(dead_code)]
373 pub fn invalidate(&mut self, key: &str) {
374 if let Some(entry) = self.entries.get_mut(key) {
375 entry.valid = false;
376 }
377 }
378 #[allow(dead_code)]
379 pub fn clear(&mut self) {
380 self.entries.clear();
381 }
382 #[allow(dead_code)]
383 pub fn hit_rate(&self) -> f64 {
384 let total = self.hits + self.misses;
385 if total == 0 {
386 return 0.0;
387 }
388 self.hits as f64 / total as f64
389 }
390 #[allow(dead_code)]
391 pub fn size(&self) -> usize {
392 self.entries.len()
393 }
394}
395#[derive(Debug, Clone, PartialEq)]
397pub enum MemoryModel {
398 Simple,
399 GLSL450,
400 OpenCL,
401 Vulkan,
402}
403#[allow(dead_code)]
404#[derive(Debug, Clone)]
405pub struct SPIRVDepGraph {
406 pub(super) nodes: Vec<u32>,
407 pub(super) edges: Vec<(u32, u32)>,
408}
409impl SPIRVDepGraph {
410 #[allow(dead_code)]
411 pub fn new() -> Self {
412 SPIRVDepGraph {
413 nodes: Vec::new(),
414 edges: Vec::new(),
415 }
416 }
417 #[allow(dead_code)]
418 pub fn add_node(&mut self, id: u32) {
419 if !self.nodes.contains(&id) {
420 self.nodes.push(id);
421 }
422 }
423 #[allow(dead_code)]
424 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
425 self.add_node(dep);
426 self.add_node(dependent);
427 self.edges.push((dep, dependent));
428 }
429 #[allow(dead_code)]
430 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
431 self.edges
432 .iter()
433 .filter(|(d, _)| *d == node)
434 .map(|(_, dep)| *dep)
435 .collect()
436 }
437 #[allow(dead_code)]
438 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
439 self.edges
440 .iter()
441 .filter(|(_, dep)| *dep == node)
442 .map(|(d, _)| *d)
443 .collect()
444 }
445 #[allow(dead_code)]
446 pub fn topological_sort(&self) -> Vec<u32> {
447 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
448 for &n in &self.nodes {
449 in_degree.insert(n, 0);
450 }
451 for (_, dep) in &self.edges {
452 *in_degree.entry(*dep).or_insert(0) += 1;
453 }
454 let mut queue: std::collections::VecDeque<u32> = self
455 .nodes
456 .iter()
457 .filter(|&&n| in_degree[&n] == 0)
458 .copied()
459 .collect();
460 let mut result = Vec::new();
461 while let Some(node) = queue.pop_front() {
462 result.push(node);
463 for dep in self.dependents_of(node) {
464 let cnt = in_degree.entry(dep).or_insert(0);
465 *cnt = cnt.saturating_sub(1);
466 if *cnt == 0 {
467 queue.push_back(dep);
468 }
469 }
470 }
471 result
472 }
473 #[allow(dead_code)]
474 pub fn has_cycle(&self) -> bool {
475 self.topological_sort().len() < self.nodes.len()
476 }
477}
478#[allow(dead_code)]
479#[derive(Debug, Clone, Default)]
480pub struct SPIRVPassStats {
481 pub total_runs: u32,
482 pub successful_runs: u32,
483 pub total_changes: u64,
484 pub time_ms: u64,
485 pub iterations_used: u32,
486}
487impl SPIRVPassStats {
488 #[allow(dead_code)]
489 pub fn new() -> Self {
490 Self::default()
491 }
492 #[allow(dead_code)]
493 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
494 self.total_runs += 1;
495 self.successful_runs += 1;
496 self.total_changes += changes;
497 self.time_ms += time_ms;
498 self.iterations_used = iterations;
499 }
500 #[allow(dead_code)]
501 pub fn average_changes_per_run(&self) -> f64 {
502 if self.total_runs == 0 {
503 return 0.0;
504 }
505 self.total_changes as f64 / self.total_runs as f64
506 }
507 #[allow(dead_code)]
508 pub fn success_rate(&self) -> f64 {
509 if self.total_runs == 0 {
510 return 0.0;
511 }
512 self.successful_runs as f64 / self.total_runs as f64
513 }
514 #[allow(dead_code)]
515 pub fn format_summary(&self) -> String {
516 format!(
517 "Runs: {}/{}, Changes: {}, Time: {}ms",
518 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
519 )
520 }
521}
522#[derive(Debug, Clone, PartialEq)]
526pub enum SpirVType {
527 Void,
529 Bool,
531 Int {
533 width: u32,
535 signed: bool,
537 },
538 Float {
540 width: u32,
542 },
543 Vector {
545 element: Box<SpirVType>,
547 count: u32,
549 },
550 Matrix {
552 column_type: Box<SpirVType>,
554 column_count: u32,
556 },
557 Array {
559 element: Box<SpirVType>,
561 length: u32,
563 },
564 RuntimeArray(Box<SpirVType>),
566 Struct(Vec<SpirVType>),
568 Pointer {
570 storage_class: StorageClass,
572 pointee: Box<SpirVType>,
574 },
575 Function {
577 return_type: Box<SpirVType>,
579 param_types: Vec<SpirVType>,
581 },
582 Image {
584 sampled_type: Box<SpirVType>,
586 dim: ImageDim,
588 depth: u32,
590 arrayed: u32,
592 ms: u32,
594 sampled: u32,
596 format: ImageFormat,
598 },
599 Sampler,
601 SampledImage(Box<SpirVType>),
603}
604#[derive(Debug)]
609pub struct SpirVBackend {
610 pub module: SpirVModule,
612 pub(super) type_cache: HashMap<String, u32>,
614 pub(super) symbol_table: HashMap<String, u32>,
616 pub(super) glsl_ext_id: Option<u32>,
618 pub(super) void_type_id: Option<u32>,
620 pub(super) bool_type_id: Option<u32>,
622}
623impl SpirVBackend {
624 pub fn new() -> Self {
626 Self {
627 module: SpirVModule::new(),
628 type_cache: HashMap::new(),
629 symbol_table: HashMap::new(),
630 glsl_ext_id: None,
631 void_type_id: None,
632 bool_type_id: None,
633 }
634 }
635 pub fn configure_for_vulkan(&mut self) {
637 self.module.add_capability(SpirVCapability::Shader);
638 self.module.memory_model = (AddressingModel::Logical, MemoryModel::GLSL450);
639 let glsl_id = self.module.import_ext_inst("GLSL.std.450");
640 self.glsl_ext_id = Some(glsl_id);
641 }
642 pub fn configure_for_opencl(&mut self) {
644 self.module.add_capability(SpirVCapability::Kernel);
645 self.module.add_capability(SpirVCapability::Addresses);
646 self.module.memory_model = (AddressingModel::Physical64, MemoryModel::OpenCL);
647 }
648 pub fn get_void_type(&mut self) -> u32 {
650 if let Some(id) = self.void_type_id {
651 return id;
652 }
653 let id = self.module.fresh_id();
654 self.module.add_type(SpirVInstruction::with_result(
655 id,
656 0,
657 SpirVOp::Capability(SpirVCapability::Shader),
658 vec![],
659 ));
660 self.void_type_id = Some(id);
661 id
662 }
663 pub fn declare_int_type(&mut self, width: u32, signed: bool) -> u32 {
665 let key = format!("int_{}_{}", width, if signed { "s" } else { "u" });
666 if let Some(&id) = self.type_cache.get(&key) {
667 return id;
668 }
669 let id = self.module.fresh_id();
670 self.module.add_type(SpirVInstruction::with_result(
671 id,
672 0,
673 SpirVOp::Constant(width as u64),
674 vec![if signed { 1 } else { 0 }],
675 ));
676 self.type_cache.insert(key, id);
677 id
678 }
679 pub fn declare_float_type(&mut self, width: u32) -> u32 {
681 let key = format!("float_{}", width);
682 if let Some(&id) = self.type_cache.get(&key) {
683 return id;
684 }
685 let id = self.module.fresh_id();
686 self.module.add_type(SpirVInstruction::with_result(
687 id,
688 0,
689 SpirVOp::Constant(width as u64),
690 vec![],
691 ));
692 self.type_cache.insert(key, id);
693 id
694 }
695 pub fn declare_vector_type(&mut self, element_type_id: u32, count: u32) -> u32 {
697 let key = format!("vec_{}_{}", element_type_id, count);
698 if let Some(&id) = self.type_cache.get(&key) {
699 return id;
700 }
701 let id = self.module.fresh_id();
702 self.module.add_type(SpirVInstruction::with_result(
703 id,
704 0,
705 SpirVOp::CompositeConstruct,
706 vec![element_type_id, count],
707 ));
708 self.type_cache.insert(key, id);
709 id
710 }
711 pub fn declare_pointer_type(
713 &mut self,
714 storage_class: StorageClass,
715 pointee_type_id: u32,
716 ) -> u32 {
717 let key = format!("ptr_{:?}_{}", storage_class, pointee_type_id);
718 if let Some(&id) = self.type_cache.get(&key) {
719 return id;
720 }
721 let id = self.module.fresh_id();
722 self.module.add_type(SpirVInstruction::with_result(
723 id,
724 0,
725 SpirVOp::Variable(storage_class),
726 vec![pointee_type_id],
727 ));
728 self.type_cache.insert(key, id);
729 id
730 }
731 pub fn declare_function_type(&mut self, return_type_id: u32, param_type_ids: Vec<u32>) -> u32 {
733 let key = format!("fn_{}_[{}]", return_type_id, {
734 let s: Vec<String> = param_type_ids.iter().map(|i| i.to_string()).collect();
735 s.join(",")
736 });
737 if let Some(&id) = self.type_cache.get(&key) {
738 return id;
739 }
740 let id = self.module.fresh_id();
741 let mut operands = vec![return_type_id];
742 operands.extend_from_slice(¶m_type_ids);
743 self.module.add_type(SpirVInstruction::with_result(
744 id,
745 0,
746 SpirVOp::Function,
747 operands,
748 ));
749 self.type_cache.insert(key, id);
750 id
751 }
752 pub fn declare_int_constant(&mut self, type_id: u32, value: u64) -> u32 {
754 let id = self.module.fresh_id();
755 self.module.add_constant(SpirVInstruction::with_result(
756 id,
757 type_id,
758 SpirVOp::Constant(value),
759 vec![value as u32, (value >> 32) as u32],
760 ));
761 id
762 }
763 pub fn declare_float_constant(&mut self, type_id: u32, value: f32) -> u32 {
765 let id = self.module.fresh_id();
766 let bits = value.to_bits();
767 self.module.add_constant(SpirVInstruction::with_result(
768 id,
769 type_id,
770 SpirVOp::Constant(bits as u64),
771 vec![bits],
772 ));
773 id
774 }
775 pub fn declare_bool_constant(&mut self, type_id: u32, value: bool) -> u32 {
777 let id = self.module.fresh_id();
778 let op = if value {
779 SpirVOp::ConstantTrue
780 } else {
781 SpirVOp::ConstantFalse
782 };
783 self.module
784 .add_constant(SpirVInstruction::with_result(id, type_id, op, vec![]));
785 id
786 }
787 pub fn declare_global_variable(
789 &mut self,
790 name: impl Into<String>,
791 type_id: u32,
792 storage_class: StorageClass,
793 decorations: Vec<Decoration>,
794 ) -> u32 {
795 let name = name.into();
796 let id = self.module.fresh_id();
797 self.module.set_name(id, name.clone());
798 for deco in decorations {
799 self.module.decorate(id, deco);
800 }
801 self.module.add_global_var(SpirVInstruction::with_result(
802 id,
803 type_id,
804 SpirVOp::Variable(storage_class),
805 vec![],
806 ));
807 self.symbol_table.insert(name, id);
808 id
809 }
810 pub fn begin_function(
812 &mut self,
813 name: impl Into<String>,
814 return_type_id: u32,
815 param_type_ids: Vec<u32>,
816 ) -> SpirVFunction {
817 let name = name.into();
818 let func_id = self.module.fresh_id();
819 let func_type_id = self.declare_function_type(return_type_id, param_type_ids.clone());
820 self.module.set_name(func_id, name.clone());
821 self.symbol_table.insert(name.clone(), func_id);
822 let mut func = SpirVFunction::new(func_id, Some(name), return_type_id, func_type_id);
823 for &pt in ¶m_type_ids {
824 let pid = self.module.fresh_id();
825 func.add_param(pid, pt);
826 }
827 func
828 }
829 pub fn finish_function(&mut self, func: SpirVFunction) {
831 self.module.add_function(func);
832 }
833 pub fn emit_text(&self) -> String {
835 self.module.emit_text()
836 }
837 pub fn emit_binary_header(&self) -> Vec<u32> {
839 vec![
840 0x0723_0203,
841 self.module.version,
842 self.module.generator,
843 self.module.bound,
844 0,
845 ]
846 }
847 pub fn function_count(&self) -> usize {
849 self.module.functions.len()
850 }
851 pub fn lookup_symbol(&self, name: &str) -> Option<u32> {
853 self.symbol_table.get(name).copied()
854 }
855 pub fn emit_compute_kernel(
857 &mut self,
858 kernel_name: impl Into<String>,
859 local_size_x: u32,
860 local_size_y: u32,
861 local_size_z: u32,
862 ) -> u32 {
863 let kernel_name = kernel_name.into();
864 self.configure_for_vulkan();
865 self.module.add_capability(SpirVCapability::Shader);
866 let void_id = self.module.fresh_id();
867 let func_type_id = self.module.fresh_id();
868 let func_id = self.module.fresh_id();
869 let entry_block_id = self.module.fresh_id();
870 self.module.set_name(func_id, kernel_name.clone());
871 let mut func =
872 SpirVFunction::new(func_id, Some(kernel_name.clone()), void_id, func_type_id);
873 func.set_entry_point(ExecutionModel::GLCompute);
874 let mut entry_block = SpirVBasicBlock::new(entry_block_id);
875 entry_block.push(SpirVInstruction::no_result(SpirVOp::Return, vec![]));
876 func.add_block(entry_block);
877 self.module
878 .add_entry_point(ExecutionModel::GLCompute, func_id, &kernel_name, vec![]);
879 self.module.add_execution_mode(
880 func_id,
881 ExecutionMode::LocalSize(local_size_x, local_size_y, local_size_z),
882 );
883 self.module.add_function(func);
884 self.symbol_table.insert(kernel_name, func_id);
885 func_id
886 }
887}
888#[derive(Debug, Clone)]
894pub struct SpirVModule {
895 pub version: u32,
897 pub generator: u32,
899 pub bound: u32,
901 pub capabilities: Vec<SpirVCapability>,
903 pub extensions: Vec<String>,
905 pub ext_inst_imports: HashMap<String, u32>,
907 pub memory_model: (AddressingModel, MemoryModel),
909 pub entry_points: Vec<(ExecutionModel, u32, String, Vec<u32>)>,
911 pub execution_modes: Vec<(u32, ExecutionMode)>,
913 pub debug_names: HashMap<u32, String>,
915 pub decorations: HashMap<u32, Vec<Decoration>>,
917 pub types: Vec<SpirVInstruction>,
919 pub constants: Vec<SpirVInstruction>,
921 pub global_vars: Vec<SpirVInstruction>,
923 pub functions: Vec<SpirVFunction>,
925}
926impl SpirVModule {
927 pub fn new() -> Self {
929 Self {
930 version: (1 << 16) | (6 << 8),
931 generator: 0x000D_0001,
932 bound: 1,
933 capabilities: Vec::new(),
934 extensions: Vec::new(),
935 ext_inst_imports: HashMap::new(),
936 memory_model: (AddressingModel::Logical, MemoryModel::GLSL450),
937 entry_points: Vec::new(),
938 execution_modes: Vec::new(),
939 debug_names: HashMap::new(),
940 decorations: HashMap::new(),
941 types: Vec::new(),
942 constants: Vec::new(),
943 global_vars: Vec::new(),
944 functions: Vec::new(),
945 }
946 }
947 pub fn fresh_id(&mut self) -> u32 {
949 let id = self.bound;
950 self.bound += 1;
951 id
952 }
953 pub fn add_capability(&mut self, cap: SpirVCapability) {
955 if !self.capabilities.contains(&cap) {
956 self.capabilities.push(cap);
957 }
958 }
959 pub fn add_extension(&mut self, ext: impl Into<String>) {
961 let ext = ext.into();
962 if !self.extensions.contains(&ext) {
963 self.extensions.push(ext);
964 }
965 }
966 pub fn import_ext_inst(&mut self, name: impl Into<String>) -> u32 {
968 let name = name.into();
969 if let Some(&id) = self.ext_inst_imports.get(&name) {
970 return id;
971 }
972 let id = self.fresh_id();
973 self.ext_inst_imports.insert(name, id);
974 id
975 }
976 pub fn add_entry_point(
978 &mut self,
979 model: ExecutionModel,
980 func_id: u32,
981 name: impl Into<String>,
982 interface_vars: Vec<u32>,
983 ) {
984 self.entry_points
985 .push((model, func_id, name.into(), interface_vars));
986 }
987 pub fn add_execution_mode(&mut self, func_id: u32, mode: ExecutionMode) {
989 self.execution_modes.push((func_id, mode));
990 }
991 pub fn set_name(&mut self, id: u32, name: impl Into<String>) {
993 self.debug_names.insert(id, name.into());
994 }
995 pub fn decorate(&mut self, id: u32, decoration: Decoration) {
997 self.decorations.entry(id).or_default().push(decoration);
998 }
999 pub fn add_type(&mut self, instr: SpirVInstruction) {
1001 self.types.push(instr);
1002 }
1003 pub fn add_constant(&mut self, instr: SpirVInstruction) {
1005 self.constants.push(instr);
1006 }
1007 pub fn add_global_var(&mut self, instr: SpirVInstruction) {
1009 self.global_vars.push(instr);
1010 }
1011 pub fn add_function(&mut self, func: SpirVFunction) {
1013 self.functions.push(func);
1014 }
1015 pub fn emit_text(&self) -> String {
1017 let mut lines = Vec::new();
1018 lines.push("; SPIR-V Module (OxiLean codegen)".to_string());
1019 lines.push(format!(
1020 "; Version: {}.{}",
1021 (self.version >> 16) & 0xFF,
1022 (self.version >> 8) & 0xFF
1023 ));
1024 lines.push(format!("; Bound: {}", self.bound));
1025 lines.push(String::new());
1026 for cap in &self.capabilities {
1027 lines.push(format!("OpCapability {:?}", cap));
1028 }
1029 for ext in &self.extensions {
1030 lines.push(format!("OpExtension \"{}\"", ext));
1031 }
1032 let mut sorted_imports: Vec<(&String, &u32)> = self.ext_inst_imports.iter().collect();
1033 sorted_imports.sort_by_key(|(_, &id)| id);
1034 for (name, id) in sorted_imports {
1035 lines.push(format!("%{} = OpExtInstImport \"{}\"", id, name));
1036 }
1037 lines.push(format!(
1038 "OpMemoryModel {:?} {:?}",
1039 self.memory_model.0, self.memory_model.1
1040 ));
1041 for (model, func_id, name, iface) in &self.entry_points {
1042 let iface_str: Vec<String> = iface.iter().map(|id| format!("%{}", id)).collect();
1043 lines.push(format!(
1044 "OpEntryPoint {:?} %{} \"{}\" {}",
1045 model,
1046 func_id,
1047 name,
1048 iface_str.join(" ")
1049 ));
1050 }
1051 for (func_id, mode) in &self.execution_modes {
1052 lines.push(format!("OpExecutionMode %{} {:?}", func_id, mode));
1053 }
1054 lines.push(String::new());
1055 lines.push("; Debug names".to_string());
1056 let mut sorted_names: Vec<(&u32, &String)> = self.debug_names.iter().collect();
1057 sorted_names.sort_by_key(|(&id, _)| id);
1058 for (id, name) in sorted_names {
1059 lines.push(format!("OpName %{} \"{}\"", id, name));
1060 }
1061 lines.push(String::new());
1062 lines.push("; Decorations".to_string());
1063 let mut sorted_decos: Vec<(&u32, &Vec<Decoration>)> = self.decorations.iter().collect();
1064 sorted_decos.sort_by_key(|(&id, _)| id);
1065 for (id, decos) in sorted_decos {
1066 for deco in decos {
1067 lines.push(format!("OpDecorate %{} {:?}", id, deco));
1068 }
1069 }
1070 lines.push(String::new());
1071 lines.push("; Types".to_string());
1072 for ty in &self.types {
1073 lines.push(ty.emit_text());
1074 }
1075 lines.push(String::new());
1076 lines.push("; Constants".to_string());
1077 for c in &self.constants {
1078 lines.push(c.emit_text());
1079 }
1080 lines.push(String::new());
1081 lines.push("; Global variables".to_string());
1082 for gv in &self.global_vars {
1083 lines.push(gv.emit_text());
1084 }
1085 lines.push(String::new());
1086 lines.push("; Functions".to_string());
1087 for func in &self.functions {
1088 lines.push(func.emit_text());
1089 lines.push(String::new());
1090 }
1091 lines.join("\n")
1092 }
1093 pub fn estimate_word_count(&self) -> u32 {
1095 let mut count = 5u32;
1096 for ty in &self.types {
1097 count += ty.word_count();
1098 }
1099 for c in &self.constants {
1100 count += c.word_count();
1101 }
1102 for gv in &self.global_vars {
1103 count += gv.word_count();
1104 }
1105 for func in &self.functions {
1106 count += 5 + 1;
1107 count += func.params.len() as u32 * 3;
1108 for block in &func.blocks {
1109 count += 2;
1110 for instr in &block.instructions {
1111 count += instr.word_count();
1112 }
1113 }
1114 }
1115 count
1116 }
1117}
1118#[allow(dead_code)]
1119#[derive(Debug, Clone)]
1120pub struct SPIRVLivenessInfo {
1121 pub live_in: Vec<std::collections::HashSet<u32>>,
1122 pub live_out: Vec<std::collections::HashSet<u32>>,
1123 pub defs: Vec<std::collections::HashSet<u32>>,
1124 pub uses: Vec<std::collections::HashSet<u32>>,
1125}
1126impl SPIRVLivenessInfo {
1127 #[allow(dead_code)]
1128 pub fn new(block_count: usize) -> Self {
1129 SPIRVLivenessInfo {
1130 live_in: vec![std::collections::HashSet::new(); block_count],
1131 live_out: vec![std::collections::HashSet::new(); block_count],
1132 defs: vec![std::collections::HashSet::new(); block_count],
1133 uses: vec![std::collections::HashSet::new(); block_count],
1134 }
1135 }
1136 #[allow(dead_code)]
1137 pub fn add_def(&mut self, block: usize, var: u32) {
1138 if block < self.defs.len() {
1139 self.defs[block].insert(var);
1140 }
1141 }
1142 #[allow(dead_code)]
1143 pub fn add_use(&mut self, block: usize, var: u32) {
1144 if block < self.uses.len() {
1145 self.uses[block].insert(var);
1146 }
1147 }
1148 #[allow(dead_code)]
1149 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1150 self.live_in
1151 .get(block)
1152 .map(|s| s.contains(&var))
1153 .unwrap_or(false)
1154 }
1155 #[allow(dead_code)]
1156 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1157 self.live_out
1158 .get(block)
1159 .map(|s| s.contains(&var))
1160 .unwrap_or(false)
1161 }
1162}
1163#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1165pub enum StorageClass {
1166 Uniform,
1168 StorageBuffer,
1170 PushConstant,
1172 Input,
1174 Output,
1176 Function,
1178 Private,
1180 Workgroup,
1182 CrossWorkgroup,
1184 Image,
1186 Generic,
1188}
1189#[derive(Debug, Clone, PartialEq)]
1191pub enum BuiltIn {
1192 Position,
1193 PointSize,
1194 ClipDistance,
1195 CullDistance,
1196 VertexId,
1197 InstanceId,
1198 PrimitiveId,
1199 InvocationId,
1200 Layer,
1201 ViewportIndex,
1202 TessLevelOuter,
1203 TessLevelInner,
1204 TessCoord,
1205 PatchVertices,
1206 FragCoord,
1207 PointCoord,
1208 FrontFacing,
1209 SampleId,
1210 SamplePosition,
1211 SampleMask,
1212 FragDepth,
1213 HelperInvocation,
1214 NumWorkgroups,
1215 WorkgroupSize,
1216 WorkgroupId,
1217 LocalInvocationId,
1218 GlobalInvocationId,
1219 LocalInvocationIndex,
1220 WorkDim,
1221 GlobalSize,
1222 EnqueuedWorkgroupSize,
1223 GlobalOffset,
1224 GlobalLinearId,
1225 SubgroupSize,
1226 SubgroupMaxSize,
1227 NumSubgroups,
1228 NumEnqueuedSubgroups,
1229 SubgroupId,
1230 SubgroupLocalInvocationId,
1231 VertexIndex,
1232 InstanceIndex,
1233}
1234#[derive(Debug, Clone, PartialEq)]
1236pub enum GlslStd450Op {
1237 Round,
1238 RoundEven,
1239 Trunc,
1240 FAbs,
1241 SAbs,
1242 FSign,
1243 SSign,
1244 Floor,
1245 Ceil,
1246 Fract,
1247 Radians,
1248 Degrees,
1249 Sin,
1250 Cos,
1251 Tan,
1252 Asin,
1253 Acos,
1254 Atan,
1255 Atan2,
1256 Sinh,
1257 Cosh,
1258 Exp,
1259 Log,
1260 Exp2,
1261 Log2,
1262 Sqrt,
1263 InverseSqrt,
1264 Pow,
1265 FMin,
1266 UMin,
1267 SMin,
1268 FMax,
1269 UMax,
1270 SMax,
1271 FClamp,
1272 UClamp,
1273 SClamp,
1274 FMix,
1275 Step,
1276 SmoothStep,
1277 Fma,
1278 Length,
1279 Distance,
1280 Cross,
1281 Normalize,
1282 Reflect,
1283 Refract,
1284 FaceForward,
1285 MatrixInverse,
1286 ModfStruct,
1287 FrexpStruct,
1288 LdexpStruct,
1289 PackSnorm4x8,
1290 PackUnorm4x8,
1291 UnpackSnorm4x8,
1292 UnpackUnorm4x8,
1293}
1294#[allow(dead_code)]
1295#[derive(Debug, Clone)]
1296pub struct SPIRVDominatorTree {
1297 pub idom: Vec<Option<u32>>,
1298 pub dom_children: Vec<Vec<u32>>,
1299 pub dom_depth: Vec<u32>,
1300}
1301impl SPIRVDominatorTree {
1302 #[allow(dead_code)]
1303 pub fn new(size: usize) -> Self {
1304 SPIRVDominatorTree {
1305 idom: vec![None; size],
1306 dom_children: vec![Vec::new(); size],
1307 dom_depth: vec![0; size],
1308 }
1309 }
1310 #[allow(dead_code)]
1311 pub fn set_idom(&mut self, node: usize, idom: u32) {
1312 self.idom[node] = Some(idom);
1313 }
1314 #[allow(dead_code)]
1315 pub fn dominates(&self, a: usize, b: usize) -> bool {
1316 if a == b {
1317 return true;
1318 }
1319 let mut cur = b;
1320 loop {
1321 match self.idom[cur] {
1322 Some(parent) if parent as usize == a => return true,
1323 Some(parent) if parent as usize == cur => return false,
1324 Some(parent) => cur = parent as usize,
1325 None => return false,
1326 }
1327 }
1328 }
1329 #[allow(dead_code)]
1330 pub fn depth(&self, node: usize) -> u32 {
1331 self.dom_depth.get(node).copied().unwrap_or(0)
1332 }
1333}
1334#[derive(Debug, Clone, PartialEq, Eq)]
1336pub enum ImageDim {
1337 Dim1D,
1338 Dim2D,
1339 Dim3D,
1340 Cube,
1341 Rect,
1342 Buffer,
1343 SubpassData,
1344}
1345#[allow(dead_code)]
1346pub struct SPIRVConstantFoldingHelper;
1347impl SPIRVConstantFoldingHelper {
1348 #[allow(dead_code)]
1349 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1350 a.checked_add(b)
1351 }
1352 #[allow(dead_code)]
1353 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1354 a.checked_sub(b)
1355 }
1356 #[allow(dead_code)]
1357 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1358 a.checked_mul(b)
1359 }
1360 #[allow(dead_code)]
1361 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1362 if b == 0 {
1363 None
1364 } else {
1365 a.checked_div(b)
1366 }
1367 }
1368 #[allow(dead_code)]
1369 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1370 a + b
1371 }
1372 #[allow(dead_code)]
1373 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1374 a * b
1375 }
1376 #[allow(dead_code)]
1377 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1378 a.checked_neg()
1379 }
1380 #[allow(dead_code)]
1381 pub fn fold_not_bool(a: bool) -> bool {
1382 !a
1383 }
1384 #[allow(dead_code)]
1385 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1386 a && b
1387 }
1388 #[allow(dead_code)]
1389 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1390 a || b
1391 }
1392 #[allow(dead_code)]
1393 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1394 a.checked_shl(b)
1395 }
1396 #[allow(dead_code)]
1397 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1398 a.checked_shr(b)
1399 }
1400 #[allow(dead_code)]
1401 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1402 if b == 0 {
1403 None
1404 } else {
1405 Some(a % b)
1406 }
1407 }
1408 #[allow(dead_code)]
1409 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1410 a & b
1411 }
1412 #[allow(dead_code)]
1413 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1414 a | b
1415 }
1416 #[allow(dead_code)]
1417 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1418 a ^ b
1419 }
1420 #[allow(dead_code)]
1421 pub fn fold_bitnot_i64(a: i64) -> i64 {
1422 !a
1423 }
1424}
1425#[derive(Debug, Clone, PartialEq)]
1430pub enum SpirVOp {
1431 Variable(StorageClass),
1433 Load,
1435 Store,
1437 AccessChain,
1439 CopyObject,
1441 IAdd,
1443 ISub,
1445 IMul,
1447 SDiv,
1449 UDiv,
1451 SMod,
1453 UMod,
1455 SNegate,
1457 FAdd,
1459 FSub,
1461 FMul,
1463 FDiv,
1465 FMod,
1467 FNegate,
1469 FRem,
1471 IEqual,
1473 INotEqual,
1475 SLessThan,
1477 SLessThanEqual,
1479 SGreaterThan,
1481 ULessThan,
1483 FOrdEqual,
1485 FOrdLessThan,
1487 FOrdGreaterThan,
1489 LogicalAnd,
1491 LogicalOr,
1493 LogicalNot,
1495 LogicalEqual,
1497 BitwiseAnd,
1499 BitwiseOr,
1501 BitwiseXor,
1503 Not,
1505 ShiftLeftLogical,
1507 ShiftRightLogical,
1509 ShiftRightArithmetic,
1511 ConvertFToS,
1513 ConvertFToU,
1515 ConvertSToF,
1517 ConvertUToF,
1519 FConvert,
1521 SConvert,
1523 UConvert,
1525 Bitcast,
1527 CompositeConstruct,
1529 CompositeExtract,
1531 CompositeInsert,
1533 VectorShuffle,
1535 VectorExtractDynamic,
1537 VectorInsertDynamic,
1539 MatrixTimesVector,
1541 VectorTimesMatrix,
1543 MatrixTimesMatrix,
1545 MatrixTimesScalar,
1547 Dot,
1549 OuterProduct,
1551 Transpose,
1553 Label,
1555 Branch,
1557 BranchConditional,
1559 Switch,
1561 Return,
1563 ReturnValue,
1565 Unreachable,
1567 Phi,
1569 LoopMerge,
1571 SelectionMerge,
1573 Function,
1575 FunctionParameter,
1577 FunctionEnd,
1579 FunctionCall,
1581 ImageSampleImplicitLod,
1583 ImageSampleExplicitLod,
1585 ImageLoad,
1587 ImageStore,
1589 AtomicLoad,
1591 AtomicStore,
1593 AtomicIAdd,
1595 AtomicISub,
1597 AtomicCompareExchange,
1599 ExtInstGlsl(GlslStd450Op),
1601 Capability(SpirVCapability),
1603 Extension(String),
1605 ExtInstImport(String),
1607 MemoryModel(AddressingModel, MemoryModel),
1609 EntryPoint(ExecutionModel, String),
1611 ExecutionMode(ExecutionMode),
1613 Decorate(Decoration),
1615 MemberDecorate(u32, Decoration),
1617 Name(String),
1619 Constant(u64),
1621 ConstantComposite,
1623 ConstantTrue,
1625 ConstantFalse,
1627 TypeForwardPointer(StorageClass),
1629 ControlBarrier,
1631 MemoryBarrier,
1633}
1634#[derive(Debug, Clone, PartialEq)]
1636pub enum ExecutionMode {
1637 Invocations(u32),
1638 SpacingEqual,
1639 SpacingFractionalEven,
1640 SpacingFractionalOdd,
1641 VertexOrderCw,
1642 VertexOrderCcw,
1643 PixelCenterInteger,
1644 OriginUpperLeft,
1645 OriginLowerLeft,
1646 EarlyFragmentTests,
1647 PointMode,
1648 Xfb,
1649 DepthReplacing,
1650 DepthGreater,
1651 DepthLess,
1652 DepthUnchanged,
1653 LocalSize(u32, u32, u32),
1654 LocalSizeHint(u32, u32, u32),
1655 InputPoints,
1656 InputLines,
1657 InputLinesAdjacency,
1658 Triangles,
1659 InputTrianglesAdjacency,
1660 Quads,
1661 Isolines,
1662 OutputVertices(u32),
1663 OutputPoints,
1664 OutputLineStrip,
1665 OutputTriangleStrip,
1666}
1667#[derive(Debug, Clone)]
1669pub struct SpirVFunction {
1670 pub id: u32,
1672 pub return_type_id: u32,
1674 pub function_type_id: u32,
1676 pub name: Option<String>,
1678 pub params: Vec<(u32, u32)>,
1680 pub blocks: Vec<SpirVBasicBlock>,
1682 pub is_entry_point: bool,
1684 pub execution_model: Option<ExecutionModel>,
1686}
1687impl SpirVFunction {
1688 pub fn new(id: u32, name: Option<String>, return_type_id: u32, function_type_id: u32) -> Self {
1690 Self {
1691 id,
1692 return_type_id,
1693 function_type_id,
1694 name,
1695 params: Vec::new(),
1696 blocks: Vec::new(),
1697 is_entry_point: false,
1698 execution_model: None,
1699 }
1700 }
1701 pub fn add_param(&mut self, param_id: u32, type_id: u32) {
1703 self.params.push((param_id, type_id));
1704 }
1705 pub fn add_block(&mut self, block: SpirVBasicBlock) {
1707 self.blocks.push(block);
1708 }
1709 pub fn set_entry_point(&mut self, model: ExecutionModel) {
1711 self.is_entry_point = true;
1712 self.execution_model = Some(model);
1713 }
1714 pub fn emit_text(&self) -> String {
1716 let mut lines = Vec::new();
1717 let name_comment = self
1718 .name
1719 .as_deref()
1720 .map(|n| format!(" ; {}", n))
1721 .unwrap_or_default();
1722 lines.push(format!(
1723 "%{} = OpFunction %{} None %{}{}",
1724 self.id, self.return_type_id, self.function_type_id, name_comment
1725 ));
1726 for (pid, tid) in &self.params {
1727 lines.push(format!(" %{} = OpFunctionParameter %{}", pid, tid));
1728 }
1729 for block in &self.blocks {
1730 lines.push(block.emit_text());
1731 }
1732 lines.push("OpFunctionEnd".to_string());
1733 lines.join("\n")
1734 }
1735 pub fn total_instrs(&self) -> usize {
1737 self.blocks.iter().map(|b| b.instr_count()).sum()
1738 }
1739}
1740#[allow(dead_code)]
1741#[derive(Debug, Clone, PartialEq)]
1742pub enum SPIRVPassPhase {
1743 Analysis,
1744 Transformation,
1745 Verification,
1746 Cleanup,
1747}
1748impl SPIRVPassPhase {
1749 #[allow(dead_code)]
1750 pub fn name(&self) -> &str {
1751 match self {
1752 SPIRVPassPhase::Analysis => "analysis",
1753 SPIRVPassPhase::Transformation => "transformation",
1754 SPIRVPassPhase::Verification => "verification",
1755 SPIRVPassPhase::Cleanup => "cleanup",
1756 }
1757 }
1758 #[allow(dead_code)]
1759 pub fn is_modifying(&self) -> bool {
1760 matches!(
1761 self,
1762 SPIRVPassPhase::Transformation | SPIRVPassPhase::Cleanup
1763 )
1764 }
1765}
1766#[allow(dead_code)]
1767#[derive(Debug, Clone)]
1768pub struct SPIRVWorklist {
1769 pub(super) items: std::collections::VecDeque<u32>,
1770 pub(super) in_worklist: std::collections::HashSet<u32>,
1771}
1772impl SPIRVWorklist {
1773 #[allow(dead_code)]
1774 pub fn new() -> Self {
1775 SPIRVWorklist {
1776 items: std::collections::VecDeque::new(),
1777 in_worklist: std::collections::HashSet::new(),
1778 }
1779 }
1780 #[allow(dead_code)]
1781 pub fn push(&mut self, item: u32) -> bool {
1782 if self.in_worklist.insert(item) {
1783 self.items.push_back(item);
1784 true
1785 } else {
1786 false
1787 }
1788 }
1789 #[allow(dead_code)]
1790 pub fn pop(&mut self) -> Option<u32> {
1791 let item = self.items.pop_front()?;
1792 self.in_worklist.remove(&item);
1793 Some(item)
1794 }
1795 #[allow(dead_code)]
1796 pub fn is_empty(&self) -> bool {
1797 self.items.is_empty()
1798 }
1799 #[allow(dead_code)]
1800 pub fn len(&self) -> usize {
1801 self.items.len()
1802 }
1803 #[allow(dead_code)]
1804 pub fn contains(&self, item: u32) -> bool {
1805 self.in_worklist.contains(&item)
1806 }
1807}