1//! Machine Intermediate Representation.
2//! This data is produced by AArch64 Codegen or AArch64 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16
17instructions: std.MultiArrayList(Inst).Slice,
18/// The meaning of this data is determined by `Inst.Tag` value.
19extra: []const u32,
20
21pub const Inst = struct {
22    tag: Tag,
23    /// The meaning of this depends on `tag`.
24    data: Data,
25
26    pub const Tag = enum(u16) {
27        /// Add (immediate)
28        add_immediate,
29        /// Branch conditionally
30        b_cond,
31        /// Branch
32        b,
33        /// Branch with Link
34        bl,
35        /// Branch with Link to Register
36        blr,
37        /// Breakpoint
38        brk,
39        /// Pseudo-instruction: Call extern
40        call_extern,
41        /// Compare (immediate)
42        cmp_immediate,
43        /// Compare (shifted register)
44        cmp_shifted_register,
45        /// Conditional set
46        cset,
47        /// Pseudo-instruction: End of prologue
48        dbg_prologue_end,
49        /// Pseudo-instruction: Beginning of epilogue
50        dbg_epilogue_begin,
51        /// Pseudo-instruction: Update debug line
52        dbg_line,
53        /// Pseudo-instruction: Load memory
54        ///
55        /// Payload is `LoadMemory`
56        load_memory,
57        /// Load Pair of Registers
58        ldp,
59        /// Pseudo-instruction: Load from stack
60        ldr_stack,
61        /// Load Register
62        // TODO: split into ldr_immediate and ldr_register
63        ldr,
64        /// Pseudo-instruction: Load byte from stack
65        ldrb_stack,
66        /// Load Register Byte
67        // TODO: split into ldrb_immediate and ldrb_register
68        ldrb,
69        /// Pseudo-instruction: Load halfword from stack
70        ldrh_stack,
71        /// Load Register Halfword
72        // TODO: split into ldrh_immediate and ldrh_register
73        ldrh,
74        /// Move (to/from SP)
75        mov_to_from_sp,
76        /// Move (register)
77        mov_register,
78        /// Move wide with keep
79        movk,
80        /// Move wide with zero
81        movz,
82        /// No Operation
83        nop,
84        /// Pseudo-instruction: Pop multiple registers
85        pop_regs,
86        /// Psuedo-instruction: Push multiple registers
87        push_regs,
88        /// Return from subroutine
89        ret,
90        /// Store Pair of Registers
91        stp,
92        /// Pseudo-instruction: Store to stack
93        str_stack,
94        /// Store Register
95        // TODO: split into str_immediate and str_register
96        str,
97        /// Pseudo-instruction: Store byte to stack
98        strb_stack,
99        /// Store Register Byte
100        // TODO: split into strb_immediate and strb_register
101        strb,
102        /// Pseudo-instruction: Store halfword to stack
103        strh_stack,
104        /// Store Register Halfword
105        // TODO: split into strh_immediate and strh_register
106        strh,
107        /// Subtract (immediate)
108        sub_immediate,
109        /// Supervisor Call
110        svc,
111    };
112
113    /// The position of an MIR instruction within the `Mir` instructions array.
114    pub const Index = u32;
115
116    /// All instructions have a 4-byte payload, which is contained within
117    /// this union. `Tag` determines which union field is active, as well as
118    /// how to interpret the data within.
119    pub const Data = union {
120        /// No additional data
121        ///
122        /// Used by e.g. nop
123        nop: void,
124        /// Another instruction
125        ///
126        /// Used by e.g. b
127        inst: Index,
128        /// An extern function
129        ///
130        /// Used by e.g. call_extern
131        extern_fn: u32,
132        /// A 16-bit immediate value.
133        ///
134        /// Used by e.g. svc
135        imm16: u16,
136        /// Index into `extra`. Meaning of what can be found there is context-dependent.
137        ///
138        /// Used by e.g. load_memory
139        payload: u32,
140        /// A register
141        ///
142        /// Used by e.g. blr
143        reg: Register,
144        /// Multiple registers
145        ///
146        /// Used by e.g. pop_regs
147        reg_list: u32,
148        /// Another instruction and a condition
149        ///
150        /// Used by e.g. b_cond
151        inst_cond: struct {
152            inst: Index,
153            cond: bits.Instruction.Condition,
154        },
155        /// A register, an unsigned 16-bit immediate, and an optional shift
156        ///
157        /// Used by e.g. movz
158        r_imm16_sh: struct {
159            rd: Register,
160            imm16: u16,
161            hw: u2 = 0,
162        },
163        /// Two registers
164        ///
165        /// Used by e.g. mov_register
166        rr: struct {
167            rd: Register,
168            rn: Register,
169        },
170        /// Two registers, an unsigned 12-bit immediate, and an optional shift
171        ///
172        /// Used by e.g. sub_immediate
173        rr_imm12_sh: struct {
174            rd: Register,
175            rn: Register,
176            imm12: u12,
177            sh: u1 = 0,
178        },
179        /// Three registers and a shift (shift type and 6-bit amount)
180        ///
181        /// Used by e.g. cmp_shifted_register
182        rrr_imm6_shift: struct {
183            rd: Register,
184            rn: Register,
185            rm: Register,
186            imm6: u6,
187            shift: bits.Instruction.AddSubtractShiftedRegisterShift,
188        },
189        /// Three registers and a condition
190        ///
191        /// Used by e.g. cset
192        rrr_cond: struct {
193            rd: Register,
194            rn: Register,
195            rm: Register,
196            cond: bits.Instruction.Condition,
197        },
198        /// Two registers and a LoadStoreOffset
199        ///
200        /// Used by e.g. str_register
201        load_store_register: struct {
202            rt: Register,
203            rn: Register,
204            offset: bits.Instruction.LoadStoreOffset,
205        },
206        /// A registers and a stack offset
207        ///
208        /// Used by e.g. str_register
209        load_store_stack: struct {
210            rt: Register,
211            offset: u32,
212        },
213        /// Three registers and a LoadStorePairOffset
214        ///
215        /// Used by e.g. stp
216        load_store_register_pair: struct {
217            rt: Register,
218            rt2: Register,
219            rn: Register,
220            offset: bits.Instruction.LoadStorePairOffset,
221        },
222        /// Debug info: line and column
223        ///
224        /// Used by e.g. dbg_line
225        dbg_line_column: struct {
226            line: u32,
227            column: u32,
228        },
229    };
230
231    // Make sure we don't accidentally make instructions bigger than expected.
232    // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
233    // comptime {
234    //     if (builtin.mode != .Debug) {
235    //         assert(@sizeOf(Inst) == 8);
236    //     }
237    // }
238};
239
240pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
241    mir.instructions.deinit(gpa);
242    gpa.free(mir.extra);
243    mir.* = undefined;
244}
245
246/// Returns the requested data, as well as the new index which is at the start of the
247/// trailers for the object.
248pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
249    const fields = std.meta.fields(T);
250    var i: usize = index;
251    var result: T = undefined;
252    inline for (fields) |field| {
253        @field(result, field.name) = switch (field.field_type) {
254            u32 => mir.extra[i],
255            i32 => @bitCast(i32, mir.extra[i]),
256            else => @compileError("bad field type"),
257        };
258        i += 1;
259    }
260    return .{
261        .data = result,
262        .end = i,
263    };
264}
265
266pub const LoadMemory = struct {
267    register: u32,
268    addr: u32,
269};
270