Rust · 4784 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, RunCapture, Stage};
6
7 fn fixture() -> PathBuf {
8 let path = PathBuf::from("test_programs/module_global_host_assoc.f90");
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 capture_run(request: CaptureRequest) -> RunCapture {
23 let result = capture_from_path(&request).expect("capture should succeed");
24 match result.get(Stage::Run) {
25 Some(CapturedStage::Run(run)) => run.clone(),
26 Some(CapturedStage::Text(_)) => panic!("expected run stage"),
27 None => panic!("missing requested stage {}", Stage::Run.as_str()),
28 }
29 }
30
31 fn function_section<'a>(ir: &'a str, name: &str) -> &'a str {
32 let header = format!(" func @{}", name);
33 let start = ir
34 .find(&header)
35 .unwrap_or_else(|| panic!("missing function section for {}", name));
36 let rest = &ir[start..];
37 let end = rest
38 .find("\n }\n")
39 .unwrap_or_else(|| panic!("unterminated function section for {}", name));
40 &rest[..end + "\n }".len()]
41 }
42
43 fn function_sections(ir: &str) -> Vec<&str> {
44 ir.match_indices(" func @")
45 .map(|(idx, _)| {
46 let rest = &ir[idx..];
47 let end = rest
48 .find("\n }\n")
49 .unwrap_or_else(|| panic!("unterminated function section in:\n{}", rest));
50 &rest[..end + "\n }".len()]
51 })
52 .collect()
53 }
54
55 fn function_name<'a>(func_section: &'a str) -> &'a str {
56 let header = func_section.lines().next().expect("function header").trim();
57 let rest = header
58 .strip_prefix("func @")
59 .expect("function header prefix");
60 let end = rest
61 .find(|ch: char| ch == ' ' || ch == '(')
62 .unwrap_or(rest.len());
63 &rest[..end]
64 }
65
66 fn non_program_function_names(ir: &str) -> Vec<&str> {
67 function_sections(ir)
68 .into_iter()
69 .map(function_name)
70 .filter(|name| !name.starts_with("__prog_"))
71 .collect()
72 }
73
74 #[test]
75 fn raw_ir_module_procedure_sees_shared_module_global() {
76 let raw_ir = capture_text(
77 CaptureRequest {
78 input: fixture(),
79 requested: BTreeSet::from([Stage::Ir]),
80 opt_level: OptLevel::O0,
81 },
82 Stage::Ir,
83 );
84
85 let helper_names = non_program_function_names(&raw_ir);
86 assert_eq!(
87 helper_names.len(),
88 1,
89 "raw IR should include exactly one module procedure helper:\n{}",
90 raw_ir
91 );
92 let bump = function_section(&raw_ir, helper_names[0]);
93
94 assert!(
95 bump.contains("global_addr @afs_mod_module_global_host_assoc_mod_g"),
96 "module procedure should resolve g through the shared module global:\n{}",
97 bump
98 );
99 assert!(
100 bump.contains("store"),
101 "module procedure should actually write the shared module global:\n{}",
102 bump
103 );
104 }
105
106 #[test]
107 fn module_global_host_assoc_runs_across_opt_levels_and_keeps_o2_object_deterministic() {
108 let source = fixture();
109
110 for level in [
111 OptLevel::O0,
112 OptLevel::O1,
113 OptLevel::O2,
114 OptLevel::O3,
115 OptLevel::Os,
116 OptLevel::Ofast,
117 ] {
118 let run = capture_run(CaptureRequest {
119 input: source.clone(),
120 requested: BTreeSet::from([Stage::Run]),
121 opt_level: level,
122 });
123 assert_eq!(
124 run.exit_code, 0,
125 "module-global host association should run successfully at {:?}:\n{:#?}",
126 level, run
127 );
128 assert!(
129 run.stdout.contains("99"),
130 "module-global host association should preserve the shared module write at {:?}:\n{:#?}",
131 level, run
132 );
133 }
134
135 let obj_a = capture_text(
136 CaptureRequest {
137 input: source.clone(),
138 requested: BTreeSet::from([Stage::Obj]),
139 opt_level: OptLevel::O2,
140 },
141 Stage::Obj,
142 );
143 let obj_b = capture_text(
144 CaptureRequest {
145 input: source,
146 requested: BTreeSet::from([Stage::Obj]),
147 opt_level: OptLevel::O2,
148 },
149 Stage::Obj,
150 );
151
152 assert_eq!(
153 obj_a, obj_b,
154 "module-global host association should have a deterministic O2 object snapshot"
155 );
156 }
157