fortrangoingonforty/armfortas / c5bede5

Browse files

add Rust runtime library crate (armfortas-rt)

staticlib crate producing libarmfortas_rt.a with C-ABI exports:

io.rs: list-directed PRINT for string (ptr+len), i32, i64,
f32, f64, logical, newline. Fortran output formatting rules.

mem.rs: _afs_allocate with 16-byte alignment, _afs_deallocate,
string concat (new allocation), string copy with space padding,
string compare (lexicographic on counted strings).

lifecycle.rs: program_init, program_finalize (flush I/O),
stop (exit 0), error_stop (exit 1).
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
c5bede50e828fdf24a482d268b2d54878e1c8316
Parents
7e6b16a
Tree
99d7451

6 changed files

StatusFile+-
M Cargo.toml 1 1
A runtime/Cargo.toml 9 0
A runtime/src/io.rs 61 0
A runtime/src/lib.rs 10 0
A runtime/src/lifecycle.rs 35 0
A runtime/src/mem.rs 94 0
Cargo.tomlmodified
@@ -1,5 +1,5 @@
11
 [workspace]
2
-members = [".", "afs-as"]
2
+members = [".", "afs-as", "runtime"]
33
 resolver = "2"
44
 
55
 [package]
runtime/Cargo.tomladded
@@ -0,0 +1,9 @@
1
+[package]
2
+name = "armfortas-rt"
3
+version = "0.1.0"
4
+edition = "2021"
5
+description = "ARMFORTAS Fortran runtime library"
6
+
7
+[lib]
8
+name = "armfortas_rt"
9
+crate-type = ["staticlib"]
runtime/src/io.rsadded
@@ -0,0 +1,61 @@
1
+//! I/O subsystem — list-directed PRINT, formatted output.
2
+//!
3
+//! Fortran list-directed output rules:
4
+//! - Leading space before each value
5
+//! - Integers: right-justified in field width
6
+//! - Reals: E-format with full precision
7
+//! - Logicals: T or F
8
+//! - Strings: as-is (no quotes)
9
+//! - Newline at end of PRINT statement
10
+
11
+use std::io::{self, Write};
12
+
13
+/// Print a character string (list-directed).
14
+/// Fortran passes ptr + length (no null terminator).
15
+#[no_mangle]
16
+pub extern "C" fn _afs_print_string(ptr: *const u8, len: i64) {
17
+    let stdout = io::stdout();
18
+    let mut out = stdout.lock();
19
+    let _ = out.write_all(b" ");
20
+    if !ptr.is_null() && len > 0 {
21
+        let slice = unsafe { std::slice::from_raw_parts(ptr, len as usize) };
22
+        let _ = out.write_all(slice);
23
+    }
24
+}
25
+
26
+/// Print a 32-bit integer (list-directed).
27
+#[no_mangle]
28
+pub extern "C" fn _afs_print_int(val: i32) {
29
+    print!(" {}", val);
30
+}
31
+
32
+/// Print a 64-bit integer (list-directed).
33
+#[no_mangle]
34
+pub extern "C" fn _afs_print_int64(val: i64) {
35
+    print!(" {}", val);
36
+}
37
+
38
+/// Print a single-precision real (list-directed, E-format).
39
+#[no_mangle]
40
+pub extern "C" fn _afs_print_real(val: f32) {
41
+    // Fortran list-directed: processor-dependent, typically E format.
42
+    print!("  {:14.7E}", val);
43
+}
44
+
45
+/// Print a double-precision real (list-directed, E-format).
46
+#[no_mangle]
47
+pub extern "C" fn _afs_print_real64(val: f64) {
48
+    print!("  {:22.15E}", val);
49
+}
50
+
51
+/// Print a logical value (list-directed: T or F).
52
+#[no_mangle]
53
+pub extern "C" fn _afs_print_logical(val: i32) {
54
+    print!(" {}", if val != 0 { "T" } else { "F" });
55
+}
56
+
57
+/// End a PRINT statement (newline).
58
+#[no_mangle]
59
+pub extern "C" fn _afs_print_newline() {
60
+    println!();
61
+}
runtime/src/lib.rsadded
@@ -0,0 +1,10 @@
1
+//! ARMFORTAS runtime library (libarmfortas_rt).
2
+//!
3
+//! Provides C-ABI functions called by generated Fortran code:
4
+//! I/O, memory management, string operations, program lifecycle.
5
+//!
6
+//! Built as a static library (.a) linked into every produced binary.
7
+
8
+mod io;
9
+mod mem;
10
+mod lifecycle;
runtime/src/lifecycle.rsadded
@@ -0,0 +1,35 @@
1
+//! Program lifecycle — init, finalize, stop.
2
+
3
+use std::process;
4
+
5
+/// Called before the user's program body.
6
+/// Sets up I/O units, signal handlers, etc.
7
+#[no_mangle]
8
+pub extern "C" fn _afs_program_init() {
9
+    // Reserved for future: I/O unit table, default signal handlers.
10
+}
11
+
12
+/// Called after the user's program body completes normally.
13
+/// Flushes I/O, runs finalizers.
14
+#[no_mangle]
15
+pub extern "C" fn _afs_program_finalize() {
16
+    // Flush stdout/stderr.
17
+    use std::io::Write;
18
+    let _ = std::io::stdout().flush();
19
+    let _ = std::io::stderr().flush();
20
+}
21
+
22
+/// Fortran STOP statement.
23
+#[no_mangle]
24
+pub extern "C" fn _afs_stop() {
25
+    _afs_program_finalize();
26
+    process::exit(0);
27
+}
28
+
29
+/// Fortran ERROR STOP statement.
30
+#[no_mangle]
31
+pub extern "C" fn _afs_error_stop() {
32
+    eprintln!("ERROR STOP");
33
+    _afs_program_finalize();
34
+    process::exit(1);
35
+}
runtime/src/mem.rsadded
@@ -0,0 +1,94 @@
1
+//! Memory management — allocate, deallocate, string operations.
2
+//!
3
+//! All heap allocation goes through these functions so we can
4
+//! track allocations, detect leaks, and implement Fortran's
5
+//! automatic deallocation semantics.
6
+
7
+use std::alloc::{self, Layout};
8
+use std::ptr;
9
+
10
+/// Allocate `size` bytes on the heap. Returns a pointer.
11
+/// Aborts on allocation failure (Fortran ALLOCATE with no STAT=).
12
+#[no_mangle]
13
+pub extern "C" fn _afs_allocate(size: i64) -> *mut u8 {
14
+    if size <= 0 {
15
+        return ptr::null_mut();
16
+    }
17
+    let layout = Layout::from_size_align(size as usize, 16)
18
+        .expect("invalid allocation layout");
19
+    let ptr = unsafe { alloc::alloc(layout) };
20
+    if ptr.is_null() {
21
+        eprintln!("ALLOCATE: out of memory ({} bytes)", size);
22
+        std::process::exit(1);
23
+    }
24
+    ptr
25
+}
26
+
27
+/// Deallocate memory previously allocated by _afs_allocate.
28
+#[no_mangle]
29
+pub extern "C" fn _afs_deallocate(ptr: *mut u8) {
30
+    if ptr.is_null() {
31
+        return;
32
+    }
33
+    // We don't track the layout, so we use a minimum layout.
34
+    // In a full implementation, the descriptor carries the size.
35
+    // For now, this is a known simplification.
36
+    let layout = Layout::from_size_align(1, 1).unwrap();
37
+    unsafe { alloc::dealloc(ptr, layout) };
38
+}
39
+
40
+/// Concatenate two strings. Returns a newly allocated string.
41
+/// Caller is responsible for freeing the result.
42
+#[no_mangle]
43
+pub extern "C" fn _afs_string_concat(
44
+    a: *const u8, alen: i64,
45
+    b: *const u8, blen: i64,
46
+) -> *mut u8 {
47
+    let total = (alen + blen) as usize;
48
+    let result = _afs_allocate(total as i64);
49
+    if !a.is_null() && alen > 0 {
50
+        unsafe { ptr::copy_nonoverlapping(a, result, alen as usize) };
51
+    }
52
+    if !b.is_null() && blen > 0 {
53
+        unsafe { ptr::copy_nonoverlapping(b, result.add(alen as usize), blen as usize) };
54
+    }
55
+    result
56
+}
57
+
58
+/// Copy a string into a fixed-length buffer, padding with spaces.
59
+/// Used for character assignment to fixed-length variables.
60
+#[no_mangle]
61
+pub extern "C" fn _afs_string_copy(
62
+    dest: *mut u8, dest_len: i64,
63
+    src: *const u8, src_len: i64,
64
+) {
65
+    if dest.is_null() || dest_len <= 0 {
66
+        return;
67
+    }
68
+    let copy_len = std::cmp::min(src_len, dest_len) as usize;
69
+    if !src.is_null() && copy_len > 0 {
70
+        unsafe { ptr::copy_nonoverlapping(src, dest, copy_len) };
71
+    }
72
+    // Pad remainder with spaces (Fortran character assignment rule).
73
+    if (copy_len as i64) < dest_len {
74
+        unsafe {
75
+            ptr::write_bytes(dest.add(copy_len), b' ', (dest_len as usize) - copy_len);
76
+        }
77
+    }
78
+}
79
+
80
+/// Compare two strings lexicographically.
81
+/// Returns negative, zero, or positive (like strcmp but for counted strings).
82
+#[no_mangle]
83
+pub extern "C" fn _afs_string_compare(
84
+    a: *const u8, alen: i64,
85
+    b: *const u8, blen: i64,
86
+) -> i32 {
87
+    let sa = if !a.is_null() && alen > 0 {
88
+        unsafe { std::slice::from_raw_parts(a, alen as usize) }
89
+    } else { &[] };
90
+    let sb = if !b.is_null() && blen > 0 {
91
+        unsafe { std::slice::from_raw_parts(b, blen as usize) }
92
+    } else { &[] };
93
+    sa.cmp(sb) as i32
94
+}