Rust · 6250 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("tests/fixtures").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 assert_i128_return_stored_after_call(asm: &str, call_marker: &str, context: &str) {
23 let call_idx = asm
24 .find(call_marker)
25 .unwrap_or_else(|| panic!("missing call marker '{}' in:\n{}", call_marker, asm));
26 assert!(
27 asm[call_idx..].contains("stp x0, x1, [x29, #-"),
28 "{}:\n{}",
29 context,
30 asm
31 );
32 }
33
34 #[test]
35 fn internal_i128_stack_call_spills_fifth_arg_and_loads_incoming_slot_at_o0() {
36 let asm = capture_text(
37 CaptureRequest {
38 input: fixture("integer16_internal_stack_call.f90"),
39 requested: BTreeSet::from([Stage::Asm]),
40 opt_level: OptLevel::O0,
41 },
42 Stage::Asm,
43 );
44
45 assert!(
46 asm.contains("bl _afs_internal_"),
47 "internal integer(16) stack-call should branch to the internalized contained helper:\n{}",
48 asm
49 );
50 assert!(
51 asm.contains("stp x16, x17, [sp, #0]"),
52 "fifth integer(16) arg should spill to the outgoing stack area:\n{}",
53 asm
54 );
55 assert!(
56 asm.contains("ldp x16, x17, [x29, #16]"),
57 "callee should load the incoming stack-passed integer(16) arg from [x29, #16]:\n{}",
58 asm
59 );
60 assert_i128_return_stored_after_call(
61 &asm,
62 "bl _afs_internal_",
63 "caller should still receive the returned integer(16) value in x0/x1 even when args spill to the stack",
64 );
65 }
66
67 #[test]
68 fn internal_i128_stack_call_runs_at_o0() {
69 let result = capture_from_path(&CaptureRequest {
70 input: fixture("integer16_internal_stack_call.f90"),
71 requested: BTreeSet::from([Stage::Run]),
72 opt_level: OptLevel::O0,
73 })
74 .expect("internal integer(16) stack-call program should run");
75
76 let run = result
77 .get(Stage::Run)
78 .and_then(CapturedStage::as_run)
79 .expect("missing run capture");
80
81 assert_eq!(
82 run.exit_code, 0,
83 "expected successful integer(16) stack-call run:\n{:#?}",
84 run
85 );
86 assert!(
87 run.stdout.contains('1'),
88 "internal integer(16) stack-call program should print score 1:\n{}",
89 run.stdout
90 );
91 }
92
93 #[test]
94 fn internal_i128_stack_call_runs_through_optimized_levels() {
95 for level in [
96 OptLevel::O1,
97 OptLevel::O2,
98 OptLevel::O3,
99 OptLevel::Os,
100 OptLevel::Ofast,
101 ] {
102 let result = capture_from_path(&CaptureRequest {
103 input: fixture("integer16_internal_stack_call.f90"),
104 requested: BTreeSet::from([Stage::Run]),
105 opt_level: level,
106 })
107 .unwrap_or_else(|e| {
108 panic!(
109 "optimized integer(16) stack-call should run at {:?}:\n{}",
110 level, e
111 )
112 });
113
114 let run = result
115 .get(Stage::Run)
116 .and_then(CapturedStage::as_run)
117 .expect("missing run capture");
118
119 assert_eq!(
120 run.exit_code, 0,
121 "expected successful integer(16) stack-call run at {:?}:\n{:#?}",
122 level, run
123 );
124 assert!(
125 run.stdout.contains('1'),
126 "integer(16) stack-call program should print score 1 at {:?}:\n{}",
127 level,
128 run.stdout
129 );
130 }
131 }
132
133 #[test]
134 fn internal_i128_stack_call_object_snapshot_is_deterministic_at_o0() {
135 let source = fixture("integer16_internal_stack_call.f90");
136 let first = capture_text(
137 CaptureRequest {
138 input: source.clone(),
139 requested: BTreeSet::from([Stage::Obj]),
140 opt_level: OptLevel::O0,
141 },
142 Stage::Obj,
143 );
144 let second = capture_text(
145 CaptureRequest {
146 input: source,
147 requested: BTreeSet::from([Stage::Obj]),
148 opt_level: OptLevel::O0,
149 },
150 Stage::Obj,
151 );
152
153 assert_eq!(
154 first, second,
155 "internal integer(16) stack-call object snapshots should be deterministic at O0"
156 );
157 }
158
159 #[test]
160 fn external_i128_stack_call_spills_fifth_arg_and_tracks_symbol_at_o0() {
161 let asm = capture_text(
162 CaptureRequest {
163 input: fixture("integer16_external_stack_call.f90"),
164 requested: BTreeSet::from([Stage::Asm]),
165 opt_level: OptLevel::O0,
166 },
167 Stage::Asm,
168 );
169
170 assert!(
171 asm.contains("bl _add5_ext"),
172 "external integer(16) stack-call should branch to the declared symbol:\n{}",
173 asm
174 );
175 assert!(
176 asm.contains("stp x16, x17, [sp, #0]"),
177 "fifth external integer(16) arg should spill to the outgoing stack area:\n{}",
178 asm
179 );
180 assert_i128_return_stored_after_call(
181 &asm,
182 "bl _add5_ext",
183 "external integer(16) stack-call should still receive the returned value in x0/x1",
184 );
185 }
186
187 #[test]
188 fn external_i128_stack_call_object_snapshot_is_deterministic_at_o0() {
189 let source = fixture("integer16_external_stack_call.f90");
190 let first = capture_text(
191 CaptureRequest {
192 input: source.clone(),
193 requested: BTreeSet::from([Stage::Obj]),
194 opt_level: OptLevel::O0,
195 },
196 Stage::Obj,
197 );
198 let second = capture_text(
199 CaptureRequest {
200 input: source,
201 requested: BTreeSet::from([Stage::Obj]),
202 opt_level: OptLevel::O0,
203 },
204 Stage::Obj,
205 );
206
207 assert_eq!(
208 first, second,
209 "external integer(16) stack-call object snapshots should be deterministic at O0"
210 );
211 }
212