Skip to main content

trueno_explain/ptx/bugs/
types.rs

1//! PTX bug types and classifications.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// PTX bug severity classification
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum BugSeverity {
9    /// P0: Correctness bugs - silent wrong results
10    Critical,
11    /// P1: Major performance bugs - 10-100x slowdown
12    High,
13    /// P2: Minor performance bugs - 2-10x slowdown
14    Medium,
15    /// False positive detection - error handling issues
16    FalsePositive,
17}
18
19impl fmt::Display for BugSeverity {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Self::Critical => write!(f, "P0-CRITICAL"),
23            Self::High => write!(f, "P1-HIGH"),
24            Self::Medium => write!(f, "P2-MEDIUM"),
25            Self::FalsePositive => write!(f, "FALSE-POSITIVE"),
26        }
27    }
28}
29
30/// PTX bug classification
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub enum PtxBugClass {
33    /// P0: Shared memory accessed with 64-bit register (should be 32-bit)
34    SharedMemU64Addressing,
35    /// P0: Loop branches to END label instead of START
36    LoopBranchToEnd,
37    /// P0: Missing barrier sync between shared memory write and read
38    MissingBarrierSync,
39    /// P0: Early thread exit before barrier in loop - causes CUDA error 700 (PARITY-114)
40    EarlyExitBeforeBarrier,
41    /// P1: Accumulator not updated in-place in loop
42    NonInPlaceLoopAccumulator,
43    /// P1: Register spills to local memory
44    RegisterSpills,
45    /// P1: High register pressure (>64 regs) reduces occupancy
46    HighRegisterPressure,
47    /// P1: Predicate register overflow (>8 predicates)
48    PredicateOverflow,
49    /// P1: Placeholder/incomplete code detected
50    PlaceholderCode,
51    /// P1: Empty loop body (no actual computation)
52    EmptyLoopBody,
53    /// P1: Missing thread bounds check
54    MissingBoundsCheck,
55    /// P2: Redundant register moves
56    RedundantMoves,
57    /// P2: Unoptimized memory access pattern
58    UnoptimizedMemoryPattern,
59    /// P2: Dead code after unconditional branch or ret
60    DeadCode,
61    /// False Positive: Invalid PTX syntax accepted
62    InvalidSyntaxAccepted,
63    /// False Positive: Missing kernel entry point
64    MissingEntryPoint,
65}
66
67impl PtxBugClass {
68    /// Get the severity of this bug class
69    #[must_use]
70    pub fn severity(&self) -> BugSeverity {
71        match self {
72            Self::SharedMemU64Addressing
73            | Self::LoopBranchToEnd
74            | Self::MissingBarrierSync
75            | Self::EarlyExitBeforeBarrier => BugSeverity::Critical,
76
77            Self::NonInPlaceLoopAccumulator
78            | Self::RegisterSpills
79            | Self::HighRegisterPressure
80            | Self::PredicateOverflow
81            | Self::PlaceholderCode
82            | Self::EmptyLoopBody
83            | Self::MissingBoundsCheck => BugSeverity::High,
84
85            Self::RedundantMoves | Self::UnoptimizedMemoryPattern | Self::DeadCode => {
86                BugSeverity::Medium
87            }
88
89            Self::InvalidSyntaxAccepted | Self::MissingEntryPoint => BugSeverity::FalsePositive,
90        }
91    }
92
93    /// Get a short code for this bug class
94    #[must_use]
95    pub fn code(&self) -> &'static str {
96        match self {
97            Self::SharedMemU64Addressing => "SHARED_U64",
98            Self::LoopBranchToEnd => "LOOP_BRANCH_END",
99            Self::MissingBarrierSync => "MISSING_BARRIER",
100            Self::EarlyExitBeforeBarrier => "EARLY_EXIT_BARRIER",
101            Self::NonInPlaceLoopAccumulator => "NON_INPLACE_ACCUM",
102            Self::RegisterSpills => "REG_SPILLS",
103            Self::HighRegisterPressure => "HIGH_REG_PRESSURE",
104            Self::PredicateOverflow => "PRED_OVERFLOW",
105            Self::PlaceholderCode => "PLACEHOLDER_CODE",
106            Self::EmptyLoopBody => "EMPTY_LOOP",
107            Self::MissingBoundsCheck => "NO_BOUNDS_CHECK",
108            Self::RedundantMoves => "REDUNDANT_MOV",
109            Self::UnoptimizedMemoryPattern => "UNOPT_MEM",
110            Self::DeadCode => "DEAD_CODE",
111            Self::InvalidSyntaxAccepted => "INVALID_SYNTAX",
112            Self::MissingEntryPoint => "NO_ENTRY",
113        }
114    }
115}
116
117impl fmt::Display for PtxBugClass {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, "{}", self.code())
120    }
121}
122
123/// A detected PTX bug with location and fix suggestion
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct PtxBug {
126    /// Bug classification
127    pub class: PtxBugClass,
128    /// Line number (1-indexed, 0 if unknown)
129    pub line: usize,
130    /// The offending PTX instruction
131    pub instruction: String,
132    /// Human-readable explanation
133    pub message: String,
134    /// Suggested fix
135    pub fix: Option<String>,
136}
137
138impl PtxBug {
139    /// Get the severity of this bug
140    #[must_use]
141    pub fn severity(&self) -> BugSeverity {
142        self.class.severity()
143    }
144}
145
146/// Result of PTX bug hunting analysis
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct PtxBugReport {
149    /// Kernel name if detected
150    pub kernel_name: Option<String>,
151    /// List of detected bugs
152    pub bugs: Vec<PtxBug>,
153    /// Total lines analyzed
154    pub lines_analyzed: usize,
155    /// Analysis mode (strict or normal)
156    pub strict_mode: bool,
157}
158
159impl PtxBugReport {
160    /// Check if PTX passed all validations (no critical bugs)
161    #[must_use]
162    pub fn is_valid(&self) -> bool {
163        !self
164            .bugs
165            .iter()
166            .any(|b| b.severity() == BugSeverity::Critical)
167    }
168
169    /// Check if there are any bugs at all
170    #[must_use]
171    pub fn has_bugs(&self) -> bool {
172        !self.bugs.is_empty()
173    }
174
175    /// Count bugs by severity
176    #[must_use]
177    pub fn count_by_severity(&self, severity: BugSeverity) -> usize {
178        self.bugs
179            .iter()
180            .filter(|b| b.severity() == severity)
181            .count()
182    }
183
184    /// Check for specific bug class
185    #[must_use]
186    pub fn has_bug(&self, class: &PtxBugClass) -> bool {
187        self.bugs.iter().any(|b| &b.class == class)
188    }
189
190    /// Get bugs by class
191    #[must_use]
192    pub fn bugs_of_class(&self, class: &PtxBugClass) -> Vec<&PtxBug> {
193        self.bugs.iter().filter(|b| &b.class == class).collect()
194    }
195
196    /// Format as bug report (bashrs style)
197    #[must_use]
198    pub fn format_report(&self) -> String {
199        let mut output = String::new();
200
201        output.push_str(
202            "╔══════════════════════════════════════════════════════════════════════════════╗\n",
203        );
204        output.push_str(
205            "║                         PTX BUG HUNTING REPORT                                ║\n",
206        );
207        output.push_str(
208            "╚══════════════════════════════════════════════════════════════════════════════╝\n\n",
209        );
210
211        if let Some(name) = &self.kernel_name {
212            output.push_str(&format!("Kernel: {}\n", name));
213        }
214        output.push_str(&format!("PTX Lines Analyzed: {}\n\n", self.lines_analyzed));
215
216        let critical = self.count_by_severity(BugSeverity::Critical);
217        let high = self.count_by_severity(BugSeverity::High);
218        let medium = self.count_by_severity(BugSeverity::Medium);
219        let false_pos = self.count_by_severity(BugSeverity::FalsePositive);
220
221        // P0 Critical
222        output.push_str(&format!("P0 CRITICAL BUGS: {}\n", critical));
223        if critical > 0 {
224            output.push_str("──────────────────\n");
225            for (i, bug) in self
226                .bugs
227                .iter()
228                .filter(|b| b.severity() == BugSeverity::Critical)
229                .enumerate()
230            {
231                output.push_str(&format!("  BUG-{:03}: {}\n", i + 1, bug.class));
232                if bug.line > 0 {
233                    output.push_str(&format!("    Line {}: {}\n", bug.line, bug.instruction));
234                }
235                output.push_str(&format!("    Impact: {}\n", bug.message));
236                if let Some(fix) = &bug.fix {
237                    output.push_str(&format!("    Fix: {}\n", fix));
238                }
239                output.push('\n');
240            }
241        }
242
243        // P1 High
244        output.push_str(&format!("\nP1 HIGH BUGS: {}\n", high));
245        if high > 0 {
246            output.push_str("─────────────────\n");
247            for bug in self
248                .bugs
249                .iter()
250                .filter(|b| b.severity() == BugSeverity::High)
251            {
252                output.push_str(&format!("  {}: {}\n", bug.class, bug.message));
253            }
254        }
255
256        // P2 Medium
257        output.push_str(&format!("\nP2 MEDIUM BUGS: {}\n", medium));
258        if medium > 0 {
259            output.push_str("─────────────────\n");
260            for bug in self
261                .bugs
262                .iter()
263                .filter(|b| b.severity() == BugSeverity::Medium)
264            {
265                output.push_str(&format!("  {}: {}\n", bug.class, bug.message));
266            }
267        }
268
269        // False positives
270        output.push_str(&format!("\nFALSE POSITIVES DETECTED: {}\n", false_pos));
271
272        // Summary
273        output.push_str("\nSUMMARY\n═══════\n");
274        output.push_str(&format!("  Total Bugs: {}\n", self.bugs.len()));
275        output.push_str(&format!("  P0 Critical: {}", critical));
276        if critical > 0 {
277            output.push_str(" ← BLOCKS RELEASE");
278        }
279        output.push('\n');
280        output.push_str(&format!("  P1 High: {}\n", high));
281        output.push_str(&format!("  P2 Medium: {}\n", medium));
282
283        output
284    }
285}