| 1 | //! Machine Intermediate Representation. |
| 2 | //! This data is produced by SPARCv9 Codegen or SPARCv9 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 | |
| 9 | const std = @import("std"); |
| 10 | const builtin = @import("builtin"); |
| 11 | const assert = std.debug.assert; |
| 12 | |
| 13 | const Mir = @This(); |
| 14 | const bits = @import("bits.zig"); |
| 15 | const InternPool = @import("../../InternPool.zig"); |
| 16 | const Emit = @import("Emit.zig"); |
| 17 | const codegen = @import("../../codegen.zig"); |
| 18 | const link = @import("../../link.zig"); |
| 19 | const Zcu = @import("../../Zcu.zig"); |
| 20 | |
| 21 | const Instruction = bits.Instruction; |
| 22 | const ASI = bits.Instruction.ASI; |
| 23 | const Register = bits.Register; |
| 24 | |
| 25 | instructions: std.MultiArrayList(Inst).Slice, |
| 26 | |
| 27 | /// The meaning of this data is determined by `Inst.Tag` value. |
| 28 | extra: []const u32, |
| 29 | |
| 30 | pub const Inst = struct { |
| 31 | tag: Tag, |
| 32 | /// The meaning of this depends on `tag`. |
| 33 | data: Data, |
| 34 | |
| 35 | pub const Tag = enum(u16) { |
| 36 | /// Pseudo-instruction: End of prologue |
| 37 | dbg_prologue_end, |
| 38 | /// Pseudo-instruction: Beginning of epilogue |
| 39 | dbg_epilogue_begin, |
| 40 | /// Pseudo-instruction: Update debug line |
| 41 | dbg_line, |
| 42 | |
| 43 | // All the real instructions are ordered by their section number |
| 44 | // in The SPARC Architecture Manual, Version 9. |
| 45 | |
| 46 | /// A.2 Add |
| 47 | /// This uses the arithmetic_3op field. |
| 48 | // TODO add other operations. |
| 49 | add, |
| 50 | addcc, |
| 51 | |
| 52 | /// A.3 Branch on Integer Register with Prediction (BPr) |
| 53 | /// This uses the branch_predict_reg field. |
| 54 | bpr, |
| 55 | |
| 56 | /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) |
| 57 | /// This uses the branch_predict_int field. |
| 58 | bpcc, |
| 59 | |
| 60 | /// A.8 Call and Link |
| 61 | /// This uses the branch_link field. |
| 62 | call, |
| 63 | |
| 64 | /// A.24 Jump and Link |
| 65 | /// This uses the arithmetic_3op field. |
| 66 | jmpl, |
| 67 | |
| 68 | /// A.27 Load Integer |
| 69 | /// This uses the arithmetic_3op field. |
| 70 | /// Note that the ldd variant of this instruction is deprecated, so do not emit |
| 71 | /// it unless specifically requested (e.g. by inline assembly). |
| 72 | // TODO add other operations. |
| 73 | ldub, |
| 74 | lduh, |
| 75 | lduw, |
| 76 | ldx, |
| 77 | |
| 78 | /// A.28 Load Integer from Alternate Space |
| 79 | /// This uses the mem_asi field. |
| 80 | /// Note that the ldda variant of this instruction is deprecated, so do not emit |
| 81 | /// it unless specifically requested (e.g. by inline assembly). |
| 82 | // TODO add other operations. |
| 83 | lduba, |
| 84 | lduha, |
| 85 | lduwa, |
| 86 | ldxa, |
| 87 | |
| 88 | /// A.31 Logical Operations |
| 89 | /// This uses the arithmetic_3op field. |
| 90 | // TODO add other operations. |
| 91 | @"and", |
| 92 | @"or", |
| 93 | xor, |
| 94 | xnor, |
| 95 | |
| 96 | /// A.32 Memory Barrier |
| 97 | /// This uses the membar_mask field. |
| 98 | membar, |
| 99 | |
| 100 | /// A.35 Move Integer Register on Condition (MOVcc) |
| 101 | /// This uses the conditional_move_int field. |
| 102 | movcc, |
| 103 | |
| 104 | /// A.36 Move Integer Register on Register Condition (MOVr) |
| 105 | /// This uses the conditional_move_reg field. |
| 106 | movr, |
| 107 | |
| 108 | /// A.37 Multiply and Divide (64-bit) |
| 109 | /// This uses the arithmetic_3op field. |
| 110 | mulx, |
| 111 | sdivx, |
| 112 | udivx, |
| 113 | |
| 114 | /// A.40 No Operation |
| 115 | /// This uses the nop field. |
| 116 | nop, |
| 117 | |
| 118 | /// A.45 RETURN |
| 119 | /// This uses the arithmetic_2op field. |
| 120 | @"return", |
| 121 | |
| 122 | /// A.46 SAVE and RESTORE |
| 123 | /// This uses the arithmetic_3op field. |
| 124 | save, |
| 125 | restore, |
| 126 | |
| 127 | /// A.48 SETHI |
| 128 | /// This uses the sethi field. |
| 129 | sethi, |
| 130 | |
| 131 | /// A.49 Shift |
| 132 | /// This uses the shift field. |
| 133 | sll, |
| 134 | srl, |
| 135 | sra, |
| 136 | sllx, |
| 137 | srlx, |
| 138 | srax, |
| 139 | |
| 140 | /// A.54 Store Integer |
| 141 | /// This uses the arithmetic_3op field. |
| 142 | /// Note that the std variant of this instruction is deprecated, so do not emit |
| 143 | /// it unless specifically requested (e.g. by inline assembly). |
| 144 | // TODO add other operations. |
| 145 | stb, |
| 146 | sth, |
| 147 | stw, |
| 148 | stx, |
| 149 | |
| 150 | /// A.55 Store Integer into Alternate Space |
| 151 | /// This uses the mem_asi field. |
| 152 | /// Note that the stda variant of this instruction is deprecated, so do not emit |
| 153 | /// it unless specifically requested (e.g. by inline assembly). |
| 154 | // TODO add other operations. |
| 155 | stba, |
| 156 | stha, |
| 157 | stwa, |
| 158 | stxa, |
| 159 | |
| 160 | /// A.56 Subtract |
| 161 | /// This uses the arithmetic_3op field. |
| 162 | // TODO add other operations. |
| 163 | sub, |
| 164 | subcc, |
| 165 | |
| 166 | /// A.61 Trap on Integer Condition Codes (Tcc) |
| 167 | /// This uses the trap field. |
| 168 | tcc, |
| 169 | |
| 170 | // SPARCv9 synthetic instructions |
| 171 | // Note that the instructions that is added here are only those that |
| 172 | // will simplify backend development. Synthetic instructions that is |
| 173 | // only used to provide syntactic sugar in, e.g. inline assembly should |
| 174 | // be deconstructed inside the parser instead. |
| 175 | // See also: G.3 Synthetic Instructions |
| 176 | // TODO add more synthetic instructions |
| 177 | |
| 178 | /// Comparison |
| 179 | /// This uses the arithmetic_2op field. |
| 180 | cmp, // cmp rs1, rs2/imm -> subcc rs1, rs2/imm, %g0 |
| 181 | |
| 182 | /// Copy register/immediate contents to another register |
| 183 | /// This uses the arithmetic_2op field, with rs1 |
| 184 | /// being the *destination* register. |
| 185 | // TODO is it okay to abuse rs1 in this way? |
| 186 | mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1 |
| 187 | |
| 188 | /// Bitwise negation |
| 189 | /// This uses the arithmetic_2op field, with rs1 |
| 190 | /// being the *destination* register. |
| 191 | // TODO is it okay to abuse rs1 in this way? |
| 192 | // not rs2, rs1 -> xnor rs2, %g0, rs1 |
| 193 | // not imm, rs1 -> xnor %g0, imm, rs1 |
| 194 | not, |
| 195 | }; |
| 196 | |
| 197 | /// The position of an MIR instruction within the `Mir` instructions array. |
| 198 | pub const Index = u32; |
| 199 | |
| 200 | /// All instructions have a 8-byte payload, which is contained within |
| 201 | /// this union. `Tag` determines which union field is active, as well as |
| 202 | /// how to interpret the data within. |
| 203 | // TODO this is a quick-n-dirty solution that needs to be cleaned up. |
| 204 | pub const Data = union { |
| 205 | /// Debug info: line and column |
| 206 | /// |
| 207 | /// Used by e.g. dbg_line |
| 208 | dbg_line_column: struct { |
| 209 | line: u32, |
| 210 | column: u32, |
| 211 | }, |
| 212 | |
| 213 | /// Two operand arithmetic. |
| 214 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 215 | /// otherwise it uses rs2 field. |
| 216 | /// |
| 217 | /// Used by e.g. return |
| 218 | arithmetic_2op: struct { |
| 219 | is_imm: bool, |
| 220 | rs1: Register, |
| 221 | rs2_or_imm: union { |
| 222 | rs2: Register, |
| 223 | imm: i13, |
| 224 | }, |
| 225 | }, |
| 226 | |
| 227 | /// Three operand arithmetic. |
| 228 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 229 | /// otherwise it uses rs2 field. |
| 230 | /// |
| 231 | /// Used by e.g. add, sub |
| 232 | arithmetic_3op: struct { |
| 233 | is_imm: bool, |
| 234 | rd: Register, |
| 235 | rs1: Register, |
| 236 | rs2_or_imm: union { |
| 237 | rs2: Register, |
| 238 | imm: i13, |
| 239 | }, |
| 240 | }, |
| 241 | |
| 242 | /// Branch and link (always unconditional). |
| 243 | /// Used by e.g. call |
| 244 | branch_link: struct { |
| 245 | inst: Index, |
| 246 | // link is always %o7 |
| 247 | }, |
| 248 | |
| 249 | /// Branch with prediction, checking the integer status code |
| 250 | /// Used by e.g. bpcc |
| 251 | branch_predict_int: struct { |
| 252 | annul: bool = false, |
| 253 | pt: bool = true, |
| 254 | ccr: Instruction.CCR, |
| 255 | cond: Instruction.ICondition, |
| 256 | inst: Index, |
| 257 | }, |
| 258 | |
| 259 | /// Branch with prediction, comparing a register's content with zero |
| 260 | /// Used by e.g. bpr |
| 261 | branch_predict_reg: struct { |
| 262 | annul: bool = false, |
| 263 | pt: bool = true, |
| 264 | cond: Instruction.RCondition, |
| 265 | rs1: Register, |
| 266 | inst: Index, |
| 267 | }, |
| 268 | |
| 269 | /// ASI-tagged memory operations. |
| 270 | /// Used by e.g. ldxa, stxa |
| 271 | mem_asi: struct { |
| 272 | rd: Register, |
| 273 | rs1: Register, |
| 274 | rs2: Register = .g0, |
| 275 | asi: ASI, |
| 276 | }, |
| 277 | |
| 278 | /// Membar mask, controls the barrier behavior |
| 279 | /// Used by e.g. membar |
| 280 | membar_mask: struct { |
| 281 | mmask: Instruction.MemOrderingConstraint = .{}, |
| 282 | cmask: Instruction.MemCompletionConstraint = .{}, |
| 283 | }, |
| 284 | |
| 285 | /// Conditional move, checking the integer status code |
| 286 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 287 | /// otherwise it uses rs2 field. |
| 288 | /// |
| 289 | /// Used by e.g. movcc |
| 290 | conditional_move_int: struct { |
| 291 | is_imm: bool, |
| 292 | ccr: Instruction.CCR, |
| 293 | cond: Instruction.Condition, |
| 294 | rd: Register, |
| 295 | rs2_or_imm: union { |
| 296 | rs2: Register, |
| 297 | imm: i11, |
| 298 | }, |
| 299 | }, |
| 300 | |
| 301 | /// Conditional move, comparing a register's content with zero |
| 302 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 303 | /// otherwise it uses rs2 field. |
| 304 | /// |
| 305 | /// Used by e.g. movr |
| 306 | conditional_move_reg: struct { |
| 307 | is_imm: bool, |
| 308 | cond: Instruction.RCondition, |
| 309 | rd: Register, |
| 310 | rs1: Register, |
| 311 | rs2_or_imm: union { |
| 312 | rs2: Register, |
| 313 | imm: i10, |
| 314 | }, |
| 315 | }, |
| 316 | |
| 317 | /// No additional data |
| 318 | /// |
| 319 | /// Used by e.g. flushw |
| 320 | nop: void, |
| 321 | |
| 322 | /// SETHI operands. |
| 323 | /// |
| 324 | /// Used by sethi |
| 325 | sethi: struct { |
| 326 | rd: Register, |
| 327 | imm: u22, |
| 328 | }, |
| 329 | |
| 330 | /// Shift operands. |
| 331 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 332 | /// otherwise it uses rs2 field. |
| 333 | /// |
| 334 | /// Used by e.g. sllx |
| 335 | shift: struct { |
| 336 | is_imm: bool, |
| 337 | rd: Register, |
| 338 | rs1: Register, |
| 339 | rs2_or_imm: union { |
| 340 | rs2: Register, |
| 341 | imm: u6, |
| 342 | }, |
| 343 | }, |
| 344 | |
| 345 | /// Trap. |
| 346 | /// if is_imm true then it uses the imm field of rs2_or_imm, |
| 347 | /// otherwise it uses rs2 field. |
| 348 | /// |
| 349 | /// Used by e.g. tcc |
| 350 | trap: struct { |
| 351 | is_imm: bool = true, |
| 352 | cond: Instruction.ICondition, |
| 353 | ccr: Instruction.CCR = .icc, |
| 354 | rs1: Register = .g0, |
| 355 | rs2_or_imm: union { |
| 356 | rs2: Register, |
| 357 | imm: u7, |
| 358 | }, |
| 359 | }, |
| 360 | }; |
| 361 | |
| 362 | // Make sure we don't accidentally make instructions bigger than expected. |
| 363 | // Note that in safety builds, Zig is allowed to insert a secret field for safety checks. |
| 364 | comptime { |
| 365 | if (!std.debug.runtime_safety) { |
| 366 | assert(@sizeOf(Data) == 8); |
| 367 | } |
| 368 | } |
| 369 | }; |
| 370 | |
| 371 | pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { |
| 372 | mir.instructions.deinit(gpa); |
| 373 | gpa.free(mir.extra); |
| 374 | mir.* = undefined; |
| 375 | } |
| 376 | |
| 377 | pub fn emit( |
| 378 | mir: Mir, |
| 379 | lf: *link.File, |
| 380 | pt: Zcu.PerThread, |
| 381 | func_index: InternPool.Index, |
| 382 | atom_index: link.File.AtomId, |
| 383 | w: *std.Io.Writer, |
| 384 | debug_output: link.File.DebugInfoOutput, |
| 385 | ) link.EmitError!void { |
| 386 | _ = atom_index; |
| 387 | const zcu = pt.zcu; |
| 388 | const func = zcu.funcInfo(func_index); |
| 389 | const nav = func.owner_nav; |
| 390 | const mod = zcu.navFileScope(nav).mod.?; |
| 391 | var e: Emit = .{ |
| 392 | .mir = mir, |
| 393 | .bin_file = lf, |
| 394 | .debug_output = debug_output, |
| 395 | .target = &mod.resolved_target.result, |
| 396 | .src_loc = zcu.navSrcLoc(nav), |
| 397 | .w = w, |
| 398 | .prev_di_pc = 0, |
| 399 | .prev_di_line = func.lbrace_line, |
| 400 | .prev_di_column = func.lbrace_column, |
| 401 | }; |
| 402 | defer e.deinit(); |
| 403 | e.emitMir() catch |err| switch (err) { |
| 404 | error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), |
| 405 | else => |err1| return err1, |
| 406 | }; |
| 407 | } |
| 408 | |
| 409 | /// Returns the requested data, as well as the new index which is at the start of the |
| 410 | /// trailers for the object. |
| 411 | pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { |
| 412 | const info = @typeInfo(T).@"struct"; |
| 413 | var i: usize = index; |
| 414 | var result: T = undefined; |
| 415 | inline for (info.field_names, info.field_types) |field_name, field_type| { |
| 416 | @field(result, field_name) = switch (field_type) { |
| 417 | u32 => mir.extra[i], |
| 418 | i32 => @as(i32, @bitCast(mir.extra[i])), |
| 419 | else => @compileError("bad field type"), |
| 420 | }; |
| 421 | i += 1; |
| 422 | } |
| 423 | return .{ |
| 424 | .data = result, |
| 425 | .end = i, |
| 426 | }; |
| 427 | } |