From 7051970ad7e277d93c8bffee7221e3e8c45c8eab Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 14:29:06 +0700 Subject: [PATCH] stage2: sparcv9: implement basic instruction lowering --- src/arch/sparcv9/CodeGen.zig | 4 +- src/arch/sparcv9/Emit.zig | 194 ++++++++++++++++++++++++++--------- src/arch/sparcv9/Mir.zig | 15 ++- src/arch/sparcv9/bits.zig | 75 +++++++++++++- 4 files changed, 231 insertions(+), 57 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7ff1473921..193600804e 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -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.*; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 4cb789b942..7ff1aeb532 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -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); +} diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 3ff675fc36..02974dfda3 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -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 { diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 07cbf7fc91..952ddef4a9 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -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, }, .{