stage2: sparcv9: implement basic instruction lowering

This commit is contained in:
Koakuma
2022-04-10 14:29:06 +07:00
parent 5e2045cbe5
commit 7051970ad7
4 changed files with 231 additions and 57 deletions

View File

@@ -1270,12 +1270,12 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
if (typed_value.val.castTag(.decl_ref)) |payload| {
_ = payload;
return self.fail("TODO implement lowerDeclRef", .{});
return self.fail("TODO implement lowerDeclRef non-mut", .{});
// return self.lowerDeclRef(typed_value, payload.data);
}
if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
_ = payload;
return self.fail("TODO implement lowerDeclRef", .{});
return self.fail("TODO implement lowerDeclRef mut", .{});
// return self.lowerDeclRef(typed_value, payload.data.decl);
}
const target = self.target.*;

View File

@@ -2,6 +2,7 @@
//! machine code
const std = @import("std");
const Endian = std.builtin.Endian;
const assert = std.debug.assert;
const link = @import("../../link.zig");
const Module = @import("../../Module.zig");
@@ -14,6 +15,8 @@ const leb128 = std.leb;
const Emit = @This();
const Mir = @import("Mir.zig");
const bits = @import("bits.zig");
const Instruction = bits.Instruction;
const Register = bits.Register;
mir: Mir,
bin_file: *link.File,
@@ -47,7 +50,7 @@ pub fn emitMir(
.dbg_prologue_end => try emit.mirDebugPrologueEnd(),
.dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
.add => @panic("TODO implement sparcv9 add"),
.add => try emit.mirArithmetic3Op(inst),
.bpcc => @panic("TODO implement sparcv9 bpcc"),
@@ -56,22 +59,22 @@ pub fn emitMir(
.jmpl => @panic("TODO implement sparcv9 jmpl"),
.jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
.@"or" => @panic("TODO implement sparcv9 or"),
.@"or" => try emit.mirArithmetic3Op(inst),
.nop => @panic("TODO implement sparcv9 nop"),
.nop => try emit.mirNop(),
.@"return" => @panic("TODO implement sparcv9 return"),
.@"return" => try emit.mirArithmetic2Op(inst),
.save => @panic("TODO implement sparcv9 save"),
.restore => @panic("TODO implement sparcv9 restore"),
.save => try emit.mirArithmetic3Op(inst),
.restore => try emit.mirArithmetic3Op(inst),
.sethi => @panic("TODO implement sparcv9 sethi"),
.sllx => @panic("TODO implement sparcv9 sllx"),
.sub => @panic("TODO implement sparcv9 sub"),
.sub => try emit.mirArithmetic3Op(inst),
.tcc => @panic("TODO implement sparcv9 tcc"),
.tcc => try emit.mirTrap(inst),
}
}
}
@@ -80,6 +83,129 @@ pub fn deinit(emit: *Emit) void {
emit.* = undefined;
}
fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
_ = dbg_arg_info;
switch (tag) {
.dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
else => unreachable,
}
}
fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
switch (tag) {
.dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
else => unreachable,
}
}
fn mirDebugPrologueEnd(self: *Emit) !void {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
fn mirDebugEpilogueBegin(self: *Emit) !void {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op;
const rs1 = data.rs1;
if (data.is_imm) {
const imm = data.rs2_or_imm.imm;
switch (tag) {
.@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
else => unreachable,
}
} else {
const rs2 = data.rs2_or_imm.rs2;
switch (tag) {
.@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
else => unreachable,
}
}
}
fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op;
const rd = data.rd;
const rs1 = data.rs1;
if (data.is_imm) {
const imm = data.rs2_or_imm.imm;
switch (tag) {
.add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
.@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
.save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
.restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
.sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
else => unreachable,
}
} else {
const rs2 = data.rs2_or_imm.rs2;
switch (tag) {
.add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
.@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
.save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
.restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
.sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
else => unreachable,
}
}
}
fn mirNop(emit: *Emit) !void {
try emit.writeInstruction(Instruction.nop());
}
fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const data = emit.mir.instructions.items(.data)[inst].trap;
const cond = data.cond;
const ccr = data.ccr;
const rs1 = data.rs1;
if (data.is_imm) {
const imm = data.rs2_or_imm.imm;
switch (tag) {
.tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)),
else => unreachable,
}
} else {
const rs2 = data.rs2_or_imm.rs2;
switch (tag) {
.tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)),
else => unreachable,
}
}
}
// Common helper functions
fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
const delta_pc: usize = self.code.items.len - self.prev_di_pc;
@@ -131,52 +257,18 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
}
}
fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
_ = dbg_arg_info;
switch (tag) {
.dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
else => unreachable,
}
}
fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
switch (tag) {
.dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
else => unreachable,
}
}
fn mirDebugPrologueEnd(self: *Emit) !void {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
fn mirDebugEpilogueBegin(self: *Emit) !void {
switch (self.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
},
.plan9 => {},
.none => {},
}
}
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
@setCold(true);
assert(emit.err_msg == null);
emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
return error.EmitFail;
}
fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
// SPARCv9 instructions are always arranged in BE regardless of the
// endianness mode the CPU is running in.
// This is to ease porting in case someone wants to do a LE SPARCv9 backend.
const endian = Endian.Big;
std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
}

View File

@@ -167,7 +167,7 @@ pub const Inst = struct {
/// Branch with prediction.
/// Used by e.g. bpcc
branch_predict: struct {
annul: bool,
annul: bool = false,
pt: bool = true,
ccr: Instruction.CCR,
cond: Instruction.Condition,
@@ -215,10 +215,21 @@ pub const Inst = struct {
rs1: Register = .g0,
rs2_or_imm: union {
rs2: Register,
imm: u8,
imm: u7,
},
},
};
// Make sure we don't accidentally make instructions bigger than expected.
// Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
comptime {
if (builtin.mode != .Debug) {
// TODO clean up the definition of Data before enabling this.
// I'll do that after the PoC backend can produce usable binaries.
// assert(@sizeOf(Data) == 8);
}
}
};
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {

View File

@@ -546,6 +546,9 @@ pub const Instruction = union(enum) {
};
}
// SPARCv9 Instruction formats.
// See section 6.2 of the SPARCv9 ISA manual.
fn format1(disp: i32) Instruction {
const udisp = @bitCast(u32, disp);
@@ -561,7 +564,7 @@ pub const Instruction = union(enum) {
};
}
fn format2a(op2: u3, rd: Register, imm: u22) Instruction {
fn format2a(op2: u3, imm: u22, rd: Register) Instruction {
return Instruction{
.format_2a = .{
.rd = rd.enc(),
@@ -956,6 +959,74 @@ pub const Instruction = union(enum) {
},
};
}
// SPARCv9 Instruction definition.
// See appendix A of the SPARCv9 ISA manual.
pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch(s2) {
Register => format3a(0b10, 0b00_0000, rs1, rs2, rd),
i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd),
else => unreachable,
};
}
pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch(s2) {
Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
else => unreachable,
};
}
pub fn nop() Instruction {
return sethi(0, .g0);
}
pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction {
return switch(s2) {
Register => format3c(0b10, 0b11_1001, rs1, rs2),
i13 => format3d(0b10, 0b11_1001, rs1, rs2),
else => unreachable,
};
}
pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch(s2) {
Register => format3a(0b10, 0b11_1100, rs1, rs2, rd),
i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd),
else => unreachable,
};
}
pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch(s2) {
Register => format3a(0b10, 0b11_1101, rs1, rs2, rd),
i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd),
else => unreachable,
};
}
pub fn sethi(imm: u22, rd: Register) Instruction {
return format2a(0b100, imm, rd);
}
pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch(s2) {
Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd),
else => unreachable,
};
}
pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
// Tcc instructions abuse the rd field to store the conditionals.
return switch(s2) {
Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
u7 => format4e(0b00_0100, ccr, rs1, @intToEnum(Register, cond), rs2),
else => unreachable,
};
}
};
test "Serialize formats" {
@@ -973,7 +1044,7 @@ test "Serialize formats" {
.expected = 0b01_000000000000000000000000000001,
},
.{
.inst = Instruction.format2a(4, .g0, 0),
.inst = Instruction.format2a(4, 0, .g0),
.expected = 0b00_00000_100_0000000000000000000000,
},
.{