zig/src/arch/sparc64/Mir.zig
Jacob Young f58200e3f2 Elf2: create a new linker from scratch
This iteration already has significantly better incremental support.

Closes #24110
2025-09-21 14:09:14 -07:00

429 lines
13 KiB
Zig

//! 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,
};
}