Rust · 3423 bytes Raw Blame History
1 use std::collections::BTreeSet;
2 use std::path::PathBuf;
3
4 use armfortas::driver::OptLevel;
5 use armfortas::testing::{capture_from_path, CaptureRequest, CapturedStage, Stage};
6
7 fn fixture(name: &str) -> PathBuf {
8 let path = PathBuf::from("test_programs").join(name);
9 assert!(path.exists(), "missing test fixture {}", path.display());
10 path
11 }
12
13 fn capture_text(request: CaptureRequest, stage: Stage) -> String {
14 let result = capture_from_path(&request).expect("capture should succeed");
15 match result.get(stage) {
16 Some(CapturedStage::Text(text)) => text.clone(),
17 Some(CapturedStage::Run(_)) => panic!("expected text stage for {}", stage.as_str()),
18 None => panic!("missing requested stage {}", stage.as_str()),
19 }
20 }
21
22 fn function_section<'a>(ir: &'a str, name: &str) -> &'a str {
23 let header = format!(" func @{}", name);
24 let start = ir
25 .find(&header)
26 .unwrap_or_else(|| panic!("missing function section for {}", name));
27 let rest = &ir[start..];
28 let end = rest
29 .find("\n }\n")
30 .unwrap_or_else(|| panic!("unterminated function section for {}", name));
31 &rest[..end + "\n }".len()]
32 }
33
34 fn function_sections(ir: &str) -> Vec<&str> {
35 ir.match_indices(" func @")
36 .map(|(idx, _)| {
37 let rest = &ir[idx..];
38 let end = rest
39 .find("\n }\n")
40 .unwrap_or_else(|| panic!("unterminated function section in:\n{}", rest));
41 &rest[..end + "\n }".len()]
42 })
43 .collect()
44 }
45
46 fn function_name<'a>(func_section: &'a str) -> &'a str {
47 let header = func_section.lines().next().expect("function header").trim();
48 let rest = header
49 .strip_prefix("func @")
50 .expect("function header prefix");
51 let end = rest
52 .find(|ch: char| ch == ' ' || ch == '(')
53 .unwrap_or(rest.len());
54 &rest[..end]
55 }
56
57 fn count(haystack: &str, needle: &str) -> usize {
58 haystack.matches(needle).count()
59 }
60
61 #[test]
62 fn o2_reuses_pure_recursive_call_in_program_caller() {
63 let source = fixture("pure_recursive_reuse.f90");
64
65 let raw_ir = capture_text(
66 CaptureRequest {
67 input: source.clone(),
68 requested: BTreeSet::from([Stage::Ir]),
69 opt_level: OptLevel::O0,
70 },
71 Stage::Ir,
72 );
73 let opt_ir = capture_text(
74 CaptureRequest {
75 input: source,
76 requested: BTreeSet::from([Stage::OptIr]),
77 opt_level: OptLevel::O2,
78 },
79 Stage::OptIr,
80 );
81 let raw_sections = function_sections(&raw_ir);
82 assert_eq!(
83 raw_sections.len(),
84 2,
85 "raw IR should include the program body plus one contained pure helper:\n{}",
86 raw_ir
87 );
88 let helper_name = function_name(raw_sections[1]);
89
90 let raw_main = function_section(&raw_ir, "__prog_pure_recursive_reuse");
91 let opt_main = function_section(&opt_ir, "__prog_pure_recursive_reuse");
92
93 let raw_pure_calls = count(raw_main, &format!("call @{}(", helper_name));
94 let opt_pure_calls = count(opt_main, &format!("call @{}(", helper_name));
95
96 assert_eq!(
97 raw_pure_calls, 2,
98 "lowered caller should materialize two recursive PURE calls before optimization:\n{}",
99 raw_main
100 );
101 assert_eq!(
102 opt_pure_calls, 1,
103 "O2 optimized caller should reuse the repeated PURE call result:\n{}",
104 opt_main
105 );
106 }
107