fortrangoingonforty/armfortas / 209204c

Browse files

Allow globals-only i128 backend

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
209204cc64e2e5d9e0ca4e2da8b3a133091a4bad
Parents
52cb9a8
Tree
47eee7d

5 changed files

StatusFile+-
M src/driver/mod.rs 2 2
M src/ir/inst.rs 44 0
M src/testing.rs 9 4
A tests/fixtures/integer16_globals_backend.f90 8 0
A tests/i128_backend_data.rs 95 0
src/driver/mod.rsmodified
@@ -230,7 +230,7 @@ pub fn compile(opts: &Options) -> Result<(), String> {
230
         return Err(format!("internal error: IR verification failed:\n{}", msg));
230
         return Err(format!("internal error: IR verification failed:\n{}", msg));
231
     }
231
     }
232
     let module_has_i128 = ir_module.contains_i128();
232
     let module_has_i128 = ir_module.contains_i128();
233
-    if module_has_i128 && opts.opt_level != OptLevel::O0 {
233
+    if ir_module.contains_i128_outside_globals() && opts.opt_level != OptLevel::O0 {
234
         return Err(
234
         return Err(
235
             "integer(16) / i128 optimization is not yet supported; use -O0 --emit-ir for now"
235
             "integer(16) / i128 optimization is not yet supported; use -O0 --emit-ir for now"
236
                 .into(),
236
                 .into(),
@@ -265,7 +265,7 @@ pub fn compile(opts: &Options) -> Result<(), String> {
265
         return Ok(());
265
         return Ok(());
266
     }
266
     }
267
 
267
 
268
-    if module_has_i128 {
268
+    if module_has_i128 && !ir_module.i128_backend_data_only_supported() {
269
         return Err(
269
         return Err(
270
             "backend does not yet support integer(16) / i128 codegen; use --emit-ir for now"
270
             "backend does not yet support integer(16) / i128 codegen; use --emit-ir for now"
271
                 .into(),
271
                 .into(),
src/ir/inst.rsmodified
@@ -410,6 +410,34 @@ impl Module {
410
                     })
410
                     })
411
             })
411
             })
412
     }
412
     }
413
+
414
+    /// True when `i128` appears anywhere outside module-global storage.
415
+    ///
416
+    /// Globals-only `i128` is the first staged backend surface we support:
417
+    /// the optimizer may ignore it and the emitter can lay it out as raw data.
418
+    /// Parameters, returns, instruction results, block params, and extern
419
+    /// signatures still imply unsupported ABI or codegen work.
420
+    pub fn contains_i128_outside_globals(&self) -> bool {
421
+        self.extern_funcs.iter().any(|func| sig_contains_i128(self, &func.sig))
422
+            || self.functions.iter().any(|func| {
423
+                type_contains_i128(self, &func.return_type)
424
+                    || func.params.iter().any(|param| type_contains_i128(self, &param.ty))
425
+                    || func.blocks.iter().any(|block| {
426
+                        block.params.iter().any(|param| type_contains_i128(self, &param.ty))
427
+                            || block.insts.iter().any(|inst| type_contains_i128(self, &inst.ty))
428
+                    })
429
+            })
430
+    }
431
+
432
+    /// True when every `i128` use in the module is a global data shape that
433
+    /// the current backend emitter can lay out directly.
434
+    pub fn i128_backend_data_only_supported(&self) -> bool {
435
+        !self.contains_i128_outside_globals()
436
+            && self
437
+                .globals
438
+                .iter()
439
+                .all(|global| global_i128_backend_data_supported(self, global))
440
+    }
413
 }
441
 }
414
 
442
 
415
 fn sig_contains_i128(module: &Module, sig: &super::types::FuncSig) -> bool {
443
 fn sig_contains_i128(module: &Module, sig: &super::types::FuncSig) -> bool {
@@ -429,3 +457,19 @@ fn type_contains_i128(module: &Module, ty: &IrType) -> bool {
429
         _ => false,
457
         _ => false,
430
     }
458
     }
431
 }
459
 }
460
+
461
+fn global_i128_backend_data_supported(module: &Module, global: &Global) -> bool {
462
+    match &global.ty {
463
+        IrType::Int(IntWidth::I128) => matches!(
464
+            global.initializer,
465
+            Some(GlobalInit::Int(_)) | Some(GlobalInit::Zero) | None
466
+        ),
467
+        IrType::Array(elem_ty, _) if matches!(elem_ty.as_ref(), IrType::Int(IntWidth::I128)) => {
468
+            matches!(
469
+                global.initializer,
470
+                Some(GlobalInit::IntArray(_)) | Some(GlobalInit::Zero) | None
471
+            )
472
+        }
473
+        _ => !type_contains_i128(module, &global.ty),
474
+    }
475
+}
src/testing.rsmodified
@@ -366,7 +366,7 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt
366
         })
366
         })
367
         && request.opt_level != OptLevel::O0;
367
         && request.opt_level != OptLevel::O0;
368
 
368
 
369
-    if module_has_i128 && needs_optimized_pipeline {
369
+    if ir_module.contains_i128_outside_globals() && needs_optimized_pipeline {
370
         return Err(CaptureFailure {
370
         return Err(CaptureFailure {
371
             input: input.clone(),
371
             input: input.clone(),
372
             opt_level: request.opt_level,
372
             opt_level: request.opt_level,
@@ -411,7 +411,7 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt
411
     }
411
     }
412
 
412
 
413
     let backend_ir = optimized_module.as_ref().unwrap_or(&ir_module);
413
     let backend_ir = optimized_module.as_ref().unwrap_or(&ir_module);
414
-    if module_has_i128 {
414
+    if module_has_i128 && !backend_ir.i128_backend_data_only_supported() {
415
         return Err(CaptureFailure {
415
         return Err(CaptureFailure {
416
             input: input.clone(),
416
             input: input.clone(),
417
             opt_level: request.opt_level,
417
             opt_level: request.opt_level,
@@ -443,7 +443,7 @@ pub fn capture_from_path(request: &CaptureRequest) -> Result<CaptureResult, Capt
443
         );
443
         );
444
     }
444
     }
445
 
445
 
446
-    let asm_text = emit_module_asm(&allocated);
446
+    let asm_text = emit_module_asm(backend_ir, &allocated);
447
     if wants(Stage::Asm) {
447
     if wants(Stage::Asm) {
448
         stages.insert(Stage::Asm, CapturedStage::Text(asm_text.clone()));
448
         stages.insert(Stage::Asm, CapturedStage::Text(asm_text.clone()));
449
     }
449
     }
@@ -792,7 +792,7 @@ fn escape_const_bytes(bytes: &[u8]) -> String {
792
     out
792
     out
793
 }
793
 }
794
 
794
 
795
-fn emit_module_asm(allocated: &[MachineFunction]) -> String {
795
+fn emit_module_asm(module: &crate::ir::inst::Module, allocated: &[MachineFunction]) -> String {
796
     let mut asm_text = String::new();
796
     let mut asm_text = String::new();
797
     asm_text.push_str(".section __TEXT,__text,regular,pure_instructions\n");
797
     asm_text.push_str(".section __TEXT,__text,regular,pure_instructions\n");
798
     for mf in allocated {
798
     for mf in allocated {
@@ -801,6 +801,11 @@ fn emit_module_asm(allocated: &[MachineFunction]) -> String {
801
         asm_text.push('\n');
801
         asm_text.push('\n');
802
     }
802
     }
803
 
803
 
804
+    if !module.globals.is_empty() {
805
+        asm_text.push_str(&emit::emit_globals(&module.globals));
806
+        asm_text.push('\n');
807
+    }
808
+
804
     if let Some(user_func) = allocated.first() {
809
     if let Some(user_func) = allocated.first() {
805
         if user_func.name != "main" {
810
         if user_func.name != "main" {
806
             let _ = write!(
811
             let _ = write!(
tests/fixtures/integer16_globals_backend.f90added
@@ -0,0 +1,8 @@
1
+module integer16_globals_backend
2
+  implicit none
3
+  integer(16), save :: big_scalar = 18446744073709551616_16
4
+  integer(16), save :: big_array(2) = [1_16, 9223372036854775808_16]
5
+contains
6
+  subroutine touch()
7
+  end subroutine touch
8
+end module integer16_globals_backend
tests/i128_backend_data.rsadded
@@ -0,0 +1,95 @@
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 globals_only_i128_backend_emits_expected_words_in_asm() {
24
+    let asm = capture_text(
25
+        CaptureRequest {
26
+            input: fixture("integer16_globals_backend.f90"),
27
+            requested: BTreeSet::from([Stage::Asm]),
28
+            opt_level: OptLevel::O0,
29
+        },
30
+        Stage::Asm,
31
+    );
32
+
33
+    assert!(
34
+        asm.contains(".section __DATA,__data"),
35
+        "asm should emit a data section for i128 globals:\n{}",
36
+        asm
37
+    );
38
+    assert!(
39
+        asm.matches(".p2align 4").count() >= 2,
40
+        "scalar and array i128 globals should request 16-byte alignment:\n{}",
41
+        asm
42
+    );
43
+    assert!(
44
+        asm.contains(".quad 0x0000000000000000\n    .quad 0x0000000000000001"),
45
+        "scalar i128 global should emit low/high 64-bit words:\n{}",
46
+        asm
47
+    );
48
+    assert!(
49
+        asm.contains(".quad 0x0000000000000001\n    .quad 0x0000000000000000"),
50
+        "array i128 initializer should emit the first element's word pair:\n{}",
51
+        asm
52
+    );
53
+    assert!(
54
+        asm.contains(".quad 0x8000000000000000\n    .quad 0x0000000000000000"),
55
+        "array i128 initializer should emit the second element's word pair:\n{}",
56
+        asm
57
+    );
58
+}
59
+
60
+#[test]
61
+fn globals_only_i128_object_snapshot_is_deterministic_at_o2() {
62
+    let source = fixture("integer16_globals_backend.f90");
63
+    let first = capture_text(
64
+        CaptureRequest {
65
+            input: source.clone(),
66
+            requested: BTreeSet::from([Stage::Obj]),
67
+            opt_level: OptLevel::O2,
68
+        },
69
+        Stage::Obj,
70
+    );
71
+    let second = capture_text(
72
+        CaptureRequest {
73
+            input: source,
74
+            requested: BTreeSet::from([Stage::Obj]),
75
+            opt_level: OptLevel::O2,
76
+        },
77
+        Stage::Obj,
78
+    );
79
+
80
+    assert!(
81
+        first.contains("_afs_mod_integer16_globals_backend_big_scalar"),
82
+        "object snapshot should retain the i128 scalar global symbol:\n{}",
83
+        first
84
+    );
85
+    assert!(
86
+        first.contains("_afs_mod_integer16_globals_backend_big_array"),
87
+        "object snapshot should retain the i128 array global symbol:\n{}",
88
+        first
89
+    );
90
+
91
+    assert_eq!(
92
+        first, second,
93
+        "globals-only i128 object snapshots should be deterministic at O2"
94
+    );
95
+}