//! Machine Intermediate Representation. //! This data is produced by SPARCv9 Codegen or SPARCv9 assembly parsing //! These instructions have a 1:1 correspondence with machine code instructions //! for the target. MIR can be lowered to source-annotated textual assembly code //! instructions, or it can be lowered to machine code. //! The main purpose of MIR is to postpone the assignment of offsets until Isel, //! so that, for example, the smaller encodings of jump instructions can be used. const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; const Mir = @This(); const bits = @import("bits.zig"); const InternPool = @import("../../InternPool.zig"); const Emit = @import("Emit.zig"); const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const Zcu = @import("../../Zcu.zig"); const Instruction = bits.Instruction; const ASI = bits.Instruction.ASI; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, pub const Inst = struct { tag: Tag, /// The meaning of this depends on `tag`. data: Data, pub const Tag = enum(u16) { /// Pseudo-instruction: End of prologue dbg_prologue_end, /// Pseudo-instruction: Beginning of epilogue dbg_epilogue_begin, /// Pseudo-instruction: Update debug line dbg_line, // All the real instructions are ordered by their section number // in The SPARC Architecture Manual, Version 9. /// A.2 Add /// This uses the arithmetic_3op field. // TODO add other operations. add, addcc, /// A.3 Branch on Integer Register with Prediction (BPr) /// This uses the branch_predict_reg field. bpr, /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) /// This uses the branch_predict_int field. bpcc, /// A.8 Call and Link /// This uses the branch_link field. call, /// A.24 Jump and Link /// This uses the arithmetic_3op field. jmpl, /// A.27 Load Integer /// This uses the arithmetic_3op field. /// Note that the ldd variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. ldub, lduh, lduw, ldx, /// A.28 Load Integer from Alternate Space /// This uses the mem_asi field. /// Note that the ldda variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. lduba, lduha, lduwa, ldxa, /// A.31 Logical Operations /// This uses the arithmetic_3op field. // TODO add other operations. @"and", @"or", xor, xnor, /// A.32 Memory Barrier /// This uses the membar_mask field. membar, /// A.35 Move Integer Register on Condition (MOVcc) /// This uses the conditional_move_int field. movcc, /// A.36 Move Integer Register on Register Condition (MOVr) /// This uses the conditional_move_reg field. movr, /// A.37 Multiply and Divide (64-bit) /// This uses the arithmetic_3op field. mulx, sdivx, udivx, /// A.40 No Operation /// This uses the nop field. nop, /// A.45 RETURN /// This uses the arithmetic_2op field. @"return", /// A.46 SAVE and RESTORE /// This uses the arithmetic_3op field. save, restore, /// A.48 SETHI /// This uses the sethi field. sethi, /// A.49 Shift /// This uses the shift field. sll, srl, sra, sllx, srlx, srax, /// A.54 Store Integer /// This uses the arithmetic_3op field. /// Note that the std variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. stb, sth, stw, stx, /// A.55 Store Integer into Alternate Space /// This uses the mem_asi field. /// Note that the stda variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. stba, stha, stwa, stxa, /// A.56 Subtract /// This uses the arithmetic_3op field. // TODO add other operations. sub, subcc, /// A.61 Trap on Integer Condition Codes (Tcc) /// This uses the trap field. tcc, // SPARCv9 synthetic instructions // Note that the instructions that is added here are only those that // will simplify backend development. Synthetic instructions that is // only used to provide syntactic sugar in, e.g. inline assembly should // be deconstructed inside the parser instead. // See also: G.3 Synthetic Instructions // TODO add more synthetic instructions /// Comparison /// This uses the arithmetic_2op field. cmp, // cmp rs1, rs2/imm -> subcc rs1, rs2/imm, %g0 /// Copy register/immediate contents to another register /// This uses the arithmetic_2op field, with rs1 /// being the *destination* register. // TODO is it okay to abuse rs1 in this way? mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1 /// Bitwise negation /// This uses the arithmetic_2op field, with rs1 /// being the *destination* register. // TODO is it okay to abuse rs1 in this way? // not rs2, rs1 -> xnor rs2, %g0, rs1 // not imm, rs1 -> xnor %g0, imm, rs1 not, }; /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; /// All instructions have a 8-byte payload, which is contained within /// this union. `Tag` determines which union field is active, as well as /// how to interpret the data within. // TODO this is a quick-n-dirty solution that needs to be cleaned up. pub const Data = union { /// Debug info: line and column /// /// Used by e.g. dbg_line dbg_line_column: struct { line: u32, column: u32, }, /// Two operand arithmetic. /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. return arithmetic_2op: struct { is_imm: bool, rs1: Register, rs2_or_imm: union { rs2: Register, imm: i13, }, }, /// Three operand arithmetic. /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. add, sub arithmetic_3op: struct { is_imm: bool, rd: Register, rs1: Register, rs2_or_imm: union { rs2: Register, imm: i13, }, }, /// Branch and link (always unconditional). /// Used by e.g. call branch_link: struct { inst: Index, // link is always %o7 }, /// Branch with prediction, checking the integer status code /// Used by e.g. bpcc branch_predict_int: struct { annul: bool = false, pt: bool = true, ccr: Instruction.CCR, cond: Instruction.ICondition, inst: Index, }, /// Branch with prediction, comparing a register's content with zero /// Used by e.g. bpr branch_predict_reg: struct { annul: bool = false, pt: bool = true, cond: Instruction.RCondition, rs1: Register, inst: Index, }, /// ASI-tagged memory operations. /// Used by e.g. ldxa, stxa mem_asi: struct { rd: Register, rs1: Register, rs2: Register = .g0, asi: ASI, }, /// Membar mask, controls the barrier behavior /// Used by e.g. membar membar_mask: struct { mmask: Instruction.MemOrderingConstraint = .{}, cmask: Instruction.MemCompletionConstraint = .{}, }, /// Conditional move, checking the integer status code /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. movcc conditional_move_int: struct { is_imm: bool, ccr: Instruction.CCR, cond: Instruction.Condition, rd: Register, rs2_or_imm: union { rs2: Register, imm: i11, }, }, /// Conditional move, comparing a register's content with zero /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. movr conditional_move_reg: struct { is_imm: bool, cond: Instruction.RCondition, rd: Register, rs1: Register, rs2_or_imm: union { rs2: Register, imm: i10, }, }, /// No additional data /// /// Used by e.g. flushw nop: void, /// SETHI operands. /// /// Used by sethi sethi: struct { rd: Register, imm: u22, }, /// Shift operands. /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. sllx shift: struct { is_imm: bool, rd: Register, rs1: Register, rs2_or_imm: union { rs2: Register, imm: u6, }, }, /// Trap. /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. tcc trap: struct { is_imm: bool = true, cond: Instruction.ICondition, ccr: Instruction.CCR = .icc, rs1: Register = .g0, rs2_or_imm: union { rs2: Register, imm: u7, }, }, }; // Make sure we don't accidentally make instructions bigger than expected. // Note that in safety builds, Zig is allowed to insert a secret field for safety checks. comptime { if (!std.debug.runtime_safety) { assert(@sizeOf(Data) == 8); } } }; pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); mir.* = undefined; } pub fn emit( mir: Mir, lf: *link.File, pt: Zcu.PerThread, src_loc: Zcu.LazySrcLoc, func_index: InternPool.Index, atom_index: u32, w: *std.Io.Writer, debug_output: link.File.DebugInfoOutput, ) (codegen.CodeGenError || std.Io.Writer.Error)!void { _ = atom_index; const zcu = pt.zcu; const func = zcu.funcInfo(func_index); const nav = func.owner_nav; const mod = zcu.navFileScope(nav).mod.?; var e: Emit = .{ .mir = mir, .bin_file = lf, .debug_output = debug_output, .target = &mod.resolved_target.result, .src_loc = src_loc, .w = w, .prev_di_pc = 0, .prev_di_line = func.lbrace_line, .prev_di_column = func.lbrace_column, }; defer e.deinit(); e.emitMir() catch |err| switch (err) { error.EmitFail => return zcu.codegenFailMsg(nav, e.err_msg.?), else => |err1| return err1, }; } /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { const fields = std.meta.fields(T); var i: usize = index; var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.type) { u32 => mir.extra[i], i32 => @as(i32, @bitCast(mir.extra[i])), else => @compileError("bad field type"), }; i += 1; } return .{ .data = result, .end = i, }; }