llvm-native-core 0.1.11

LLVM-native core semantic engine — IR, CodeGen, X86 MC, Clang frontend pipeline
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
//! SystemZ Target Machine — target description and configuration.
//!
//! Defines the IBM z/Architecture (s390x) target: register layout, calling
//! convention, frame lowering, and code emission pipeline.
//!
//! Key characteristics:
//! - 64-bit big-endian CISC
//! - 16 GPRs, 16 FPRs, 16 ARs
//! - PSW includes condition code and addressing mode bits
//! - Stack grows downward; r15 = stack pointer
//! - Frame pointer: r11; return address: r14

use crate::systemz::systemz_register_info::SystemzRegister;

// ============================================================================
//  SystemZ Calling Convention
// ============================================================================

/// SystemZ calling convention (s390x ELF ABI).
pub struct SystemzCallingConvention {
    /// Argument registers in order (r2–r6).
    pub arg_regs: Vec<SystemzRegister>,
    /// Return value registers (r2, r3).
    pub ret_regs: Vec<SystemzRegister>,
    /// FP argument registers (f0, f2, f4, f6).
    pub fp_arg_regs: Vec<SystemzRegister>,
    /// FP return register (f0).
    pub fp_ret_regs: Vec<SystemzRegister>,
    /// Callee-saved registers.
    pub callee_saved: Vec<SystemzRegister>,
    /// Caller-saved registers.
    pub caller_saved: Vec<SystemzRegister>,
    /// Stack pointer.
    pub sp: SystemzRegister,
    /// Frame pointer.
    pub fp: SystemzRegister,
    /// Return address register.
    pub ra: SystemzRegister,
}

impl SystemzCallingConvention {
    pub fn new() -> Self {
        SystemzCallingConvention {
            arg_regs: vec![
                SystemzRegister::R2,
                SystemzRegister::R3,
                SystemzRegister::R4,
                SystemzRegister::R5,
                SystemzRegister::R6,
            ],
            ret_regs: vec![SystemzRegister::R2, SystemzRegister::R3],
            fp_arg_regs: vec![
                SystemzRegister::F0,
                SystemzRegister::F2,
                SystemzRegister::F4,
                SystemzRegister::F6,
            ],
            fp_ret_regs: vec![SystemzRegister::F0],
            callee_saved: vec![
                SystemzRegister::R6,
                SystemzRegister::R7,
                SystemzRegister::R8,
                SystemzRegister::R9,
                SystemzRegister::R10,
                SystemzRegister::R11,
                SystemzRegister::R12,
                SystemzRegister::R13,
                SystemzRegister::R14,
                SystemzRegister::R15,
                SystemzRegister::F8,
                SystemzRegister::F9,
                SystemzRegister::F10,
                SystemzRegister::F11,
                SystemzRegister::F12,
                SystemzRegister::F13,
                SystemzRegister::F14,
                SystemzRegister::F15,
            ],
            caller_saved: vec![
                SystemzRegister::R0,
                SystemzRegister::R1,
                SystemzRegister::R2,
                SystemzRegister::R3,
                SystemzRegister::R4,
                SystemzRegister::R5,
                SystemzRegister::F0,
                SystemzRegister::F1,
                SystemzRegister::F2,
                SystemzRegister::F3,
                SystemzRegister::F4,
                SystemzRegister::F5,
                SystemzRegister::F6,
                SystemzRegister::F7,
            ],
            sp: SystemzRegister::R15,
            fp: SystemzRegister::R11,
            ra: SystemzRegister::R14,
        }
    }
}

// ============================================================================
//  SystemZ Target Machine
// ============================================================================

/// Architecture level configuration for SystemZ.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SystemzArchLevel {
    Z10,   // z10 (2008)
    Z196,  // z196 (2010)
    ZEC12, // zEC12 (2012)
    Z13,   // z13 (2015)
    Z14,   // z14 (2017)
    Z15,   // z15 (2019)
    Z16,   // z16 (2022)
}

/// The main SystemZ target machine configuration.
pub struct SystemzTargetMachine {
    /// CPU model string.
    pub cpu: String,
    /// Architecture level.
    pub arch_level: SystemzArchLevel,
    /// Whether to use the vector facility.
    pub has_vector: bool,
    /// Whether to use the decimal floating-point facility.
    pub has_dfp: bool,
    /// Whether to use the transactional execution facility.
    pub has_tx: bool,
    /// Calling convention.
    pub calling_conv: SystemzCallingConvention,
}

impl SystemzTargetMachine {
    /// Create a target machine for the specified architecture level.
    pub fn new(arch: SystemzArchLevel) -> Self {
        let (cpu, has_vector, has_dfp, has_tx) = match arch {
            SystemzArchLevel::Z10 => ("z10", false, false, false),
            SystemzArchLevel::Z196 => ("z196", false, true, false),
            SystemzArchLevel::ZEC12 => ("zEC12", false, true, true),
            SystemzArchLevel::Z13 => ("z13", true, true, true),
            SystemzArchLevel::Z14 => ("z14", true, true, true),
            SystemzArchLevel::Z15 => ("z15", true, true, true),
            SystemzArchLevel::Z16 => ("z16", true, true, true),
        };

        SystemzTargetMachine {
            cpu: cpu.to_string(),
            arch_level: arch,
            has_vector,
            has_dfp,
            has_tx,
            calling_conv: SystemzCallingConvention::new(),
        }
    }

    /// Default target (z14).
    pub fn default_target() -> Self {
        Self::new(SystemzArchLevel::Z14)
    }

    /// Get the target triple string.
    pub fn get_target_triple(&self) -> String {
        "s390x-ibm-linux-gnu".to_string()
    }

    /// Get the data layout string for s390x.
    pub fn get_data_layout(&self) -> String {
        "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64".to_string()
    }

    /// Get pointer size in bits (64).
    pub fn get_pointer_size(&self) -> u32 {
        64
    }

    /// Get register width in bits.
    pub fn get_register_width(&self) -> u32 {
        64
    }

    /// Get the stack alignment (8 bytes).
    pub fn get_stack_alignment(&self) -> u32 {
        8
    }

    /// Get the red zone size (160 bytes, per ABI).
    pub fn get_red_zone_size(&self) -> u32 {
        160
    }

    /// Check if vector facility is available.
    pub fn has_vector_facility(&self) -> bool {
        self.has_vector
    }

    /// Check if decimal floating point is available.
    pub fn has_dfp_facility(&self) -> bool {
        self.has_dfp
    }

    /// Check if transactional execution is available.
    pub fn has_tx_facility(&self) -> bool {
        self.has_tx
    }

    /// Get the CPU name.
    pub fn get_cpu(&self) -> &str {
        &self.cpu
    }

    /// Get the architecture level.
    pub fn get_arch_level(&self) -> SystemzArchLevel {
        self.arch_level
    }

    /// Get the default page size.
    pub fn get_page_size(&self) -> u32 {
        4096
    }

    /// Get the minimum stack size.
    pub fn get_min_stack_size(&self) -> u32 {
        8192 // 8 KiB minimum
    }

    /// Get maximum alignment for any data type.
    pub fn get_max_alignment(&self) -> u32 {
        8
    }

    /// Check if unaligned memory access is supported.
    pub fn supports_unaligned_access(&self) -> bool {
        // All modern z processors support unaligned access
        true
    }
}

// ============================================================================
//  Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_z14_config() {
        let tm = SystemzTargetMachine::new(SystemzArchLevel::Z14);
        assert!(tm.has_vector);
        assert!(tm.has_dfp);
        assert!(tm.has_tx);
        assert_eq!(tm.cpu, "z14");
    }

    #[test]
    fn test_z10_config() {
        let tm = SystemzTargetMachine::new(SystemzArchLevel::Z10);
        assert!(!tm.has_vector);
        assert!(!tm.has_tx);
    }

    #[test]
    fn test_z196_has_dfp() {
        let tm = SystemzTargetMachine::new(SystemzArchLevel::Z196);
        assert!(tm.has_dfp);
        assert!(!tm.has_vector);
    }

    #[test]
    fn test_zec12_has_tx() {
        let tm = SystemzTargetMachine::new(SystemzArchLevel::ZEC12);
        assert!(tm.has_tx);
    }

    #[test]
    fn test_target_triple() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_target_triple(), "s390x-ibm-linux-gnu");
    }

    #[test]
    fn test_data_layout() {
        let tm = SystemzTargetMachine::default_target();
        let layout = tm.get_data_layout();
        assert!(layout.starts_with("E"));
        assert!(layout.contains("i64:64"));
    }

    #[test]
    fn test_pointer_size() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_pointer_size(), 64);
    }

    #[test]
    fn test_register_width() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_register_width(), 64);
    }

    #[test]
    fn test_stack_alignment() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_stack_alignment(), 8);
    }

    #[test]
    fn test_red_zone_size() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_red_zone_size(), 160);
    }

    #[test]
    fn test_page_size() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_page_size(), 4096);
    }

    #[test]
    fn test_min_stack_size() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_min_stack_size(), 8192);
    }

    #[test]
    fn test_supports_unaligned_access() {
        let tm = SystemzTargetMachine::default_target();
        assert!(tm.supports_unaligned_access());
    }

    #[test]
    fn test_calling_convention_arg_regs() {
        let cc = SystemzCallingConvention::new();
        assert_eq!(cc.arg_regs.len(), 5);
        assert_eq!(cc.arg_regs[0], SystemzRegister::R2);
    }

    #[test]
    fn test_calling_convention_ret_regs() {
        let cc = SystemzCallingConvention::new();
        assert_eq!(cc.ret_regs, vec![SystemzRegister::R2, SystemzRegister::R3]);
    }

    #[test]
    fn test_calling_convention_fp_arg_regs() {
        let cc = SystemzCallingConvention::new();
        assert_eq!(cc.fp_arg_regs[0], SystemzRegister::F0);
        assert_eq!(cc.fp_arg_regs[2], SystemzRegister::F4);
    }

    #[test]
    fn test_calling_convention_fp_ret_regs() {
        let cc = SystemzCallingConvention::new();
        assert_eq!(cc.fp_ret_regs, vec![SystemzRegister::F0]);
    }

    #[test]
    fn test_calling_convention_callee_saved() {
        let cc = SystemzCallingConvention::new();
        assert!(cc.callee_saved.contains(&SystemzRegister::R15));
        assert!(cc.callee_saved.contains(&SystemzRegister::F15));
        assert!(!cc.callee_saved.contains(&SystemzRegister::R0));
    }

    #[test]
    fn test_calling_convention_sp_fp_ra() {
        let cc = SystemzCallingConvention::new();
        assert_eq!(cc.sp, SystemzRegister::R15);
        assert_eq!(cc.fp, SystemzRegister::R11);
        assert_eq!(cc.ra, SystemzRegister::R14);
    }

    #[test]
    fn test_default_target_is_z14() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.cpu, "z14");
        assert!(tm.has_vector);
    }

    #[test]
    fn test_arch_level_enum_variants() {
        // Verify all variants exist
        let levels = [
            SystemzArchLevel::Z10,
            SystemzArchLevel::Z196,
            SystemzArchLevel::ZEC12,
            SystemzArchLevel::Z13,
            SystemzArchLevel::Z14,
            SystemzArchLevel::Z15,
            SystemzArchLevel::Z16,
        ];
        for l in &levels {
            let tm = SystemzTargetMachine::new(*l);
            assert!(!tm.cpu.is_empty());
        }
    }

    #[test]
    fn test_max_alignment() {
        let tm = SystemzTargetMachine::default_target();
        assert_eq!(tm.get_max_alignment(), 8);
    }
}