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