use super::types::*;
use super::analyzer::*;
use super::coverage::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shared_mem_u64_detection() {
let ptx = r#"
.visible .entry test() {
.reg .u64 %rd<5>;
.reg .f32 %f<2>;
.shared .b8 smem[4096];
st.shared.f32 [%rd0], %f0;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::SharedMemU64Addressing));
}
#[test]
fn test_shared_mem_u32_valid() {
let ptx = r#"
.visible .entry test() {
.reg .u32 %r<5>;
.reg .f32 %f<2>;
.shared .b8 smem[4096];
st.shared.f32 [%r0], %f0;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::SharedMemU64Addressing));
}
#[test]
fn test_missing_barrier_sync_strict() {
let ptx = r#"
.visible .entry test() {
.shared .b8 smem[1024];
st.shared.f32 [%r0], %f0;
ld.shared.f32 %f1, [%r1];
ret;
}
"#;
let normal_result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!normal_result.has_bug(&PtxBugClass::MissingBarrierSync));
let strict_result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(strict_result.has_bug(&PtxBugClass::MissingBarrierSync));
}
#[test]
fn test_barrier_present_valid() {
let ptx = r#"
.visible .entry test() {
.shared .b8 smem[1024];
st.shared.f32 [%r0], %f0;
bar.sync 0;
ld.shared.f32 %f1, [%r1];
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
let missing_barrier_bugs: Vec<_> = result.bugs_of_class(&PtxBugClass::MissingBarrierSync);
assert!(missing_barrier_bugs
.iter()
.all(|b| !b.message.contains("ld.shared follows st.shared")));
}
#[test]
fn test_loop_branch_to_end_detection() {
let ptx = r#"
.visible .entry test() {
main_loop:
// loop body
bra main_loop_end;
main_loop_end:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::LoopBranchToEnd));
}
#[test]
fn test_conditional_branch_not_flagged() {
let ptx = r#"
.visible .entry test() {
loop_start:
@%p0 bra loop_end;
bra loop_start;
loop_end:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::LoopBranchToEnd));
}
#[test]
fn test_register_spills_detection() {
let ptx = r#"
.visible .entry test() {
.local .align 4 .b8 __local_depot[32];
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::RegisterSpills));
}
#[test]
fn test_missing_entry_point_detection() {
let ptx = r#"
.version 8.0
.target sm_70
.reg .f32 %f<4>;
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::MissingEntryPoint));
}
#[test]
fn test_valid_kernel_no_bugs() {
let ptx = r#"
.version 8.0
.target sm_70
.visible .entry valid_kernel() {
.reg .f32 %f<4>;
.reg .u32 %r<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.is_valid());
assert!(!result.has_bugs());
}
#[test]
fn test_bug_severity_classification() {
assert_eq!(
PtxBugClass::MissingBarrierSync.severity(),
BugSeverity::Critical
);
assert_eq!(
PtxBugClass::SharedMemU64Addressing.severity(),
BugSeverity::Critical
);
assert_eq!(PtxBugClass::RegisterSpills.severity(), BugSeverity::High);
assert_eq!(
PtxBugClass::MissingEntryPoint.severity(),
BugSeverity::FalsePositive
);
}
#[test]
fn test_bug_report_format() {
let ptx = r#"
.visible .entry test() {
.shared .b8 smem[1024];
st.shared.f32 [%rd0], %f0;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
let report = result.format_report();
assert!(report.contains("PTX BUG HUNTING REPORT"));
assert!(report.contains("P0 CRITICAL BUGS:"));
assert!(report.contains("SUMMARY"));
}
#[test]
fn test_kernel_name_extraction() {
let ptx = r#"
.visible .entry gemm_tiled() {
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert_eq!(result.kernel_name, Some("gemm_tiled".to_string()));
}
#[test]
fn test_count_by_severity() {
let report = PtxBugReport {
kernel_name: Some("test".to_string()),
bugs: vec![
PtxBug {
class: PtxBugClass::MissingBarrierSync,
line: 1,
instruction: "test".to_string(),
message: "test".to_string(),
fix: None,
},
PtxBug {
class: PtxBugClass::RegisterSpills,
line: 2,
instruction: "test".to_string(),
message: "test".to_string(),
fix: None,
},
],
lines_analyzed: 10,
strict_mode: true,
};
assert_eq!(report.count_by_severity(BugSeverity::Critical), 1);
assert_eq!(report.count_by_severity(BugSeverity::High), 1);
assert_eq!(report.count_by_severity(BugSeverity::Medium), 0);
}
#[test]
fn f101_detect_shared_u64_addressing() {
let ptx = "st.shared.f32 [%rd5], %f0;";
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::SharedMemU64Addressing));
}
#[test]
fn f102_detect_missing_barrier() {
let ptx = r#"
.visible .entry test() {
.shared .b8 smem[1024];
st.shared.f32 [%r0], %f0;
ld.shared.f32 %f1, [%r1];
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::MissingBarrierSync));
}
#[test]
fn f103_detect_loop_branch_end() {
let ptx = r#"
.entry test() {
test_loop:
bra test_loop_end;
test_loop_end:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::LoopBranchToEnd));
}
#[test]
fn f104_valid_ptx_passes() {
let ptx = r#"
.version 8.0
.target sm_70
.visible .entry valid() {
.reg .f32 %f<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.is_valid());
}
#[test]
fn f106_missing_entry_detected() {
let ptx = ".version 8.0
.target sm_70
.reg .f32 %f<4>;";
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::MissingEntryPoint));
}
#[test]
fn test_redundant_moves_chain() {
let ptx = r#"
.visible .entry test() {
mov.u32 %r1, %r0;
mov.u32 %r2, %r1;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::RedundantMoves));
}
#[test]
fn test_redundant_moves_no_chain() {
let ptx = r#"
.visible .entry test() {
mov.u32 %r1, %r0;
add.u32 %r2, %r1, 1;
mov.u32 %r3, %r2;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::RedundantMoves));
}
#[test]
fn test_unoptimized_memory_single_loads() {
let ptx = r#"
.visible .entry test() {
ld.global.f32 %f0, [%rd0];
ld.global.f32 %f1, [%rd1];
ld.global.f32 %f2, [%rd2];
ld.global.f32 %f3, [%rd3];
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::UnoptimizedMemoryPattern));
}
#[test]
fn test_unoptimized_memory_vector_loads() {
let ptx = r#"
.visible .entry test() {
ld.global.v4.f32 {%f0, %f1, %f2, %f3}, [%rd0];
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::UnoptimizedMemoryPattern));
}
#[test]
fn test_unoptimized_memory_few_loads() {
let ptx = r#"
.visible .entry test() {
ld.global.f32 %f0, [%rd0];
ld.global.f32 %f1, [%rd1];
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::UnoptimizedMemoryPattern));
}
#[test]
fn test_unoptimized_memory_suspicious_stride() {
let ptx = r#"
.visible .entry test() {
mul.wide.u32 %rd0, %r0, 17;
ld.global.f32 %f0, [%rd0];
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::UnoptimizedMemoryPattern));
}
#[test]
fn test_unoptimized_memory_normal_stride() {
let ptx = r#"
.visible .entry test() {
mul.wide.u32 %rd0, %r0, 4;
ld.global.f32 %f0, [%rd0];
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::UnoptimizedMemoryPattern));
}
#[test]
fn test_high_register_pressure() {
let ptx = r#"
.visible .entry test() {
.reg .b32 %r<64>;
.reg .b64 %rd<16>;
.reg .f32 %f<32>;
.reg .pred %p<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_normal_register_pressure() {
let ptx = r#"
.visible .entry test() {
.reg .b32 %r<16>;
.reg .b64 %rd<8>;
.reg .f32 %f<8>;
.reg .pred %p<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_predicate_overflow() {
let ptx = r#"
.visible .entry test() {
.reg .pred %p<12>;
.reg .b32 %r<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::PredicateOverflow));
}
#[test]
fn test_normal_predicate_count() {
let ptx = r#"
.visible .entry test() {
.reg .pred %p<8>;
.reg .b32 %r<4>;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::PredicateOverflow));
}
#[test]
fn test_placeholder_code_omitted() {
let ptx = r#"
.visible .entry test() {
// ... loading logic omitted for brevity
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::PlaceholderCode));
}
#[test]
fn test_placeholder_code_simplified() {
let ptx = r#"
.visible .entry test() {
// Simplified: only first element
st.global.f32 [%rd0], %f0;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::PlaceholderCode));
}
#[test]
fn test_placeholder_code_explicit() {
let ptx = r#"
.visible .entry test() {
// This is placeholder code for now
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::PlaceholderCode));
}
#[test]
fn test_no_placeholder_code() {
let ptx = r#"
.visible .entry test() {
// Load input
ld.global.f32 %f0, [%rd0];
// Compute result
mul.f32 %f1, %f0, %f0;
// Store output
st.global.f32 [%rd1], %f1;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::PlaceholderCode));
}
#[test]
fn test_new_bug_severities() {
assert_eq!(
PtxBugClass::HighRegisterPressure.severity(),
BugSeverity::High
);
assert_eq!(PtxBugClass::PredicateOverflow.severity(), BugSeverity::High);
assert_eq!(PtxBugClass::PlaceholderCode.severity(), BugSeverity::High);
}
#[test]
fn test_new_bug_codes() {
assert_eq!(
PtxBugClass::HighRegisterPressure.code(),
"HIGH_REG_PRESSURE"
);
assert_eq!(PtxBugClass::PredicateOverflow.code(), "PRED_OVERFLOW");
assert_eq!(PtxBugClass::PlaceholderCode.code(), "PLACEHOLDER_CODE");
}
#[test]
fn test_whitelist_suppresses_bug() {
let ptx = r#"
.visible .entry q4k_gemm_ggml() {
.reg .b32 %r<64>;
.reg .b64 %rd<16>;
.reg .f32 %f<32>;
ret;
}
"#;
let result_no_whitelist = PtxBugAnalyzer::new().analyze(ptx);
assert!(result_no_whitelist.has_bug(&PtxBugClass::HighRegisterPressure));
let result_with_whitelist = PtxBugAnalyzer::with_quantized_whitelist().analyze(ptx);
assert!(!result_with_whitelist.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_whitelist_exact_match() {
let ptx = r#"
.visible .entry special_kernel() {
.reg .b32 %r<64>;
.reg .b64 %rd<16>;
.reg .f32 %f<32>;
ret;
}
"#;
let analyzer = PtxBugAnalyzer::new().with_whitelist(
"special_kernel",
PtxBugClass::HighRegisterPressure,
"Expected high regs",
);
let result = analyzer.analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_whitelist_no_match() {
let ptx = r#"
.visible .entry other_kernel() {
.reg .b32 %r<64>;
.reg .b64 %rd<16>;
.reg .f32 %f<32>;
ret;
}
"#;
let result = PtxBugAnalyzer::with_quantized_whitelist().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_performance_whitelist_tensor_core() {
let ptx = r#"
.visible .entry gemm_tensor_core() {
.reg .b32 %r<64>;
.reg .b64 %rd<32>;
.reg .f32 %f<64>;
.reg .pred %p<12>;
ret;
}
"#;
let result_no_whitelist = PtxBugAnalyzer::new().analyze(ptx);
assert!(result_no_whitelist.has_bug(&PtxBugClass::HighRegisterPressure));
assert!(result_no_whitelist.has_bug(&PtxBugClass::PredicateOverflow));
let result_with_whitelist = PtxBugAnalyzer::with_performance_whitelist().analyze(ptx);
assert!(!result_with_whitelist.has_bug(&PtxBugClass::HighRegisterPressure));
assert!(!result_with_whitelist.has_bug(&PtxBugClass::PredicateOverflow));
}
#[test]
fn test_performance_whitelist_attention() {
let ptx = r#"
.visible .entry flash_attention_tensor_core() {
.reg .b32 %r<64>;
.reg .b64 %rd<32>;
.reg .f32 %f<48>;
ret;
}
"#;
let result = PtxBugAnalyzer::with_performance_whitelist().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::HighRegisterPressure));
}
#[test]
fn test_empty_loop_body_detected() {
let ptx = r#"
.visible .entry test() {
empty_loop:
// Just comments here
bra empty_loop;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::EmptyLoopBody));
}
#[test]
fn test_valid_loop_body_not_flagged() {
let ptx = r#"
.visible .entry test() {
.reg .f32 %f<4>;
.reg .u32 %r<4>;
compute_loop:
add.f32 %f0, %f0, %f1;
add.u32 %r0, %r0, 1;
setp.lt.u32 %p0, %r0, %r1;
@%p0 bra compute_loop;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EmptyLoopBody));
}
#[test]
fn test_loop_with_exit_condition_not_flagged() {
let ptx = r#"
.visible .entry test() {
.reg .u32 %r<4>;
.reg .pred %p<2>;
check_loop:
setp.lt.u32 %p0, %r0, %r1;
@%p0 bra check_loop;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EmptyLoopBody));
}
#[test]
fn test_missing_bounds_check() {
let ptx = r#"
.visible .entry test() {
.reg .u64 %rd<4>;
.reg .f32 %f<4>;
mov.u32 %r0, %tid.x;
ld.global.f32 %f0, [%rd0];
st.global.f32 [%rd1], %f0;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::MissingBoundsCheck));
}
#[test]
fn test_proper_bounds_check_not_flagged() {
let ptx = r#"
.visible .entry test() {
.reg .u64 %rd<4>;
.reg .f32 %f<4>;
.reg .u32 %r<4>;
.reg .pred %p<2>;
mov.u32 %r0, %tid.x;
setp.lt.u32 %p0, %r0, %r1;
@%p0 bra do_work;
bra done;
do_work:
ld.global.f32 %f0, [%rd0];
st.global.f32 [%rd1], %f0;
done:
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::MissingBoundsCheck));
}
#[test]
fn test_no_global_mem_no_bounds_check_needed() {
let ptx = r#"
.visible .entry test() {
.reg .u32 %r<4>;
mov.u32 %r0, %tid.x;
add.u32 %r1, %r0, 1;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::MissingBoundsCheck));
}
#[test]
fn test_dead_code_after_ret() {
let ptx = r#"
.visible .entry test() {
.reg .f32 %f<4>;
add.f32 %f0, %f1, %f2;
ret;
mul.f32 %f3, %f0, %f1;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::DeadCode));
}
#[test]
fn test_dead_code_after_branch() {
let ptx = r#"
.visible .entry test() {
.reg .f32 %f<4>;
bra skip;
add.f32 %f0, %f1, %f2;
skip:
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::DeadCode));
}
#[test]
fn test_reachable_code_not_flagged() {
let ptx = r#"
.visible .entry test() {
.reg .f32 %f<4>;
.reg .pred %p<2>;
@%p0 bra skip;
add.f32 %f0, %f1, %f2;
skip:
mul.f32 %f3, %f0, %f1;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::DeadCode));
}
#[test]
fn test_code_after_label_reachable() {
let ptx = r#"
.visible .entry test() {
.reg .f32 %f<4>;
bra middle;
middle:
add.f32 %f0, %f1, %f2;
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::DeadCode));
}
#[test]
fn test_extended_bug_severities() {
assert_eq!(PtxBugClass::EmptyLoopBody.severity(), BugSeverity::High);
assert_eq!(
PtxBugClass::MissingBoundsCheck.severity(),
BugSeverity::High
);
assert_eq!(PtxBugClass::DeadCode.severity(), BugSeverity::Medium);
}
#[test]
fn test_extended_bug_codes() {
assert_eq!(PtxBugClass::EmptyLoopBody.code(), "EMPTY_LOOP");
assert_eq!(PtxBugClass::MissingBoundsCheck.code(), "NO_BOUNDS_CHECK");
assert_eq!(PtxBugClass::DeadCode.code(), "DEAD_CODE");
}
#[test]
fn test_parity114_conditional_exit_before_barrier() {
let ptx = r#"
.visible .entry kernel() {
mov.u32 %r0, %tid.x;
setp.lt.u32 %p0, %r0, 32;
loop_start:
@!%p0 bra exit;
ld.shared.f32 %f0, [%r0];
bar.sync 0;
st.shared.f32 [%r0], %f0;
bra loop_start;
loop_start_end:
done:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
assert_eq!(
PtxBugClass::EarlyExitBeforeBarrier.severity(),
BugSeverity::Critical
);
}
#[test]
fn test_parity114_unconditional_exit_before_barrier() {
let ptx = r#"
.visible .entry kernel() {
loop_start:
bra exit;
bar.sync 0;
bra loop_start;
loop_start_end:
done:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
}
#[test]
fn test_parity114_safe_barrier_first() {
let ptx = r#"
.visible .entry kernel() {
mov.u32 %r0, %tid.x;
setp.lt.u32 %p0, %r0, 32;
loop_start:
ld.shared.f32 %f0, [%r0];
bar.sync 0;
st.shared.f32 [%r0], %f0;
bra loop_start;
loop_start_end:
@!%p0 bra exit;
st.global.f32 [%r1], %f0;
exit:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
}
#[test]
fn test_parity114_exit_after_loop_is_safe() {
let ptx = r#"
.visible .entry kernel() {
k_tile_loop:
bar.sync 0;
ld.shared.f32 %f0, [%r0];
bra k_tile_loop;
k_tile_end:
@!%p0 bra exit;
st.global.f32 [%r1], %f0;
done:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
}
#[test]
fn test_parity114_non_strict_mode() {
let ptx = r#"
.visible .entry kernel() {
loop_start:
@!%p0 bra exit;
bar.sync 0;
bra loop_start;
loop_start_end:
done:
ret;
}
"#;
let result = PtxBugAnalyzer::new().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
let strict_result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(strict_result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
}
#[test]
fn test_parity114_bug_class_properties() {
assert_eq!(
PtxBugClass::EarlyExitBeforeBarrier.code(),
"EARLY_EXIT_BARRIER"
);
assert_eq!(
PtxBugClass::EarlyExitBeforeBarrier.severity(),
BugSeverity::Critical
);
}
#[test]
fn test_parity114_attention_kv_loop_safe() {
let ptx = r#"
.visible .entry flash_attention() {
kv_loop:
bar.sync 0;
wmma.mma.sync.aligned.row.col.m16n16k16.f32.f16.f16.f32;
bra kv_loop;
kv_loop_end:
@!%p_valid bra exit;
st.global.f32 [%out], %f0;
done:
ret;
}
"#;
let result = PtxBugAnalyzer::strict().analyze(ptx);
assert!(!result.has_bug(&PtxBugClass::EarlyExitBeforeBarrier));
}
}