stage2: sparcv9: implement basic instruction lowering
This commit is contained in:
@@ -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.*;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
.{
|
||||
|
||||
Reference in New Issue
Block a user