Rust · 11145 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 #[test]
23 fn simple_local_i128_roundtrip_emits_wide_pair_moves_at_o0() {
24 let asm = capture_text(
25 CaptureRequest {
26 input: fixture("integer16_local_roundtrip.f90"),
27 requested: BTreeSet::from([Stage::Asm]),
28 opt_level: OptLevel::O0,
29 },
30 Stage::Asm,
31 );
32
33 assert!(
34 asm.contains("movz x16, #42"),
35 "backend should materialize the low 64-bit half of the i128 constant:\n{}",
36 asm
37 );
38 assert!(
39 asm.contains("mov x17, xzr"),
40 "backend should zero the high 64-bit half of the i128 constant:\n{}",
41 asm
42 );
43 assert!(
44 asm.contains("stp x16, x17"),
45 "backend should store i128 values as paired 64-bit writes:\n{}",
46 asm
47 );
48 assert!(
49 asm.contains("ldp x16, x17"),
50 "backend should reload i128 values as paired 64-bit reads:\n{}",
51 asm
52 );
53 }
54
55 #[test]
56 fn simple_local_i128_add_emits_carry_chain_ops_at_o0() {
57 let asm = capture_text(
58 CaptureRequest {
59 input: fixture("integer16_add.f90"),
60 requested: BTreeSet::from([Stage::Asm]),
61 opt_level: OptLevel::O0,
62 },
63 Stage::Asm,
64 );
65
66 assert!(
67 asm.contains("adds x16, x16, x8"),
68 "backend should use ADDS for the low i128 word:\n{}",
69 asm
70 );
71 assert!(
72 asm.contains("adc x17, x17, x8"),
73 "backend should use ADC for the high i128 word:\n{}",
74 asm
75 );
76 }
77
78 #[test]
79 fn simple_local_i128_subneg_emits_borrow_chain_ops_at_o0() {
80 let asm = capture_text(
81 CaptureRequest {
82 input: fixture("integer16_subneg.f90"),
83 requested: BTreeSet::from([Stage::Asm]),
84 opt_level: OptLevel::O0,
85 },
86 Stage::Asm,
87 );
88
89 assert!(
90 asm.contains("subs x16, x16, x8"),
91 "backend should use SUBS for the low i128 subtraction word:\n{}",
92 asm
93 );
94 assert!(
95 asm.contains("sbc x17, x17, x8"),
96 "backend should use SBC for the high i128 subtraction word:\n{}",
97 asm
98 );
99 assert!(
100 asm.contains("subs x16, xzr, x16"),
101 "backend should use SUBS from xzr for i128 negation:\n{}",
102 asm
103 );
104 }
105
106 #[test]
107 fn simple_local_i128_eqne_emits_pair_compare_ops_at_o0() {
108 let asm = capture_text(
109 CaptureRequest {
110 input: fixture("integer16_eqne.f90"),
111 requested: BTreeSet::from([Stage::Asm]),
112 opt_level: OptLevel::O0,
113 },
114 Stage::Asm,
115 );
116
117 assert!(
118 asm.contains("cmp x16, x8"),
119 "backend should compare the low i128 limb:\n{}",
120 asm
121 );
122 assert!(
123 asm.contains("cmp x17, x9"),
124 "backend should compare the high i128 limb:\n{}",
125 asm
126 );
127 assert!(
128 asm.contains("cset w10, eq"),
129 "backend should materialize low/high equality flags:\n{}",
130 asm
131 );
132 assert!(
133 asm.contains("and w10, w10, w11"),
134 "backend should combine equality limbs with AND:\n{}",
135 asm
136 );
137 assert!(
138 asm.contains("cset w10, ne"),
139 "backend should materialize low/high inequality flags:\n{}",
140 asm
141 );
142 assert!(
143 asm.contains("orr w10, w10, w11"),
144 "backend should combine inequality limbs with OR:\n{}",
145 asm
146 );
147 }
148
149 #[test]
150 fn simple_local_i128_ordered_compares_emit_signed_and_unsigned_limb_checks_at_o0() {
151 let asm = capture_text(
152 CaptureRequest {
153 input: fixture("integer16_ordered_branch.f90"),
154 requested: BTreeSet::from([Stage::Asm]),
155 opt_level: OptLevel::O0,
156 },
157 Stage::Asm,
158 );
159
160 assert!(
161 asm.contains("cset w10, lt"),
162 "backend should use signed high-limb compare for i128 lt/le:\n{}",
163 asm
164 );
165 assert!(
166 asm.contains("cset w8, lo"),
167 "backend should use unsigned low-limb compare for strict ordered i128 compares:\n{}",
168 asm
169 );
170 assert!(
171 asm.contains("cset w8, ls"),
172 "backend should use unsigned low-limb <= compare for i128 le:\n{}",
173 asm
174 );
175 assert!(
176 asm.contains("cset w10, gt"),
177 "backend should use signed high-limb compare for i128 gt/ge:\n{}",
178 asm
179 );
180 assert!(
181 asm.contains("cset w8, hi"),
182 "backend should use unsigned low-limb > compare for i128 gt:\n{}",
183 asm
184 );
185 assert!(
186 asm.contains("cset w8, hs"),
187 "backend should use unsigned low-limb >= compare for i128 ge:\n{}",
188 asm
189 );
190 }
191
192 #[test]
193 fn simple_local_i128_ordered_branch_runs_at_o0() {
194 let result = capture_from_path(&CaptureRequest {
195 input: fixture("integer16_ordered_branch.f90"),
196 requested: BTreeSet::from([Stage::Run]),
197 opt_level: OptLevel::O0,
198 })
199 .expect("ordered i128 branch program should run");
200
201 let run = result
202 .get(Stage::Run)
203 .and_then(CapturedStage::as_run)
204 .expect("missing run capture");
205
206 assert_eq!(
207 run.exit_code, 0,
208 "expected successful ordered branch run:\n{:#?}",
209 run
210 );
211 assert!(
212 run.stdout.contains('4'),
213 "ordered i128 branch program should print score 4:\n{}",
214 run.stdout
215 );
216 }
217
218 #[test]
219 fn simple_local_i128_select_lowers_to_ir_select_and_pair_csel_at_o0() {
220 let ir = capture_text(
221 CaptureRequest {
222 input: fixture("integer16_select.f90"),
223 requested: BTreeSet::from([Stage::Ir]),
224 opt_level: OptLevel::O0,
225 },
226 Stage::Ir,
227 );
228 assert!(
229 ir.contains("select"),
230 "simple integer(16) diamond should lower to IR select:\n{}",
231 ir
232 );
233
234 let asm = capture_text(
235 CaptureRequest {
236 input: fixture("integer16_select.f90"),
237 requested: BTreeSet::from([Stage::Asm]),
238 opt_level: OptLevel::O0,
239 },
240 Stage::Asm,
241 );
242 assert_eq!(
243 asm.matches("csel x").count(),
244 2,
245 "wide integer(16) select should lower to one CSEL per limb:\n{}",
246 asm
247 );
248 }
249
250 #[test]
251 fn simple_local_i128_select_runs_at_o0() {
252 let result = capture_from_path(&CaptureRequest {
253 input: fixture("integer16_select.f90"),
254 requested: BTreeSet::from([Stage::Run]),
255 opt_level: OptLevel::O0,
256 })
257 .expect("integer(16) select program should run");
258
259 let run = result
260 .get(Stage::Run)
261 .and_then(CapturedStage::as_run)
262 .expect("missing run capture");
263
264 assert_eq!(
265 run.exit_code, 0,
266 "expected successful integer(16) select run:\n{:#?}",
267 run
268 );
269 assert!(
270 run.stdout.contains('1'),
271 "integer(16) select program should print score 1:\n{}",
272 run.stdout
273 );
274 }
275
276 #[test]
277 fn simple_local_i128_select_object_snapshot_is_deterministic_at_o0() {
278 let source = fixture("integer16_select.f90");
279 let first = capture_text(
280 CaptureRequest {
281 input: source.clone(),
282 requested: BTreeSet::from([Stage::Obj]),
283 opt_level: OptLevel::O0,
284 },
285 Stage::Obj,
286 );
287 let second = capture_text(
288 CaptureRequest {
289 input: source,
290 requested: BTreeSet::from([Stage::Obj]),
291 opt_level: OptLevel::O0,
292 },
293 Stage::Obj,
294 );
295
296 assert_eq!(
297 first, second,
298 "integer(16) select object snapshots should be deterministic at O0"
299 );
300 }
301
302 #[test]
303 fn simple_internal_i128_call_uses_pair_arg_and_return_regs_at_o0() {
304 let asm = capture_text(
305 CaptureRequest {
306 input: fixture("integer16_internal_call.f90"),
307 requested: BTreeSet::from([Stage::Asm]),
308 opt_level: OptLevel::O0,
309 },
310 Stage::Asm,
311 );
312
313 assert!(
314 asm.contains("bl _add_one"),
315 "internal integer(16) call should branch to the contained helper:\n{}",
316 asm
317 );
318 assert!(
319 asm.matches("stp x0, x1").count() >= 2,
320 "internal integer(16) ABI should spill pair-register args/results with STP x0, x1:\n{}",
321 asm
322 );
323 assert!(
324 asm.matches("ldp x0, x1").count() >= 2,
325 "internal integer(16) ABI should load pair-register args/results with LDP x0, x1:\n{}",
326 asm
327 );
328 }
329
330 #[test]
331 fn simple_internal_i128_call_runs_at_o0() {
332 let result = capture_from_path(&CaptureRequest {
333 input: fixture("integer16_internal_call.f90"),
334 requested: BTreeSet::from([Stage::Run]),
335 opt_level: OptLevel::O0,
336 })
337 .expect("internal integer(16) call program should run");
338
339 let run = result
340 .get(Stage::Run)
341 .and_then(CapturedStage::as_run)
342 .expect("missing run capture");
343
344 assert_eq!(
345 run.exit_code, 0,
346 "expected successful internal integer(16) call run:\n{:#?}",
347 run
348 );
349 assert!(
350 run.stdout.contains('1'),
351 "internal integer(16) call program should print score 1:\n{}",
352 run.stdout
353 );
354 }
355
356 #[test]
357 fn simple_internal_i128_call_object_snapshot_is_deterministic_at_o0() {
358 let source = fixture("integer16_internal_call.f90");
359 let first = capture_text(
360 CaptureRequest {
361 input: source.clone(),
362 requested: BTreeSet::from([Stage::Obj]),
363 opt_level: OptLevel::O0,
364 },
365 Stage::Obj,
366 );
367 let second = capture_text(
368 CaptureRequest {
369 input: source,
370 requested: BTreeSet::from([Stage::Obj]),
371 opt_level: OptLevel::O0,
372 },
373 Stage::Obj,
374 );
375
376 assert_eq!(
377 first, second,
378 "internal integer(16) call object snapshots should be deterministic at O0"
379 );
380 }
381
382 #[test]
383 fn simple_local_i128_object_snapshot_is_deterministic_at_o0() {
384 let source = fixture("integer16_local_roundtrip.f90");
385 let first = capture_text(
386 CaptureRequest {
387 input: source.clone(),
388 requested: BTreeSet::from([Stage::Obj]),
389 opt_level: OptLevel::O0,
390 },
391 Stage::Obj,
392 );
393 let second = capture_text(
394 CaptureRequest {
395 input: source,
396 requested: BTreeSet::from([Stage::Obj]),
397 opt_level: OptLevel::O0,
398 },
399 Stage::Obj,
400 );
401
402 assert_eq!(
403 first, second,
404 "simple local i128 object snapshots should be deterministic at O0"
405 );
406 }
407