1use std::collections::{HashMap, HashSet};
6
7#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct FutharkReverse {
11 pub array: String,
12}
13#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct FutharkConcat {
17 pub arrays: Vec<String>,
18 pub dim: Option<usize>,
19}
20#[allow(dead_code)]
22#[derive(Debug, Default, Clone)]
23pub struct FutharkExtPassTiming {
24 pub pass_name: String,
25 pub duration_us: u64,
26}
27#[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#[allow(dead_code)]
37#[derive(Debug, Clone)]
38pub struct FutharkZip {
39 pub arrays: Vec<String>,
40}
41#[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#[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#[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#[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#[derive(Debug, Clone)]
141pub struct FutharkTypeAlias {
142 pub name: String,
144 pub params: Vec<String>,
146 pub ty: FutharkType,
148 pub is_opaque: bool,
150}
151#[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#[derive(Debug, Clone)]
163pub enum FutharkStmt {
164 LetBinding(String, Option<FutharkType>, FutharkExpr),
166 LetTupleBinding(Vec<String>, FutharkExpr),
168 LoopBinding(String, FutharkExpr, String, FutharkExpr, Vec<FutharkStmt>),
170 ReturnExpr(FutharkExpr),
172 Comment(String),
174}
175#[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#[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#[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#[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#[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#[derive(Debug, Clone)]
270pub struct FutharkModule {
271 pub opens: Vec<String>,
273 pub types: Vec<FutharkTypeAlias>,
275 pub funs: Vec<FutharkFun>,
277 pub constants: Vec<(String, FutharkType, FutharkExpr)>,
279 pub doc: Option<String>,
281}
282impl FutharkModule {
283 pub fn new() -> Self {
285 FutharkModule {
286 opens: vec![],
287 types: vec![],
288 funs: vec![],
289 constants: vec![],
290 doc: None,
291 }
292 }
293 pub fn add_open(&mut self, name: impl Into<String>) {
295 self.opens.push(name.into());
296 }
297 pub fn add_type(&mut self, alias: FutharkTypeAlias) {
299 self.types.push(alias);
300 }
301 pub fn add_fun(&mut self, fun: FutharkFun) {
303 self.funs.push(fun);
304 }
305 pub fn add_constant(&mut self, name: impl Into<String>, ty: FutharkType, expr: FutharkExpr) {
307 self.constants.push((name.into(), ty, expr));
308 }
309 pub fn set_doc(&mut self, doc: impl Into<String>) {
311 self.doc = Some(doc.into());
312 }
313}
314#[allow(dead_code)]
316#[derive(Debug, Clone, PartialEq)]
317pub enum FutharkDiagLevel {
318 Info,
319 Warning,
320 Error,
321}
322#[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#[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#[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#[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#[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#[allow(dead_code)]
462#[derive(Debug, Clone)]
463pub struct FutharkIndexExpr {
464 pub array: String,
465 pub index: String,
466}
467#[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#[allow(dead_code)]
477#[derive(Debug, Clone)]
478pub struct FutharkMapExpr {
479 pub func: String,
480 pub arrays: Vec<String>,
481}
482#[allow(dead_code)]
484#[derive(Debug, Clone)]
485pub struct FutharkCopy {
486 pub array: String,
487}
488#[derive(Debug, Clone)]
490pub struct FutharkConfig {
491 pub indent_width: usize,
493 pub annotate_lets: bool,
495 pub default_int: FutharkType,
497 pub default_float: FutharkType,
499}
500#[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#[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#[allow(dead_code)]
522#[derive(Debug, Clone)]
523pub struct FutharkScatter {
524 pub dest: String,
525 pub indices: String,
526 pub values: String,
527}
528#[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#[allow(dead_code)]
545#[derive(Debug, Clone)]
546pub struct FutharkSizeExpr {
547 pub dim: usize,
548 pub array: String,
549}
550#[allow(dead_code)]
552#[derive(Debug, Clone)]
553pub struct FutharkPartitionExpr {
554 pub k: usize,
555 pub pred: String,
556 pub array: String,
557}
558#[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#[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#[derive(Debug, Clone, PartialEq)]
593pub enum FutharkAttr {
594 Inline,
596 NoInline,
598 NoMap,
600 Sequential,
602 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#[allow(dead_code)]
613#[derive(Debug, Clone)]
614pub struct FutharkRotate {
615 pub dim: usize,
616 pub amount: String,
617 pub array: String,
618}
619#[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#[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#[allow(dead_code)]
640#[derive(Debug, Clone)]
641pub struct FutharkMatchArm {
642 pub pattern: String,
643 pub body: String,
644}
645#[derive(Debug, Clone)]
647pub struct FutharkFun {
648 pub name: String,
650 pub type_params: Vec<String>,
652 pub params: Vec<(String, FutharkType)>,
654 pub return_type: FutharkType,
656 pub body: Vec<FutharkStmt>,
658 pub is_entry: bool,
660 pub attrs: Vec<FutharkAttr>,
662}
663impl FutharkFun {
664 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 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 pub fn with_type_param(mut self, tp: impl Into<String>) -> Self {
700 self.type_params.push(tp.into());
701 self
702 }
703 pub fn with_attr(mut self, attr: FutharkAttr) -> Self {
705 self.attrs.push(attr);
706 self
707 }
708}
709pub struct FutharkBackend {
711 pub(super) buf: String,
713 pub(super) indent: usize,
715 pub(super) indent_str: String,
717 pub(super) config: FutharkConfig,
719}
720impl FutharkBackend {
721 pub fn new() -> Self {
723 FutharkBackend::with_config(FutharkConfig::default())
724 }
725 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 pub fn emit_type(&mut self, ty: &FutharkType) {
764 let s = ty.to_string();
765 self.push(&s);
766 }
767 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 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 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 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 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 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 pub fn finish(&mut self) -> String {
1194 std::mem::take(&mut self.buf)
1195 }
1196 pub fn generate(module: &FutharkModule) -> String {
1198 let mut backend = FutharkBackend::new();
1199 backend.emit_module(module);
1200 backend.finish()
1201 }
1202 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#[derive(Debug, Clone, PartialEq)]
1260pub enum FutharkType {
1261 I8,
1263 I16,
1265 I32,
1267 I64,
1269 U8,
1271 U16,
1273 U32,
1275 U64,
1277 F16,
1279 F32,
1281 F64,
1283 Bool,
1285 Array(Box<FutharkType>, Vec<Option<String>>),
1287 Tuple(Vec<FutharkType>),
1289 Record(Vec<(String, FutharkType)>),
1291 Opaque(String),
1293 Named(String),
1295 Parametric(String, Vec<String>),
1297}
1298#[allow(dead_code)]
1300#[derive(Debug, Clone)]
1301pub struct FutharkReplicate {
1302 pub n: String,
1303 pub value: String,
1304}
1305#[allow(dead_code)]
1307#[derive(Debug, Clone)]
1308pub struct FutharkArrayLiteral {
1309 pub elements: Vec<String>,
1310 pub element_type: FutharkType,
1311}
1312#[allow(dead_code)]
1314#[derive(Debug, Clone)]
1315pub struct FutharkInPlaceUpdate {
1316 pub array: String,
1317 pub index: String,
1318 pub value: String,
1319}
1320#[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#[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#[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#[derive(Debug, Clone)]
1359pub enum FutharkExpr {
1360 IntLit(i64, FutharkType),
1362 FloatLit(f64, FutharkType),
1364 BoolLit(bool),
1366 Var(String),
1368 ArrayLit(Vec<FutharkExpr>),
1370 Index(Box<FutharkExpr>, Box<FutharkExpr>),
1372 Slice(
1374 Box<FutharkExpr>,
1375 Option<Box<FutharkExpr>>,
1376 Option<Box<FutharkExpr>>,
1377 ),
1378 Map(Box<FutharkExpr>, Box<FutharkExpr>),
1380 Map2(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1382 Reduce(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1384 Scan(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1386 Filter(Box<FutharkExpr>, Box<FutharkExpr>),
1388 Zip(Box<FutharkExpr>, Box<FutharkExpr>),
1390 Unzip(Box<FutharkExpr>),
1392 Iota(Box<FutharkExpr>),
1394 Replicate(Box<FutharkExpr>, Box<FutharkExpr>),
1396 IfThenElse(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1398 Lambda(Vec<(String, FutharkType)>, Box<FutharkExpr>),
1400 LetIn(
1402 String,
1403 Option<FutharkType>,
1404 Box<FutharkExpr>,
1405 Box<FutharkExpr>,
1406 ),
1407 Loop(
1409 String,
1410 Box<FutharkExpr>,
1411 String,
1412 Box<FutharkExpr>,
1413 Box<FutharkExpr>,
1414 ),
1415 BinOp(String, Box<FutharkExpr>, Box<FutharkExpr>),
1417 UnOp(String, Box<FutharkExpr>),
1419 Apply(Box<FutharkExpr>, Vec<FutharkExpr>),
1421 TupleLit(Vec<FutharkExpr>),
1423 RecordLit(Vec<(String, FutharkExpr)>),
1425 FieldAccess(Box<FutharkExpr>, String),
1427 Ascribe(Box<FutharkExpr>, FutharkType),
1429 Scatter(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1431 Rotate(Box<FutharkExpr>, Box<FutharkExpr>),
1433 Transpose(Box<FutharkExpr>),
1435 Flatten(Box<FutharkExpr>),
1437 Unflatten(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1439 Size(usize, Box<FutharkExpr>),
1441 With(Box<FutharkExpr>, Box<FutharkExpr>, Box<FutharkExpr>),
1443}
1444#[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#[allow(dead_code)]
1455#[derive(Debug, Clone)]
1456pub struct FutharkFilterExpr {
1457 pub pred: String,
1458 pub array: String,
1459}
1460#[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#[allow(dead_code)]
1491#[derive(Debug, Clone)]
1492pub struct FutharkScanExpr {
1493 pub op: String,
1494 pub neutral: String,
1495 pub array: String,
1496}
1497#[allow(dead_code)]
1499#[derive(Debug, Clone)]
1500pub struct FutharkTranspose {
1501 pub array: String,
1502}
1503#[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}