1#![allow(
2 clippy::doc_lazy_continuation,
3 clippy::double_must_use,
4 clippy::manual_div_ceil,
5 clippy::needless_range_loop,
6 clippy::collapsible_if,
7 clippy::match_like_matches_macro,
8 clippy::redundant_closure
9)]
10pub mod analyses;
40pub mod audit;
41pub mod descriptor;
42pub mod emit_adversarial_corpus;
43pub mod error;
44pub mod lower;
45pub(crate) mod op_properties;
46pub(crate) mod operand_semantics;
47pub mod optimization_corpus;
48pub mod pre_emit;
49pub mod rewrites;
50pub mod verify;
51
52pub use audit::{
53 audit, audit_optimized, audit_with_histogram, PerfAuditReport, Recommendation,
54 RecommendationCategory,
55};
56
57pub fn verify_then_optimize(
66 desc: &KernelDescriptor,
67) -> Result<(KernelDescriptor, rewrites::OptimizationStats), VerifyFailure> {
68 if let Err(errs) = verify::verify(desc) {
69 return Err(VerifyFailure::Input(errs));
70 }
71 let (optimized, stats) = rewrites::run_all_with_stats(desc);
72 if let Err(errs) = verify::verify(&optimized) {
73 return Err(VerifyFailure::Output(errs));
74 }
75 Ok((optimized, stats))
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum VerifyFailure {
81 Input(Vec<verify::VerifyError>),
83 Output(Vec<verify::VerifyError>),
87}
88
89impl VerifyFailure {
90 pub fn errors(&self) -> &[verify::VerifyError] {
91 match self {
92 VerifyFailure::Input(e) | VerifyFailure::Output(e) => e,
93 }
94 }
95}
96
97#[must_use]
103pub fn full_report(desc: &KernelDescriptor) -> FullReport {
104 let descriptor_id = desc.id.clone();
105 let summary = desc.summary();
106 let histogram = analyses::op_histogram::analyze(desc);
107 let perf = audit::audit(desc);
108 let verify_input = verify::verify(desc);
109 let (optimized_summary, verify_output, stats, optimization_ran) = if verify_input.is_ok() {
110 let (optimized, stats) = rewrites::run_all_with_stats(desc);
111 let verify_output = verify::verify(&optimized);
112 (optimized.summary(), verify_output, stats, true)
113 } else {
114 (
115 summary.clone(),
116 Ok(()),
117 optimization_skipped_stats(desc),
118 false,
119 )
120 };
121 let fix_text = build_full_report_fix_text(
122 &verify_input,
123 optimization_ran.then_some(&verify_output),
124 );
125 FullReport {
126 descriptor_id,
127 summary,
128 optimized_summary,
129 histogram,
130 perf,
131 verify_input,
132 verify_output,
133 stats,
134 optimization_ran,
135 fix_text,
136 }
137}
138
139#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
142pub struct FullReport {
143 #[serde(default)]
144 pub descriptor_id: String,
145 pub summary: String,
146 pub optimized_summary: String,
147 pub histogram: analyses::op_histogram::OpHistogram,
148 pub perf: PerfAuditReport,
149 pub verify_input: verify::VerifyResult,
150 #[serde(default = "default_verify_result")]
151 pub verify_output: verify::VerifyResult,
152 pub stats: rewrites::OptimizationStats,
153 #[serde(default)]
154 pub optimization_ran: bool,
155 #[serde(default)]
156 pub fix_text: String,
157}
158
159impl FullReport {
160 #[must_use]
161 pub fn verify_input_status(&self) -> &'static str {
162 verification_status(&self.verify_input)
163 }
164
165 #[must_use]
166 pub fn verify_output_status(&self) -> &'static str {
167 if self.optimization_ran {
168 verification_status(&self.verify_output)
169 } else {
170 "SKIPPED"
171 }
172 }
173
174 pub fn format_short(&self) -> String {
177 format!(
178 "{} | id {} | {} | {} | {} | input verify {} | output verify {}",
179 self.summary,
180 self.descriptor_id,
181 self.histogram.format_short(),
182 self.perf.format_short(),
183 self.stats.format_short(),
184 self.verify_input_status(),
185 self.verify_output_status(),
186 )
187 }
188
189 pub fn format_long(&self) -> String {
192 let mut out = String::new();
193 use std::fmt::Write as _;
194 let _ = writeln!(out, "Kernel:");
195 let _ = writeln!(out, " descriptor id: {}", self.descriptor_id);
196 let _ = writeln!(out, " raw: {}", self.summary);
197 let _ = writeln!(out, " optimized: {}", self.optimized_summary);
198 let _ = writeln!(out, "Histogram:");
199 let _ = writeln!(out, " {}", self.histogram.format_short());
200 if let Some((cat, n)) = self.histogram.dominant() {
201 let _ = writeln!(out, " dominant: {cat} ({n})");
202 }
203 let _ = writeln!(out, "Perf audit:");
204 let _ = writeln!(out, " {}", self.perf.format_short());
205 for r in &self.perf.recommendations {
206 let _ = writeln!(
207 out,
208 " - [p{}] {:?}: {} (≤{:.2}× speedup)",
209 r.priority, r.category, r.message, r.estimated_speedup_upper_bound
210 );
211 }
212 let _ = writeln!(out, "Optimization:");
213 let _ = writeln!(out, " {}", self.stats.format_short());
214 let _ = writeln!(out, "Verify (input):");
215 write_verify_section(&mut out, &self.verify_input);
216 let _ = writeln!(out, "Verify (output):");
217 if self.optimization_ran {
218 write_verify_section(&mut out, &self.verify_output);
219 } else {
220 let _ = writeln!(out, " SKIPPED (input verification failed)");
221 }
222 if !self.fix_text.is_empty() {
223 let _ = writeln!(out, "Fix:");
224 let _ = writeln!(out, " {}", self.fix_text);
225 }
226 out
227 }
228}
229
230fn default_verify_result() -> verify::VerifyResult {
231 Ok(())
232}
233
234fn optimization_skipped_stats(desc: &KernelDescriptor) -> rewrites::OptimizationStats {
235 rewrites::OptimizationStats {
236 ops_before: desc.body.ops.len(),
237 ops_after: desc.body.ops.len(),
238 bindings_before: desc.bindings.slots.len(),
239 bindings_after: desc.bindings.slots.len(),
240 literals_before: desc.body.literals.len(),
241 literals_after: desc.body.literals.len(),
242 iterations: 0,
243 converged: false,
244 }
245}
246
247fn verification_status(result: &verify::VerifyResult) -> &'static str {
248 if result.is_ok() {
249 "OK"
250 } else {
251 "FAIL"
252 }
253}
254
255fn write_verify_section(out: &mut String, result: &verify::VerifyResult) {
256 use std::fmt::Write as _;
257 match result {
258 Ok(()) => {
259 let _ = writeln!(out, " OK");
260 }
261 Err(errs) => {
262 let _ = writeln!(out, " FAIL ({} errors)", errs.len());
263 for e in errs {
264 let _ = writeln!(out, " {:?}", e);
265 }
266 }
267 }
268}
269
270fn build_full_report_fix_text(
271 verify_input: &verify::VerifyResult,
272 verify_output: Option<&verify::VerifyResult>,
273) -> String {
274 let mut messages = Vec::new();
275 push_verify_fix_text("input", verify_input, &mut messages);
276 if let Some(verify_output) = verify_output {
277 push_verify_fix_text("output", verify_output, &mut messages);
278 }
279 messages.join(" ")
280}
281
282fn push_verify_fix_text(
283 stage: &str,
284 result: &verify::VerifyResult,
285 messages: &mut Vec<String>,
286) {
287 if let Err(errs) = result {
288 if errs.is_empty() {
289 messages.push(format!(
290 "Fix: {stage} descriptor verification returned an empty error list; treat this as a verifier contract bug and preserve the descriptor for triage."
291 ));
292 } else {
293 messages.push(format!(
294 "Fix: {stage} descriptor verification failed with {} error(s); repair the descriptor before emitting benchmark evidence. First error: {:?}",
295 errs.len(),
296 errs[0]
297 ));
298 }
299 }
300}
301
302impl std::fmt::Display for FullReport {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 f.write_str(&self.format_short())
305 }
306}
307pub use verify::{verify, VerifyError, VerifyErrorKind, VerifyResult};
308
309pub use descriptor::{
310 BindingLayout, BindingSlot, BindingVisibility, DescriptorIntent, DescriptorIntentError,
311 DescriptorIntentEvidence, DescriptorIntentKind, DescriptorIntentSet,
312 DescriptorIntentStrategy, Dispatch, IntentAnnotatedDescriptor, KernelBody,
313 KernelDescriptor, KernelOp, KernelOpKind, LiteralValue, MatrixMmaElement,
314 MatrixMmaLayout, MatrixMmaShape, MemoryClass, OpaqueExprData, OpaqueNodeData,
315 scan_construct_intent_mapping, ScanConstructIntentClass, ScanConstructIntentMapping,
316 DESCRIPTOR_INTENT_SCHEMA_VERSION, SCAN_CONSTRUCT_INTENT_MAPPINGS, TRAP_SIDECAR_NAME,
317 TRAP_SIDECAR_WORDS,
318};
319pub use error::LowerError;
320pub use vyre_foundation::ir::SubgroupReduceOp;
323pub use lower::lower;
324pub use pre_emit::{lower_for_emit, prepare_program_for_emit, LoweredKernel, PreEmitError};
325
326#[cfg(test)]
327mod verify_then_optimize_tests {
328 use super::*;
329
330 #[test]
331 fn valid_input_returns_optimized_and_stats() {
332 let desc = KernelDescriptor {
333 id: "k".into(),
334 bindings: BindingLayout { slots: vec![] },
335 dispatch: Dispatch::new(64, 1, 1),
336 body: KernelBody {
337 ops: vec![KernelOp {
338 kind: KernelOpKind::Literal,
339 operands: vec![0],
340 result: Some(0),
341 }],
342 child_bodies: vec![],
343 literals: vec![LiteralValue::U32(7)],
344 },
345 };
346 let (out, stats) = verify_then_optimize(&desc).unwrap();
347 assert_eq!(out.id, "k");
348 assert!(stats.iterations >= 1);
349 }
350
351 #[test]
352 fn invalid_input_returns_input_failure() {
353 let desc = KernelDescriptor {
355 id: "bad".into(),
356 bindings: BindingLayout { slots: vec![] },
357 dispatch: Dispatch::new(0, 1, 1),
358 body: KernelBody {
359 ops: vec![],
360 child_bodies: vec![],
361 literals: vec![],
362 },
363 };
364 let r = verify_then_optimize(&desc);
365 assert!(matches!(r, Err(VerifyFailure::Input(_))));
366 }
367
368 #[test]
369 fn full_report_runs_every_layer_without_panic() {
370 let desc = KernelDescriptor {
371 id: "fr".into(),
372 bindings: BindingLayout { slots: vec![] },
373 dispatch: Dispatch::new(64, 1, 1),
374 body: KernelBody {
375 ops: vec![
376 KernelOp {
377 kind: KernelOpKind::Literal,
378 operands: vec![0],
379 result: Some(0),
380 },
381 KernelOp {
382 kind: KernelOpKind::Literal,
383 operands: vec![0],
384 result: Some(1),
385 },
386 ],
387 child_bodies: vec![],
388 literals: vec![LiteralValue::U32(7)],
389 },
390 };
391 let report = full_report(&desc);
392 assert_eq!(report.descriptor_id, "fr");
393 assert!(report.summary.contains("fr:"));
394 assert_eq!(report.histogram.literal, 2);
395 assert_eq!(report.perf.kernel_id, "fr");
396 assert!(report.verify_input.is_ok());
397 assert!(report.verify_output.is_ok());
398 assert!(report.optimization_ran);
399 assert_eq!(report.verify_input_status(), "OK");
400 assert_eq!(report.verify_output_status(), "OK");
401 assert!(report.stats.iterations >= 1);
402 assert!(report.fix_text.is_empty());
403 let s = format!("{report}");
405 assert!(s.contains("fr:"));
406 assert!(s.contains("id fr"));
407 assert!(s.contains("OK"));
408 }
409
410 #[test]
411 fn full_report_serializes_to_json() {
412 let desc = KernelDescriptor {
413 id: "fr".into(),
414 bindings: BindingLayout { slots: vec![] },
415 dispatch: Dispatch::new(64, 1, 1),
416 body: KernelBody {
417 ops: vec![KernelOp {
418 kind: KernelOpKind::Literal,
419 operands: vec![0],
420 result: Some(0),
421 }],
422 child_bodies: vec![],
423 literals: vec![LiteralValue::U32(7)],
424 },
425 };
426 let report = full_report(&desc);
427 assert_eq!(report.descriptor_id, "fr");
428 let json = serde_json::to_string(&report).expect("Fix: serialize");
429 assert!(json.contains("\"descriptor_id\""));
430 assert!(json.contains("\"summary\""));
431 assert!(json.contains("\"histogram\""));
432 assert!(json.contains("\"perf\""));
433 assert!(json.contains("\"verify_input\""));
434 assert!(json.contains("\"verify_output\""));
435 assert!(json.contains("\"stats\""));
436 assert!(json.contains("\"optimization_ran\""));
437 assert!(json.contains("\"fix_text\""));
438
439 let _back: FullReport = serde_json::from_str(&json).expect("Fix: round-trip");
441 }
442
443 #[test]
444 fn full_report_format_long_includes_all_sections() {
445 let desc = KernelDescriptor {
446 id: "fr".into(),
447 bindings: BindingLayout { slots: vec![] },
448 dispatch: Dispatch::new(64, 1, 1),
449 body: KernelBody {
450 ops: vec![KernelOp {
451 kind: KernelOpKind::Literal,
452 operands: vec![0],
453 result: Some(0),
454 }],
455 child_bodies: vec![],
456 literals: vec![LiteralValue::U32(7)],
457 },
458 };
459 let r = full_report(&desc);
460 let long = r.format_long();
461 assert!(long.contains("Kernel:"));
462 assert!(long.contains("descriptor id: fr"));
463 assert!(long.contains("Histogram:"));
464 assert!(long.contains("Perf audit:"));
465 assert!(long.contains("Optimization:"));
466 assert!(long.contains("Verify (input):"));
467 assert!(long.contains("Verify (output):"));
468 assert!(long.contains("OK"));
469 }
470
471 #[test]
472 fn full_report_records_verify_fix_text_for_bad_descriptor() {
473 let desc = KernelDescriptor {
474 id: "bad".into(),
475 bindings: BindingLayout { slots: vec![] },
476 dispatch: Dispatch::new(0, 1, 1),
477 body: KernelBody {
478 ops: vec![],
479 child_bodies: vec![],
480 literals: vec![],
481 },
482 };
483 let report = full_report(&desc);
484 assert_eq!(report.descriptor_id, "bad");
485 assert_eq!(report.verify_input_status(), "FAIL");
486 assert_eq!(report.verify_output_status(), "SKIPPED");
487 assert!(!report.optimization_ran);
488 assert_eq!(report.stats.iterations, 0);
489 assert!(
490 report
491 .fix_text
492 .contains("Fix: input descriptor verification failed"),
493 "Fix: invalid descriptor reports must carry operator-actionable verifier repair text."
494 );
495 let long = report.format_long();
496 assert!(long.contains("Verify (input):"));
497 assert!(long.contains("Verify (output):"));
498 assert!(long.contains("Fix:"));
499 }
500
501 #[test]
502 fn errors_accessor_yields_underlying() {
503 let desc = KernelDescriptor {
504 id: "bad".into(),
505 bindings: BindingLayout { slots: vec![] },
506 dispatch: Dispatch::new(0, 1, 1),
507 body: KernelBody {
508 ops: vec![],
509 child_bodies: vec![],
510 literals: vec![],
511 },
512 };
513 let f = verify_then_optimize(&desc).unwrap_err();
514 assert_ne!(f.errors().len(), 0);
515 }
516}