Skip to main content

oxilean_codegen/futhark_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet};
6
7/// Futhark reverse
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct FutharkReverse {
11    pub array: String,
12}
13/// Futhark concat
14#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct FutharkConcat {
17    pub arrays: Vec<String>,
18    pub dim: Option<usize>,
19}
20/// Futhark pass timing
21#[allow(dead_code)]
22#[derive(Debug, Default, Clone)]
23pub struct FutharkExtPassTiming {
24    pub pass_name: String,
25    pub duration_us: u64,
26}
27/// Futhark loop expression
28#[allow(dead_code)]
29#[derive(Debug, Clone)]
30pub struct FutharkLoop {
31    pub kind: FutharkLoopKind,
32    pub params: Vec<(String, FutharkType, String)>,
33    pub body: String,
34}
35/// Futhark zip / unzip
36#[allow(dead_code)]
37#[derive(Debug, Clone)]
38pub struct FutharkZip {
39    pub arrays: Vec<String>,
40}
41/// Futhark id generator
42#[allow(dead_code)]
43#[derive(Debug, Default)]
44pub struct FutharkExtIdGen {
45    pub(super) counter: u64,
46    pub(super) prefix: String,
47}
48#[allow(dead_code)]
49impl FutharkExtIdGen {
50    pub fn new(prefix: &str) -> Self {
51        Self {
52            counter: 0,
53            prefix: prefix.to_string(),
54        }
55    }
56    pub fn next(&mut self) -> String {
57        let id = self.counter;
58        self.counter += 1;
59        format!("{}_{}", self.prefix, id)
60    }
61    pub fn reset(&mut self) {
62        self.counter = 0;
63    }
64}
65/// Futhark kernel launch params
66#[allow(dead_code)]
67#[derive(Debug, Clone)]
68pub struct FutharkKernelLaunch {
69    pub kernel_name: String,
70    pub global_size: Vec<u64>,
71    pub local_size: Vec<u64>,
72    pub shared_mem_bytes: u64,
73}
74/// Futhark name mangler for OxiLean → Futhark identifiers
75#[allow(dead_code)]
76#[derive(Debug, Default)]
77pub struct FutharkNameMangler {
78    pub used: std::collections::HashSet<String>,
79    pub map: std::collections::HashMap<String, String>,
80}
81#[allow(dead_code)]
82impl FutharkNameMangler {
83    pub fn new() -> Self {
84        Self::default()
85    }
86    pub fn mangle(&mut self, name: &str) -> String {
87        if let Some(m) = self.map.get(name) {
88            return m.clone();
89        }
90        let mut mangled: String = name
91            .chars()
92            .map(|c| {
93                if c.is_alphanumeric() || c == '_' {
94                    c
95                } else {
96                    '_'
97                }
98            })
99            .collect();
100        let reserved = [
101            "let", "in", "if", "then", "else", "loop", "for", "while", "do", "fun", "entry",
102            "type", "module", "open", "import", "val", "include", "match", "case", "true", "false",
103            "local",
104        ];
105        if reserved.contains(&mangled.as_str()) || mangled.starts_with(|c: char| c.is_ascii_digit())
106        {
107            mangled = format!("ox_{}", mangled);
108        }
109        let mut candidate = mangled.clone();
110        let mut counter = 0;
111        while self.used.contains(&candidate) {
112            counter += 1;
113            candidate = format!("{}_{}", mangled, counter);
114        }
115        self.used.insert(candidate.clone());
116        self.map.insert(name.to_string(), candidate.clone());
117        candidate
118    }
119    pub fn reset(&mut self) {
120        self.used.clear();
121        self.map.clear();
122    }
123}
124/// Futhark map2 (map with 2 arrays)
125#[allow(dead_code)]
126#[derive(Debug, Clone)]
127pub struct FutharkMap2Expr {
128    pub func: String,
129    pub arr1: String,
130    pub arr2: String,
131}
132#[allow(dead_code)]
133#[derive(Debug, Clone)]
134pub struct FutharkDiag {
135    pub level: FutharkDiagLevel,
136    pub message: String,
137    pub location: Option<String>,
138}
139/// A Futhark type alias: `type t = ...`
140#[derive(Debug, Clone)]
141pub struct FutharkTypeAlias {
142    /// Alias name
143    pub name: String,
144    /// Type parameters
145    pub params: Vec<String>,
146    /// The aliased type
147    pub ty: FutharkType,
148    /// Whether the type is opaque (abstract)
149    pub is_opaque: bool,
150}
151/// Futhark tuning parameter
152#[allow(dead_code)]
153#[derive(Debug, Clone)]
154pub struct FutharkTuningParam {
155    pub name: String,
156    pub default_value: i64,
157    pub min_value: i64,
158    pub max_value: i64,
159    pub description: String,
160}
161/// Futhark statement (top-level body items inside functions).
162#[derive(Debug, Clone)]
163pub enum FutharkStmt {
164    /// `let x = e`
165    LetBinding(String, Option<FutharkType>, FutharkExpr),
166    /// `let (x, y) = e`
167    LetTupleBinding(Vec<String>, FutharkExpr),
168    /// Loop binding: `loop (acc = init) for i < n do body`
169    LoopBinding(String, FutharkExpr, String, FutharkExpr, Vec<FutharkStmt>),
170    /// Return expression (final expression in block)
171    ReturnExpr(FutharkExpr),
172    /// Comment
173    Comment(String),
174}
175/// Futhark source buffer (accumulates emitted code)
176#[allow(dead_code)]
177#[derive(Debug, Default)]
178pub struct FutharkExtSourceBuffer {
179    pub sections: Vec<(String, String)>,
180    pub current: String,
181}
182#[allow(dead_code)]
183impl FutharkExtSourceBuffer {
184    pub fn new() -> Self {
185        Self::default()
186    }
187    pub fn write(&mut self, s: &str) {
188        self.current.push_str(s);
189    }
190    pub fn writeln(&mut self, s: &str) {
191        self.current.push_str(s);
192        self.current.push('\n');
193    }
194    pub fn begin_section(&mut self, name: &str) {
195        let done = std::mem::take(&mut self.current);
196        if !done.is_empty() {
197            self.sections.push(("anonymous".to_string(), done));
198        }
199        self.current = format!("-- === {} ===\n", name);
200    }
201    pub fn finish(self) -> String {
202        let mut out = String::new();
203        for (_, sec) in &self.sections {
204            out.push_str(sec);
205        }
206        out.push_str(&self.current);
207        out
208    }
209}
210/// Futhark slice expression
211#[allow(dead_code)]
212#[derive(Debug, Clone)]
213pub struct FutharkSliceExpr {
214    pub array: String,
215    pub start: Option<String>,
216    pub end: Option<String>,
217    pub stride: Option<String>,
218}
219/// Futhark profiling result
220#[allow(dead_code)]
221#[derive(Debug, Clone)]
222pub struct FutharkProfileResult {
223    pub kernel_name: String,
224    pub runs: u64,
225    pub total_us: u64,
226    pub min_us: u64,
227    pub max_us: u64,
228    pub mean_us: f64,
229}
230/// Futhark let binding
231#[allow(dead_code)]
232#[derive(Debug, Clone)]
233pub struct FutharkLetBinding {
234    pub pattern: String,
235    pub type_ann: Option<FutharkType>,
236    pub value: String,
237    pub body: String,
238}
239/// Futhark flatten / unflatten
240#[allow(dead_code)]
241#[derive(Debug, Clone)]
242pub struct FutharkFlatten {
243    pub array: String,
244}
245#[allow(dead_code)]
246#[derive(Debug, Default)]
247pub struct FutharkExtProfiler {
248    pub timings: Vec<FutharkExtPassTiming>,
249}
250#[allow(dead_code)]
251impl FutharkExtProfiler {
252    pub fn new() -> Self {
253        Self::default()
254    }
255    pub fn record(&mut self, pass: &str, us: u64) {
256        self.timings.push(FutharkExtPassTiming {
257            pass_name: pass.to_string(),
258            duration_us: us,
259        });
260    }
261    pub fn total_us(&self) -> u64 {
262        self.timings.iter().map(|t| t.duration_us).sum()
263    }
264    pub fn slowest_pass(&self) -> Option<&FutharkExtPassTiming> {
265        self.timings.iter().max_by_key(|t| t.duration_us)
266    }
267}
268/// A Futhark source module (single `.fut` file).
269#[derive(Debug, Clone)]
270pub struct FutharkModule {
271    /// Module-level `open` directives
272    pub opens: Vec<String>,
273    /// Type aliases
274    pub types: Vec<FutharkTypeAlias>,
275    /// Function definitions (including entry points)
276    pub funs: Vec<FutharkFun>,
277    /// Module-level constants: `let c = e`
278    pub constants: Vec<(String, FutharkType, FutharkExpr)>,
279    /// Module-level doc comment
280    pub doc: Option<String>,
281}
282impl FutharkModule {
283    /// Create an empty module.
284    pub fn new() -> Self {
285        FutharkModule {
286            opens: vec![],
287            types: vec![],
288            funs: vec![],
289            constants: vec![],
290            doc: None,
291        }
292    }
293    /// Add an `open` directive.
294    pub fn add_open(&mut self, name: impl Into<String>) {
295        self.opens.push(name.into());
296    }
297    /// Add a type alias.
298    pub fn add_type(&mut self, alias: FutharkTypeAlias) {
299        self.types.push(alias);
300    }
301    /// Add a function.
302    pub fn add_fun(&mut self, fun: FutharkFun) {
303        self.funs.push(fun);
304    }
305    /// Add a module-level constant.
306    pub fn add_constant(&mut self, name: impl Into<String>, ty: FutharkType, expr: FutharkExpr) {
307        self.constants.push((name.into(), ty, expr));
308    }
309    /// Set the module-level doc comment.
310    pub fn set_doc(&mut self, doc: impl Into<String>) {
311        self.doc = Some(doc.into());
312    }
313}
314/// Diagnostics for Futhark code generation
315#[allow(dead_code)]
316#[derive(Debug, Clone, PartialEq)]
317pub enum FutharkDiagLevel {
318    Info,
319    Warning,
320    Error,
321}
322/// Futhark program builder
323#[allow(dead_code)]
324#[derive(Debug, Default)]
325pub struct FutharkProgramBuilder {
326    pub imports: Vec<String>,
327    pub open_imports: Vec<String>,
328    pub type_defs: Vec<String>,
329    pub module_defs: Vec<String>,
330    pub fun_defs: Vec<String>,
331    pub entry_points: Vec<String>,
332}
333#[allow(dead_code)]
334impl FutharkProgramBuilder {
335    pub fn new() -> Self {
336        Self::default()
337    }
338    pub fn add_import(&mut self, path: &str) {
339        self.imports.push(format!("import \"{}\"", path));
340    }
341    pub fn open_import(&mut self, path: &str) {
342        self.open_imports.push(format!("open import \"{}\"", path));
343    }
344    pub fn add_type_alias(&mut self, name: &str, ty: &FutharkType) {
345        self.type_defs.push(format!("type {} = {}", name, ty));
346    }
347    pub fn add_module_alias(&mut self, name: &str, module: &str) {
348        self.module_defs
349            .push(format!("module {} = {}", name, module));
350    }
351    pub fn add_fun(&mut self, fun: &str) {
352        self.fun_defs.push(fun.to_string());
353    }
354    pub fn add_entry(&mut self, entry: &str) {
355        self.entry_points.push(entry.to_string());
356    }
357    pub fn build(&self) -> String {
358        let mut out = String::new();
359        for imp in &self.imports {
360            out.push_str(imp);
361            out.push('\n');
362        }
363        for op in &self.open_imports {
364            out.push_str(op);
365            out.push('\n');
366        }
367        if !self.imports.is_empty() || !self.open_imports.is_empty() {
368            out.push('\n');
369        }
370        for td in &self.type_defs {
371            out.push_str(td);
372            out.push('\n');
373        }
374        for md in &self.module_defs {
375            out.push_str(md);
376            out.push('\n');
377        }
378        if !self.type_defs.is_empty() || !self.module_defs.is_empty() {
379            out.push('\n');
380        }
381        for fd in &self.fun_defs {
382            out.push_str(fd);
383            out.push('\n');
384        }
385        for ep in &self.entry_points {
386            out.push_str(ep);
387            out.push('\n');
388        }
389        out
390    }
391}
392/// Extended Futhark emit stats
393#[allow(dead_code)]
394#[derive(Debug, Default, Clone)]
395pub struct FutharkExtEmitStats {
396    pub bytes_written: usize,
397    pub items_emitted: usize,
398    pub errors: usize,
399    pub warnings: usize,
400}
401/// Futhark tuning file
402#[allow(dead_code)]
403#[derive(Debug, Default)]
404pub struct FutharkTuningFile {
405    pub params: Vec<FutharkTuningParam>,
406    pub program: String,
407}
408#[allow(dead_code)]
409impl FutharkTuningFile {
410    pub fn new(program: &str) -> Self {
411        Self {
412            params: Vec::new(),
413            program: program.to_string(),
414        }
415    }
416    pub fn add_param(&mut self, p: FutharkTuningParam) {
417        self.params.push(p);
418    }
419    pub fn emit(&self) -> String {
420        let mut out = String::new();
421        for p in &self.params {
422            out.push_str(&format!("{} = {}\n", p.name, p.default_value));
423        }
424        out
425    }
426}
427/// Futhark primitive value constant
428#[allow(dead_code)]
429#[derive(Debug, Clone, PartialEq)]
430pub enum FutharkConst {
431    I8(i8),
432    I16(i16),
433    I32(i32),
434    I64(i64),
435    U8(u8),
436    U16(u16),
437    U32(u32),
438    U64(u64),
439    F16(u16),
440    F32(f32),
441    F64(f64),
442    Bool(bool),
443}
444/// Futhark stream_red / stream_map
445#[allow(dead_code)]
446#[derive(Debug, Clone)]
447pub enum FutharkStreamKind {
448    Seq,
449    Par,
450}
451#[allow(dead_code)]
452#[derive(Debug, Clone)]
453pub struct FutharkStreamRed {
454    pub kind: FutharkStreamKind,
455    pub op: String,
456    pub neutral: String,
457    pub func: String,
458    pub array: String,
459}
460/// Futhark index expression
461#[allow(dead_code)]
462#[derive(Debug, Clone)]
463pub struct FutharkIndexExpr {
464    pub array: String,
465    pub index: String,
466}
467/// Futhark iota expression
468#[allow(dead_code)]
469#[derive(Debug, Clone)]
470pub struct FutharkIota {
471    pub n: String,
472    pub start: Option<String>,
473    pub step: Option<String>,
474}
475/// Futhark map expression
476#[allow(dead_code)]
477#[derive(Debug, Clone)]
478pub struct FutharkMapExpr {
479    pub func: String,
480    pub arrays: Vec<String>,
481}
482/// Futhark copy
483#[allow(dead_code)]
484#[derive(Debug, Clone)]
485pub struct FutharkCopy {
486    pub array: String,
487}
488/// Configuration for the Futhark backend emitter.
489#[derive(Debug, Clone)]
490pub struct FutharkConfig {
491    /// Number of spaces per indentation level
492    pub indent_width: usize,
493    /// Emit type annotations on let-bindings when available
494    pub annotate_lets: bool,
495    /// Default integer type for literals without explicit type
496    pub default_int: FutharkType,
497    /// Default float type for literals without explicit type
498    pub default_float: FutharkType,
499}
500/// Futhark unsafe coerce (for performance-critical code)
501#[allow(dead_code)]
502#[derive(Debug, Clone)]
503pub struct FutharkUnsafeCoerce {
504    pub value: String,
505    pub from_type: FutharkType,
506    pub to_type: FutharkType,
507}
508/// Futhark version target
509#[allow(dead_code)]
510#[derive(Debug, Clone, PartialEq, Eq)]
511pub enum FutharkVersion {
512    V020,
513    V021,
514    V022,
515    V023,
516    V024,
517    V025,
518    Latest,
519}
520/// Futhark scatter primitive
521#[allow(dead_code)]
522#[derive(Debug, Clone)]
523pub struct FutharkScatter {
524    pub dest: String,
525    pub indices: String,
526    pub values: String,
527}
528/// Futhark code statistics
529#[allow(dead_code)]
530#[derive(Debug, Default, Clone)]
531pub struct FutharkCodeStats {
532    pub num_functions: usize,
533    pub num_entries: usize,
534    pub num_type_defs: usize,
535    pub num_map_exprs: usize,
536    pub num_reduce_exprs: usize,
537    pub num_scan_exprs: usize,
538    pub num_filter_exprs: usize,
539    pub num_scatter_exprs: usize,
540    pub num_loops: usize,
541    pub num_unsafe: usize,
542}
543/// Futhark size expression
544#[allow(dead_code)]
545#[derive(Debug, Clone)]
546pub struct FutharkSizeExpr {
547    pub dim: usize,
548    pub array: String,
549}
550/// Futhark partition expression
551#[allow(dead_code)]
552#[derive(Debug, Clone)]
553pub struct FutharkPartitionExpr {
554    pub k: usize,
555    pub pred: String,
556    pub array: String,
557}
558/// Futhark pass stats additional
559#[allow(dead_code)]
560#[derive(Debug, Default, Clone)]
561pub struct FutharkPassStats {
562    pub functions_processed: usize,
563    pub maps_emitted: usize,
564    pub reduces_emitted: usize,
565    pub scans_emitted: usize,
566    pub kernels_generated: usize,
567    pub total_parallelism: u64,
568}
569/// Futhark loop form
570#[allow(dead_code)]
571#[derive(Debug, Clone)]
572pub enum FutharkLoopKind {
573    For {
574        var: String,
575        bound: String,
576    },
577    While {
578        cond: String,
579    },
580    ForWhile {
581        var: String,
582        bound: String,
583        cond: String,
584    },
585}
586#[allow(dead_code)]
587#[derive(Debug, Clone)]
588pub struct FutharkUnzip {
589    pub array: String,
590}
591/// Attributes that can annotate a Futhark function.
592#[derive(Debug, Clone, PartialEq)]
593pub enum FutharkAttr {
594    /// `#[inline]`
595    Inline,
596    /// `#[noinline]`
597    NoInline,
598    /// `#[nomap]` — prevent automatic parallelisation
599    NoMap,
600    /// `#[sequential]`
601    Sequential,
602    /// Custom attribute string
603    Custom(String),
604}
605#[allow(dead_code)]
606#[derive(Debug, Clone)]
607pub struct FutharkMatchExpr {
608    pub scrutinee: String,
609    pub arms: Vec<FutharkMatchArm>,
610}
611/// Futhark rotate
612#[allow(dead_code)]
613#[derive(Debug, Clone)]
614pub struct FutharkRotate {
615    pub dim: usize,
616    pub amount: String,
617    pub array: String,
618}
619/// Futhark memory block (for GPU memory management)
620#[allow(dead_code)]
621#[derive(Debug, Clone)]
622pub struct FutharkMemBlock {
623    pub block_id: u32,
624    pub size_bytes: u64,
625    pub device: String,
626    pub is_pinned: bool,
627}
628/// Futhark feature flags
629#[allow(dead_code)]
630#[derive(Debug, Clone, Default)]
631pub struct FutharkFeatureFlags {
632    pub enable_unsafe: bool,
633    pub enable_in_place_updates: bool,
634    pub enable_streaming: bool,
635    pub enable_loop_fusion: bool,
636    pub enable_double_buffering: bool,
637}
638/// Futhark match expression (introduced in Futhark 0.24)
639#[allow(dead_code)]
640#[derive(Debug, Clone)]
641pub struct FutharkMatchArm {
642    pub pattern: String,
643    pub body: String,
644}
645/// A Futhark function (or entry point).
646#[derive(Debug, Clone)]
647pub struct FutharkFun {
648    /// Function name
649    pub name: String,
650    /// Type parameters (e.g., `'t`)
651    pub type_params: Vec<String>,
652    /// Parameters: `(name : type)`
653    pub params: Vec<(String, FutharkType)>,
654    /// Return type
655    pub return_type: FutharkType,
656    /// Body statements
657    pub body: Vec<FutharkStmt>,
658    /// Whether this is an `entry` point
659    pub is_entry: bool,
660    /// Function attributes
661    pub attrs: Vec<FutharkAttr>,
662}
663impl FutharkFun {
664    /// Create a new regular function.
665    pub fn new(
666        name: impl Into<String>,
667        params: Vec<(String, FutharkType)>,
668        return_type: FutharkType,
669        body: Vec<FutharkStmt>,
670    ) -> Self {
671        FutharkFun {
672            name: name.into(),
673            type_params: vec![],
674            params,
675            return_type,
676            body,
677            is_entry: false,
678            attrs: vec![],
679        }
680    }
681    /// Create a new entry point.
682    pub fn entry(
683        name: impl Into<String>,
684        params: Vec<(String, FutharkType)>,
685        return_type: FutharkType,
686        body: Vec<FutharkStmt>,
687    ) -> Self {
688        FutharkFun {
689            name: name.into(),
690            type_params: vec![],
691            params,
692            return_type,
693            body,
694            is_entry: true,
695            attrs: vec![],
696        }
697    }
698    /// Add a type parameter.
699    pub fn with_type_param(mut self, tp: impl Into<String>) -> Self {
700        self.type_params.push(tp.into());
701        self
702    }
703    /// Add an attribute.
704    pub fn with_attr(mut self, attr: FutharkAttr) -> Self {
705        self.attrs.push(attr);
706        self
707    }
708}
709/// Backend state for emitting Futhark source code.
710pub struct FutharkBackend {
711    /// Output buffer
712    pub(super) buf: String,
713    /// Current indentation level
714    pub(super) indent: usize,
715    /// Indentation string (spaces per level)
716    pub(super) indent_str: String,
717    /// Configuration options
718    pub(super) config: FutharkConfig,
719}
720impl FutharkBackend {
721    /// Create a new backend with default configuration.
722    pub fn new() -> Self {
723        FutharkBackend::with_config(FutharkConfig::default())
724    }
725    /// Create a new backend with custom configuration.
726    pub fn with_config(config: FutharkConfig) -> Self {
727        let indent_str = " ".repeat(config.indent_width);
728        FutharkBackend {
729            buf: String::new(),
730            indent: 0,
731            indent_str,
732            config,
733        }
734    }
735    pub(super) fn push(&mut self, s: &str) {
736        self.buf.push_str(s);
737    }
738    pub(super) fn push_char(&mut self, c: char) {
739        self.buf.push(c);
740    }
741    pub(super) fn newline(&mut self) {
742        self.buf.push('\n');
743    }
744    pub(super) fn emit_indent(&mut self) {
745        for _ in 0..self.indent {
746            self.buf.push_str(&self.indent_str.clone());
747        }
748    }
749    pub(super) fn emit_line(&mut self, s: &str) {
750        self.emit_indent();
751        self.push(s);
752        self.newline();
753    }
754    pub(super) fn indent_in(&mut self) {
755        self.indent += 1;
756    }
757    pub(super) fn indent_out(&mut self) {
758        if self.indent > 0 {
759            self.indent -= 1;
760        }
761    }
762    /// Emit a Futhark type to the buffer.
763    pub fn emit_type(&mut self, ty: &FutharkType) {
764        let s = ty.to_string();
765        self.push(&s);
766    }
767    /// Emit a Futhark expression to the buffer.
768    pub fn emit_expr(&mut self, expr: &FutharkExpr) {
769        match expr {
770            FutharkExpr::IntLit(n, ty) => {
771                self.push(&n.to_string());
772                self.push(&ty.to_string());
773            }
774            FutharkExpr::FloatLit(v, ty) => {
775                self.push(&format!("{v}"));
776                self.push(&ty.to_string());
777            }
778            FutharkExpr::BoolLit(b) => {
779                self.push(if *b { "true" } else { "false" });
780            }
781            FutharkExpr::Var(name) => {
782                self.push(name);
783            }
784            FutharkExpr::ArrayLit(elems) => {
785                self.push("[");
786                for (i, e) in elems.iter().enumerate() {
787                    if i > 0 {
788                        self.push(", ");
789                    }
790                    self.emit_expr(e);
791                }
792                self.push("]");
793            }
794            FutharkExpr::Index(arr, idx) => {
795                self.emit_expr_paren(arr);
796                self.push("[");
797                self.emit_expr(idx);
798                self.push("]");
799            }
800            FutharkExpr::Slice(arr, lo, hi) => {
801                self.emit_expr_paren(arr);
802                self.push("[");
803                if let Some(l) = lo {
804                    self.emit_expr(l);
805                }
806                self.push(":");
807                if let Some(h) = hi {
808                    self.emit_expr(h);
809                }
810                self.push("]");
811            }
812            FutharkExpr::Map(f, a) => {
813                self.push("map ");
814                self.emit_expr_paren(f);
815                self.push(" ");
816                self.emit_expr_paren(a);
817            }
818            FutharkExpr::Map2(f, a, b) => {
819                self.push("map2 ");
820                self.emit_expr_paren(f);
821                self.push(" ");
822                self.emit_expr_paren(a);
823                self.push(" ");
824                self.emit_expr_paren(b);
825            }
826            FutharkExpr::Reduce(op, ne, a) => {
827                self.push("reduce ");
828                self.emit_expr_paren(op);
829                self.push(" ");
830                self.emit_expr_paren(ne);
831                self.push(" ");
832                self.emit_expr_paren(a);
833            }
834            FutharkExpr::Scan(op, ne, a) => {
835                self.push("scan ");
836                self.emit_expr_paren(op);
837                self.push(" ");
838                self.emit_expr_paren(ne);
839                self.push(" ");
840                self.emit_expr_paren(a);
841            }
842            FutharkExpr::Filter(f, a) => {
843                self.push("filter ");
844                self.emit_expr_paren(f);
845                self.push(" ");
846                self.emit_expr_paren(a);
847            }
848            FutharkExpr::Zip(a, b) => {
849                self.push("zip ");
850                self.emit_expr_paren(a);
851                self.push(" ");
852                self.emit_expr_paren(b);
853            }
854            FutharkExpr::Unzip(a) => {
855                self.push("unzip ");
856                self.emit_expr_paren(a);
857            }
858            FutharkExpr::Iota(n) => {
859                self.push("iota ");
860                self.emit_expr_paren(n);
861            }
862            FutharkExpr::Replicate(n, x) => {
863                self.push("replicate ");
864                self.emit_expr_paren(n);
865                self.push(" ");
866                self.emit_expr_paren(x);
867            }
868            FutharkExpr::IfThenElse(cond, t, e) => {
869                self.push("if ");
870                self.emit_expr(cond);
871                self.push(" then ");
872                self.emit_expr(t);
873                self.push(" else ");
874                self.emit_expr(e);
875            }
876            FutharkExpr::Lambda(params, body) => {
877                self.push("\\");
878                for (i, (name, ty)) in params.iter().enumerate() {
879                    if i > 0 {
880                        self.push(" ");
881                    }
882                    self.push("(");
883                    self.push(name);
884                    self.push(": ");
885                    self.emit_type(ty);
886                    self.push(")");
887                }
888                self.push(" -> ");
889                self.emit_expr(body);
890            }
891            FutharkExpr::LetIn(name, ty, val, body) => {
892                self.push("let ");
893                self.push(name);
894                if let Some(t) = ty {
895                    if self.config.annotate_lets {
896                        self.push(": ");
897                        self.emit_type(t);
898                    }
899                }
900                self.push(" = ");
901                self.emit_expr(val);
902                self.push(" in ");
903                self.emit_expr(body);
904            }
905            FutharkExpr::Loop(acc, init, var, bound, body) => {
906                self.push("loop (");
907                self.push(acc);
908                self.push(" = ");
909                self.emit_expr(init);
910                self.push(") for ");
911                self.push(var);
912                self.push(" < ");
913                self.emit_expr(bound);
914                self.push(" do ");
915                self.emit_expr(body);
916            }
917            FutharkExpr::BinOp(op, lhs, rhs) => {
918                self.emit_expr_paren(lhs);
919                self.push(" ");
920                self.push(op);
921                self.push(" ");
922                self.emit_expr_paren(rhs);
923            }
924            FutharkExpr::UnOp(op, e) => {
925                self.push(op);
926                self.emit_expr_paren(e);
927            }
928            FutharkExpr::Apply(f, args) => {
929                self.emit_expr_paren(f);
930                for arg in args {
931                    self.push(" ");
932                    self.emit_expr_paren(arg);
933                }
934            }
935            FutharkExpr::TupleLit(elems) => {
936                self.push("(");
937                for (i, e) in elems.iter().enumerate() {
938                    if i > 0 {
939                        self.push(", ");
940                    }
941                    self.emit_expr(e);
942                }
943                self.push(")");
944            }
945            FutharkExpr::RecordLit(fields) => {
946                self.push("{");
947                for (i, (name, e)) in fields.iter().enumerate() {
948                    if i > 0 {
949                        self.push(", ");
950                    }
951                    self.push(name);
952                    self.push(" = ");
953                    self.emit_expr(e);
954                }
955                self.push("}");
956            }
957            FutharkExpr::FieldAccess(rec, field) => {
958                self.emit_expr_paren(rec);
959                self.push(".");
960                self.push(field);
961            }
962            FutharkExpr::Ascribe(e, ty) => {
963                self.push("(");
964                self.emit_expr(e);
965                self.push(" : ");
966                self.emit_type(ty);
967                self.push(")");
968            }
969            FutharkExpr::Scatter(a, is, vs) => {
970                self.push("scatter ");
971                self.emit_expr_paren(a);
972                self.push(" ");
973                self.emit_expr_paren(is);
974                self.push(" ");
975                self.emit_expr_paren(vs);
976            }
977            FutharkExpr::Rotate(n, a) => {
978                self.push("rotate ");
979                self.emit_expr_paren(n);
980                self.push(" ");
981                self.emit_expr_paren(a);
982            }
983            FutharkExpr::Transpose(a) => {
984                self.push("transpose ");
985                self.emit_expr_paren(a);
986            }
987            FutharkExpr::Flatten(a) => {
988                self.push("flatten ");
989                self.emit_expr_paren(a);
990            }
991            FutharkExpr::Unflatten(n, m, a) => {
992                self.push("unflatten ");
993                self.emit_expr_paren(n);
994                self.push(" ");
995                self.emit_expr_paren(m);
996                self.push(" ");
997                self.emit_expr_paren(a);
998            }
999            FutharkExpr::Size(dim, e) => {
1000                self.push(&format!("#{dim}("));
1001                self.emit_expr(e);
1002                self.push(")");
1003            }
1004            FutharkExpr::With(arr, idx, val) => {
1005                self.emit_expr_paren(arr);
1006                self.push(" with [");
1007                self.emit_expr(idx);
1008                self.push("] = ");
1009                self.emit_expr(val);
1010            }
1011        }
1012    }
1013    /// Emit an expression, parenthesised when it is complex.
1014    pub(super) fn emit_expr_paren(&mut self, expr: &FutharkExpr) {
1015        let needs_paren = matches!(
1016            expr,
1017            FutharkExpr::Lambda(..)
1018                | FutharkExpr::LetIn(..)
1019                | FutharkExpr::Loop(..)
1020                | FutharkExpr::IfThenElse(..)
1021                | FutharkExpr::BinOp(..)
1022                | FutharkExpr::Map(..)
1023                | FutharkExpr::Map2(..)
1024                | FutharkExpr::Reduce(..)
1025                | FutharkExpr::Scan(..)
1026                | FutharkExpr::Filter(..)
1027                | FutharkExpr::Apply(..)
1028                | FutharkExpr::Scatter(..)
1029                | FutharkExpr::Unflatten(..)
1030        );
1031        if needs_paren {
1032            self.push("(");
1033            self.emit_expr(expr);
1034            self.push(")");
1035        } else {
1036            self.emit_expr(expr);
1037        }
1038    }
1039    /// Emit a single Futhark statement.
1040    pub fn emit_stmt(&mut self, stmt: &FutharkStmt) {
1041        match stmt {
1042            FutharkStmt::LetBinding(name, ty, expr) => {
1043                self.emit_indent();
1044                self.push("let ");
1045                self.push(name);
1046                if let Some(t) = ty {
1047                    if self.config.annotate_lets {
1048                        self.push(": ");
1049                        self.emit_type(t);
1050                    }
1051                }
1052                self.push(" = ");
1053                self.emit_expr(expr);
1054                self.newline();
1055            }
1056            FutharkStmt::LetTupleBinding(names, expr) => {
1057                self.emit_indent();
1058                self.push("let (");
1059                for (i, n) in names.iter().enumerate() {
1060                    if i > 0 {
1061                        self.push(", ");
1062                    }
1063                    self.push(n);
1064                }
1065                self.push(") = ");
1066                self.emit_expr(expr);
1067                self.newline();
1068            }
1069            FutharkStmt::LoopBinding(acc, init, var, bound, body) => {
1070                self.emit_indent();
1071                self.push("let ");
1072                self.push(acc);
1073                self.push(" = loop (");
1074                self.push(acc);
1075                self.push(" = ");
1076                self.emit_expr(init);
1077                self.push(") for ");
1078                self.push(var);
1079                self.push(" < ");
1080                self.emit_expr(bound);
1081                self.push(" do");
1082                self.newline();
1083                self.indent_in();
1084                for s in body {
1085                    self.emit_stmt(s);
1086                }
1087                self.indent_out();
1088            }
1089            FutharkStmt::ReturnExpr(expr) => {
1090                self.emit_indent();
1091                self.emit_expr(expr);
1092                self.newline();
1093            }
1094            FutharkStmt::Comment(text) => {
1095                self.emit_indent();
1096                self.push("-- ");
1097                self.push(text);
1098                self.newline();
1099            }
1100        }
1101    }
1102    /// Emit a Futhark function definition.
1103    pub fn emit_fun(&mut self, fun: &FutharkFun) {
1104        for attr in &fun.attrs {
1105            self.emit_line(&attr.to_string());
1106        }
1107        self.emit_indent();
1108        if fun.is_entry {
1109            self.push("entry ");
1110        } else {
1111            self.push("let ");
1112        }
1113        self.push(&fun.name);
1114        for tp in &fun.type_params {
1115            self.push(" '");
1116            self.push(tp);
1117        }
1118        for (pname, pty) in &fun.params {
1119            self.push(" (");
1120            self.push(pname);
1121            self.push(": ");
1122            self.emit_type(pty);
1123            self.push(")");
1124        }
1125        self.push(": ");
1126        self.emit_type(&fun.return_type.clone());
1127        self.push(" =");
1128        self.newline();
1129        self.indent_in();
1130        for stmt in &fun.body {
1131            self.emit_stmt(stmt);
1132        }
1133        self.indent_out();
1134        self.newline();
1135    }
1136    /// Emit a type alias definition.
1137    pub fn emit_type_alias(&mut self, alias: &FutharkTypeAlias) {
1138        self.emit_indent();
1139        if alias.is_opaque {
1140            self.push("type^ ");
1141        } else {
1142            self.push("type ");
1143        }
1144        self.push(&alias.name);
1145        for p in &alias.params {
1146            self.push(" '");
1147            self.push(p);
1148        }
1149        self.push(" = ");
1150        self.emit_type(&alias.ty.clone());
1151        self.newline();
1152    }
1153    /// Emit an entire Futhark module.
1154    pub fn emit_module(&mut self, module: &FutharkModule) {
1155        if let Some(doc) = &module.doc.clone() {
1156            for line in doc.lines() {
1157                self.push("-- | ");
1158                self.push(line);
1159                self.newline();
1160            }
1161            self.newline();
1162        }
1163        for open in &module.opens.clone() {
1164            self.emit_line(&format!("open {open}"));
1165        }
1166        if !module.opens.is_empty() {
1167            self.newline();
1168        }
1169        for alias in &module.types.clone() {
1170            self.emit_type_alias(alias);
1171        }
1172        if !module.types.is_empty() {
1173            self.newline();
1174        }
1175        for (name, ty, expr) in &module.constants.clone() {
1176            self.emit_indent();
1177            self.push("let ");
1178            self.push(name);
1179            self.push(": ");
1180            self.emit_type(ty);
1181            self.push(" = ");
1182            self.emit_expr(expr);
1183            self.newline();
1184        }
1185        if !module.constants.is_empty() {
1186            self.newline();
1187        }
1188        for fun in &module.funs.clone() {
1189            self.emit_fun(fun);
1190        }
1191    }
1192    /// Return the generated source and reset the buffer.
1193    pub fn finish(&mut self) -> String {
1194        std::mem::take(&mut self.buf)
1195    }
1196    /// Generate a complete `.fut` file from a module.
1197    pub fn generate(module: &FutharkModule) -> String {
1198        let mut backend = FutharkBackend::new();
1199        backend.emit_module(module);
1200        backend.finish()
1201    }
1202    /// Generate with custom configuration.
1203    pub fn generate_with_config(module: &FutharkModule, config: FutharkConfig) -> String {
1204        let mut backend = FutharkBackend::with_config(config);
1205        backend.emit_module(module);
1206        backend.finish()
1207    }
1208}
1209#[allow(dead_code)]
1210#[derive(Debug, Default)]
1211pub struct FutharkDiagSink {
1212    pub diags: Vec<FutharkDiag>,
1213}
1214#[allow(dead_code)]
1215impl FutharkDiagSink {
1216    pub fn new() -> Self {
1217        Self::default()
1218    }
1219    pub fn info(&mut self, msg: &str) {
1220        self.diags.push(FutharkDiag {
1221            level: FutharkDiagLevel::Info,
1222            message: msg.to_string(),
1223            location: None,
1224        });
1225    }
1226    pub fn warn(&mut self, msg: &str) {
1227        self.diags.push(FutharkDiag {
1228            level: FutharkDiagLevel::Warning,
1229            message: msg.to_string(),
1230            location: None,
1231        });
1232    }
1233    pub fn error(&mut self, msg: &str) {
1234        self.diags.push(FutharkDiag {
1235            level: FutharkDiagLevel::Error,
1236            message: msg.to_string(),
1237            location: None,
1238        });
1239    }
1240    pub fn has_errors(&self) -> bool {
1241        self.diags
1242            .iter()
1243            .any(|d| d.level == FutharkDiagLevel::Error)
1244    }
1245    pub fn error_count(&self) -> usize {
1246        self.diags
1247            .iter()
1248            .filter(|d| d.level == FutharkDiagLevel::Error)
1249            .count()
1250    }
1251    pub fn warning_count(&self) -> usize {
1252        self.diags
1253            .iter()
1254            .filter(|d| d.level == FutharkDiagLevel::Warning)
1255            .count()
1256    }
1257}
1258/// Futhark type representation.
1259#[derive(Debug, Clone, PartialEq)]
1260pub enum FutharkType {
1261    /// `i8`
1262    I8,
1263    /// `i16`
1264    I16,
1265    /// `i32`
1266    I32,
1267    /// `i64`
1268    I64,
1269    /// `u8`
1270    U8,
1271    /// `u16`
1272    U16,
1273    /// `u32`
1274    U32,
1275    /// `u64`
1276    U64,
1277    /// `f16`
1278    F16,
1279    /// `f32`
1280    F32,
1281    /// `f64`
1282    F64,
1283    /// `bool`
1284    Bool,
1285    /// Multi-dimensional array: `[n][m]...t`
1286    Array(Box<FutharkType>, Vec<Option<String>>),
1287    /// Tuple: `(t1, t2, ...)`
1288    Tuple(Vec<FutharkType>),
1289    /// Record: `{field: type, ...}`
1290    Record(Vec<(String, FutharkType)>),
1291    /// Opaque type (abstract/named): `#[opaque] type Foo`
1292    Opaque(String),
1293    /// Named (user-defined) type alias
1294    Named(String),
1295    /// Parametric type: `name 't`
1296    Parametric(String, Vec<String>),
1297}
1298/// Futhark replicate expression
1299#[allow(dead_code)]
1300#[derive(Debug, Clone)]
1301pub struct FutharkReplicate {
1302    pub n: String,
1303    pub value: String,
1304}
1305/// Array literal in Futhark
1306#[allow(dead_code)]
1307#[derive(Debug, Clone)]
1308pub struct FutharkArrayLiteral {
1309    pub elements: Vec<String>,
1310    pub element_type: FutharkType,
1311}
1312/// Futhark in-place update
1313#[allow(dead_code)]
1314#[derive(Debug, Clone)]
1315pub struct FutharkInPlaceUpdate {
1316    pub array: String,
1317    pub index: String,
1318    pub value: String,
1319}
1320/// Futhark reduce_by_index (histogram)
1321#[allow(dead_code)]
1322#[derive(Debug, Clone)]
1323pub struct FutharkReduceByIndex {
1324    pub dest: String,
1325    pub op: String,
1326    pub neutral: String,
1327    pub indices: String,
1328    pub values: String,
1329}
1330/// Futhark GPU backend selector
1331#[allow(dead_code)]
1332#[derive(Debug, Clone, PartialEq, Eq)]
1333pub enum FutharkGpuBackend {
1334    OpenCL,
1335    CUDA,
1336    Hip,
1337    Sequential,
1338    Multicore,
1339    IsPC,
1340    WGpu,
1341}
1342/// Futhark reduce expression
1343#[allow(dead_code)]
1344#[derive(Debug, Clone)]
1345pub struct FutharkReduceExpr {
1346    pub op: String,
1347    pub neutral: String,
1348    pub array: String,
1349}
1350#[allow(dead_code)]
1351#[derive(Debug, Clone)]
1352pub struct FutharkUnflatten {
1353    pub n: String,
1354    pub m: String,
1355    pub array: String,
1356}
1357/// Futhark expression representation.
1358#[derive(Debug, Clone)]
1359pub enum FutharkExpr {
1360    /// Integer literal: `42i32`
1361    IntLit(i64, FutharkType),
1362    /// Float literal: `3.14f64`
1363    FloatLit(f64, FutharkType),
1364    /// Bool literal: `true` / `false`
1365    BoolLit(bool),
1366    /// Variable reference: `x`
1367    Var(String),
1368    /// Array literal: `[e1, e2, ...]`
1369    ArrayLit(Vec<FutharkExpr>),
1370    /// Array index: `a[i]`
1371    Index(Box<FutharkExpr>, Box<FutharkExpr>),
1372    /// Array slice: `a[lo:hi]`
1373    Slice(
1374        Box<FutharkExpr>,
1375        Option<Box<FutharkExpr>>,
1376        Option<Box<FutharkExpr>>,
1377    ),
1378    /// `map f a`
1379    Map(Box<FutharkExpr>, Box<FutharkExpr>),
1380    /// `map2 f a b`
1381    Map2(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1382    /// `reduce op ne a`
1383    Reduce(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1384    /// `scan op ne a`
1385    Scan(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1386    /// `filter f a`
1387    Filter(Box<FutharkExpr>, Box<FutharkExpr>),
1388    /// `zip a b`
1389    Zip(Box<FutharkExpr>, Box<FutharkExpr>),
1390    /// `unzip a`
1391    Unzip(Box<FutharkExpr>),
1392    /// `iota n`
1393    Iota(Box<FutharkExpr>),
1394    /// `replicate n x`
1395    Replicate(Box<FutharkExpr>, Box<FutharkExpr>),
1396    /// `if cond then t else e`
1397    IfThenElse(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1398    /// Lambda: `\\ x -> body`
1399    Lambda(Vec<(String, FutharkType)>, Box<FutharkExpr>),
1400    /// Let-in: `let x = e in body`
1401    LetIn(
1402        String,
1403        Option<FutharkType>,
1404        Box<FutharkExpr>,
1405        Box<FutharkExpr>,
1406    ),
1407    /// Loop: `loop (acc = init) for i < n do body`
1408    Loop(
1409        String,
1410        Box<FutharkExpr>,
1411        String,
1412        Box<FutharkExpr>,
1413        Box<FutharkExpr>,
1414    ),
1415    /// Binary operation: `e1 op e2`
1416    BinOp(String, Box<FutharkExpr>, Box<FutharkExpr>),
1417    /// Unary operation: `op e`
1418    UnOp(String, Box<FutharkExpr>),
1419    /// Function application: `f a1 a2 ...`
1420    Apply(Box<FutharkExpr>, Vec<FutharkExpr>),
1421    /// Tuple construction: `(e1, e2, ...)`
1422    TupleLit(Vec<FutharkExpr>),
1423    /// Record construction: `{field = e, ...}`
1424    RecordLit(Vec<(String, FutharkExpr)>),
1425    /// Field access: `r.field`
1426    FieldAccess(Box<FutharkExpr>, String),
1427    /// Type ascription: `e : t`
1428    Ascribe(Box<FutharkExpr>, FutharkType),
1429    /// Scatter: `scatter a is vs`
1430    Scatter(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1431    /// `rotate n a`
1432    Rotate(Box<FutharkExpr>, Box<FutharkExpr>),
1433    /// `transpose a`
1434    Transpose(Box<FutharkExpr>),
1435    /// Flatten: `flatten a`
1436    Flatten(Box<FutharkExpr>),
1437    /// `unflatten n m a`
1438    Unflatten(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1439    /// Size expression: `#(e)`
1440    Size(usize, Box<FutharkExpr>),
1441    /// `with` update: `a with [i] = v`
1442    With(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1443}
1444/// Futhark version info
1445#[allow(dead_code)]
1446#[derive(Debug, Clone)]
1447pub struct FutharkExtVersionInfo {
1448    pub major: u32,
1449    pub minor: u32,
1450    pub patch: u32,
1451    pub git_rev: Option<String>,
1452}
1453/// Futhark filter expression
1454#[allow(dead_code)]
1455#[derive(Debug, Clone)]
1456pub struct FutharkFilterExpr {
1457    pub pred: String,
1458    pub array: String,
1459}
1460/// Futhark module importer
1461#[allow(dead_code)]
1462#[derive(Debug, Default)]
1463pub struct FutharkModuleImporter {
1464    pub imported: Vec<String>,
1465    pub opened: Vec<String>,
1466}
1467#[allow(dead_code)]
1468impl FutharkModuleImporter {
1469    pub fn new() -> Self {
1470        Self::default()
1471    }
1472    pub fn import(&mut self, path: &str) {
1473        self.imported.push(path.to_string());
1474    }
1475    pub fn open(&mut self, path: &str) {
1476        self.opened.push(path.to_string());
1477    }
1478    pub fn emit(&self) -> String {
1479        let mut out = String::new();
1480        for p in &self.imported {
1481            out.push_str(&format!("import \"{}\"\n", p));
1482        }
1483        for p in &self.opened {
1484            out.push_str(&format!("open import \"{}\"\n", p));
1485        }
1486        out
1487    }
1488}
1489/// Futhark scan expression
1490#[allow(dead_code)]
1491#[derive(Debug, Clone)]
1492pub struct FutharkScanExpr {
1493    pub op: String,
1494    pub neutral: String,
1495    pub array: String,
1496}
1497/// Futhark transpose
1498#[allow(dead_code)]
1499#[derive(Debug, Clone)]
1500pub struct FutharkTranspose {
1501    pub array: String,
1502}
1503/// Extended Futhark backend config
1504#[allow(dead_code)]
1505#[derive(Debug, Clone)]
1506pub struct FutharkExtConfig {
1507    pub target_version: FutharkVersion,
1508    pub emit_safety_checks: bool,
1509    pub inline_threshold: usize,
1510    pub vectorize_threshold: usize,
1511    pub emit_comments: bool,
1512    pub mangle_names: bool,
1513    pub backend_target: String,
1514}