From b6d7f63f34bbdb7c67abecbcd47389d2a3d28743 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 20 Apr 2022 21:16:17 +0700 Subject: [PATCH 01/12] stage2: sparcv9: Implement jmpl lowering --- src/arch/sparcv9/CodeGen.zig | 20 ++++++++++++++++++-- src/arch/sparcv9/Emit.zig | 5 +++-- src/arch/sparcv9/Mir.zig | 12 +----------- src/arch/sparcv9/bits.zig | 16 +++++++++++++--- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7d93916fc1..ffd1dfde86 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1,5 +1,7 @@ //! SPARCv9 codegen. //! This lowers AIR into MIR. +//! For now this only implements medium/low code model with absolute addressing. +//! TODO add support for other code models. const std = @import("std"); const assert = std.debug.assert; const log = std.log.scoped(.codegen); @@ -884,7 +886,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. _ = try self.addInst(.{ .tag = .jmpl, - .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = .o7, + .rs1 = .o7, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, }); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail("TODO implement calling extern functions", .{}); @@ -899,7 +908,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. _ = try self.addInst(.{ .tag = .jmpl, - .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = .o7, + .rs1 = .o7, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, }); } diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index b811a3567f..a136c622e7 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -56,8 +56,7 @@ pub fn emitMir( .call => @panic("TODO implement sparcv9 call"), - .jmpl => @panic("TODO implement sparcv9 jmpl"), - .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), + .jmpl => try emit.mirArithmetic3Op(inst), .ldub => try emit.mirArithmetic3Op(inst), .lduh => try emit.mirArithmetic3Op(inst), @@ -163,6 +162,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), @@ -177,6 +177,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index c79ebdcac1..21f59322cd 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -54,11 +54,8 @@ pub const Inst = struct { call, /// A.24 Jump and Link - /// jmpl (far direct jump) uses the branch_link field, - /// while jmpl_i (indirect jump) uses the branch_link_indirect field. - /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction. + /// It uses the arithmetic_3op field. jmpl, - jmpl_i, /// A.27 Load Integer /// Those uses the arithmetic_3op field. @@ -167,13 +164,6 @@ pub const Inst = struct { link: Register = .o7, }, - /// Indirect branch and link (always unconditional). - /// Used by e.g. jmpl_i - branch_link_indirect: struct { - reg: Register, - link: Register = .o7, - }, - /// Branch with prediction. /// Used by e.g. bpcc branch_predict: struct { diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index bc8b8822b7..2b50e68d32 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -271,6 +271,8 @@ pub const Instruction = union(enum) { rd: u5, op3: u6, rs1: u5, + // See Errata 58 of SPARCv9 specification + // https://sparc.org/errata-for-v9/#58 i: u1 = 0b1, reserved: u8 = 0b00000000, rs2: u5, @@ -977,10 +979,10 @@ pub const Instruction = union(enum) { }; } - pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + pub fn jmpl(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), + Register => format3a(0b10, 0b11_1000, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd), else => unreachable, }; } @@ -1017,6 +1019,14 @@ pub const Instruction = union(enum) { }; } + 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); } From a00d69ea4a8c6e21ca99daa10d3b324609f3ef24 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 04:52:50 +0700 Subject: [PATCH 02/12] stage2: sparcv9: Implement basic stack load/stores --- src/arch/sparcv9/CodeGen.zig | 74 +++++++++++++++++++++++++++++++++++- src/arch/sparcv9/Emit.zig | 13 +++++++ src/arch/sparcv9/Mir.zig | 10 +++++ src/arch/sparcv9/abi.zig | 5 +++ src/arch/sparcv9/bits.zig | 32 ++++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index ffd1dfde86..ca31297888 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1351,7 +1351,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const simm13 = math.cast(u12, off) catch + const biased_offset = off + abi.stack_bias; + const simm13 = math.cast(i13, biased_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, @@ -1381,11 +1382,80 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, - .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}), + .register => |reg| { + const biased_offset = stack_offset + abi.stack_bias; + const simm13 = math.cast(i13, biased_offset) catch + return self.fail("TODO larger stack offsets", .{}); + return self.genStore(reg, .sp, i13, simm13, abi_size); + }, .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), } } +fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { + assert(off_type == Register or off_type == i13); + + const is_imm = (off_type == i13); + const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; + + switch (abi_size) { + 1 => { + _ = try self.addInst(.{ + .tag = .stb, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .sth, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 4 => { + _ = try self.addInst(.{ + .tag = .stw, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 8 => { + _ = try self.addInst(.{ + .tag = .stx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index a136c622e7..91dcb4fd5f 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -76,6 +76,11 @@ pub fn emitMir( .sllx => @panic("TODO implement sparcv9 sllx"), + .stb => try emit.mirArithmetic3Op(inst), + .sth => try emit.mirArithmetic3Op(inst), + .stw => try emit.mirArithmetic3Op(inst), + .stx => try emit.mirArithmetic3Op(inst), + .sub => try emit.mirArithmetic3Op(inst), .tcc => try emit.mirTrap(inst), @@ -170,6 +175,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"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)), + .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)), + .sth => try emit.writeInstruction(Instruction.sth(i13, rs1, imm, rd)), + .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)), + .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)), .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), else => unreachable, } @@ -185,6 +194,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"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)), + .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)), + .sth => try emit.writeInstruction(Instruction.sth(Register, rs1, rs2, rd)), + .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)), + .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)), .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), else => unreachable, } diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 21f59322cd..54f147f415 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -94,6 +94,16 @@ pub const Inst = struct { // TODO add other operations. sllx, + /// 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.56 Subtract /// Those uses the arithmetic_3op field. // TODO add other operations. diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index 4cb10a99ea..ae72f270be 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,6 +1,11 @@ const bits = @import("bits.zig"); const Register = bits.Register; +// On SPARCv9, %sp points to top of stack + stack bias, +// and %fp points to top of previous frame + stack bias. +// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. +pub const stack_bias = 2047; + // There are no callee-preserved registers since the windowing // mechanism already takes care of them. // We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 2b50e68d32..e66b24f617 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -1059,6 +1059,38 @@ pub const Instruction = union(enum) { return format2a(0b100, imm, rd); } + pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0101, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sth(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0110, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0110, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn stw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0100, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn stx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_1110, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_1110, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), From e03ec51b4b47c6730462bed3f8d703767a7b33d9 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 05:29:15 +0700 Subject: [PATCH 03/12] stage2: sparcv9: Pad branch delay slots with nops --- src/arch/sparcv9/CodeGen.zig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index ca31297888..240b7e3a55 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -394,7 +394,11 @@ fn gen(self: *Self) !void { }, }); - // TODO Find a way to fill this slot + // Branches in SPARC have a delay slot, that is, the instruction + // following it will unconditionally be executed. + // See: Section 3.2.3 Control Transfer in SPARCv9 manual. + // See also: https://arcb.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/delaybra.html + // TODO Find a way to fill this delay slot // nop _ = try self.addInst(.{ .tag = .nop, @@ -895,6 +899,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }, }, }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail("TODO implement calling extern functions", .{}); } else { @@ -917,6 +927,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }, }, }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); } const result = info.return_value; From f6bf3dd78c7bbb139e64ddfa05995f3896366d77 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 06:02:53 +0700 Subject: [PATCH 04/12] stage2: sparcv9: Fix stack space accounting --- src/arch/sparcv9/CodeGen.zig | 37 ++++++++++++++++++++++++++++-------- src/arch/sparcv9/abi.zig | 9 ++++++++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 240b7e3a55..3715c56558 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -340,16 +340,15 @@ fn gen(self: *Self) !void { if (cc != .Naked) { // TODO Finish function prologue and epilogue for sparcv9. - // TODO Backpatch stack offset - // save %sp, -176, %sp - _ = try self.addInst(.{ + // save %sp, stack_save_area, %sp + const save_inst = try self.addInst(.{ .tag = .save, .data = .{ .arithmetic_3op = .{ .is_imm = true, .rd = .sp, .rs1 = .sp, - .rs2_or_imm = .{ .imm = -176 }, + .rs2_or_imm = .{ .imm = -abi.stack_save_area }, }, }, }); @@ -382,6 +381,28 @@ fn gen(self: *Self) !void { return self.fail("TODO add branches in sparcv9", .{}); } + // Backpatch stack offset + const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space; + const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); + if (math.cast(i13, stack_size)) |size| { + self.mir_instructions.set(save_inst, .{ + .tag = .save, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = .sp, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = -size }, + }, + }, + }); + } else |_| { + // TODO for large stacks, replace the prologue with: + // setx stack_size, %g1 + // save %sp, %g1, %sp + return self.fail("TODO SPARCv9: allow larger stacks", .{}); + } + // return %i7 + 8 _ = try self.addInst(.{ .tag = .@"return", @@ -1367,8 +1388,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const biased_offset = off + abi.stack_bias; - const simm13 = math.cast(i13, biased_offset) catch + const real_offset = off + abi.stack_bias + abi.stack_save_area; + const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, @@ -1399,8 +1420,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const biased_offset = stack_offset + abi.stack_bias; - const simm13 = math.cast(i13, biased_offset) catch + const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area; + const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index ae72f270be..a9001c7dc7 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,11 +1,18 @@ const bits = @import("bits.zig"); const Register = bits.Register; +// SPARCv9 stack constants. +// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. + // On SPARCv9, %sp points to top of stack + stack bias, // and %fp points to top of previous frame + stack bias. -// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. pub const stack_bias = 2047; +// The first 176 bytes of the stack is reserved for register saving purposes. +// SPARCv9 requires to reserve space in the stack for the first six arguments, +// even though they are usually passed in registers. +pub const stack_save_area = 176; + // There are no callee-preserved registers since the windowing // mechanism already takes care of them. // We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling From ae201807f52077709b46ab7d2adfe5178ce138af Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 06:06:58 +0700 Subject: [PATCH 05/12] stage2: sparcv9: Simplify genLoad/genStore --- src/arch/sparcv9/CodeGen.zig | 102 +++++++---------------------------- 1 file changed, 20 insertions(+), 82 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 3715c56558..b435b1885c 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1185,48 +1185,17 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; switch (abi_size) { - 1 => { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .ldub, + 2 => .lduh, + 4 => .lduw, + 8 => .ldx, + else => unreachable, // unexpected abi size + }; + _ = try self.addInst(.{ - .tag = .ldub, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .lduh, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .lduw, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .ldx, + .tag = tag, .data = .{ .arithmetic_3op = .{ .is_imm = is_imm, @@ -1436,48 +1405,17 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; switch (abi_size) { - 1 => { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .stb, + 2 => .sth, + 4 => .stw, + 8 => .stx, + else => unreachable, // unexpected abi size + }; + _ = try self.addInst(.{ - .tag = .stb, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .sth, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .stw, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .stx, + .tag = tag, .data = .{ .arithmetic_3op = .{ .is_imm = is_imm, From e76d52c74dbb7566af75baf7184b04f131d26d5f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 20:02:27 +0700 Subject: [PATCH 06/12] stage2: sparcv9: Remove dbg_arg instruction --- src/arch/sparcv9/CodeGen.zig | 12 ------------ src/arch/sparcv9/Emit.zig | 12 ------------ src/arch/sparcv9/Mir.zig | 15 +-------------- 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index b435b1885c..6176be73a3 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -780,16 +780,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { // TODO Copy registers to the stack const mcv = result; - _ = try self.addInst(.{ - .tag = .dbg_arg, - .data = .{ - .dbg_arg_info = .{ - .air_inst = inst, - .arg_index = arg_index, - }, - }, - }); - if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -1050,9 +1040,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; - try self.mir_instructions.ensureUnusedCapacity(gpa, 1); - const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); return result_index; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 91dcb4fd5f..aea25f2ffc 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -45,7 +45,6 @@ pub fn emitMir( for (mir_tags) |tag, index| { const inst = @intCast(u32, index); switch (tag) { - .dbg_arg => try emit.mirDbgArg(inst), .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), @@ -92,17 +91,6 @@ 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; diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 54f147f415..8916eafe80 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -28,8 +28,6 @@ pub const Inst = struct { data: Data, pub const Tag = enum(u16) { - /// Pseudo-instruction: Argument - dbg_arg, /// Pseudo-instruction: End of prologue dbg_prologue_end, /// Pseudo-instruction: Beginning of epilogue @@ -122,14 +120,6 @@ pub const Inst = struct { /// 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: argument - /// - /// Used by e.g. dbg_arg - dbg_arg_info: struct { - air_inst: Air.Inst.Index, - arg_index: usize, - }, - /// Debug info: line and column /// /// Used by e.g. dbg_line @@ -234,10 +224,7 @@ pub const Inst = struct { // 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); + assert(@sizeOf(Data) == 8); } } }; From c73eb00727e70e6d5224b371b5d0dc4ac6af4dd3 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 20:17:41 +0700 Subject: [PATCH 07/12] stage2: sparcv9: Add debug info generation for args --- src/arch/sparcv9/CodeGen.zig | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 6176be73a3..35d9a78dcd 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -780,6 +780,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { // TODO Copy registers to the stack const mcv = result; + try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); + if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -1038,6 +1040,26 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Common helper functions +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const mod = self.bin_file.options.module.?; + const atom = switch (self.bin_file.tag) { + .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + else => {}, + } +} + fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; try self.mir_instructions.ensureUnusedCapacity(gpa, 1); @@ -1166,6 +1188,40 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live self.finishAirBookkeeping(); } +fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void { + const ty = self.air.instructions.items(.data)[inst].ty; + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + + switch (mcv) { + .register => |reg| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + else => {}, + } + }, + .stack_offset => |offset| { + _ = offset; + switch (self.debug_output) { + .dwarf => {}, + else => {}, + } + }, + else => {}, + } +} + fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { assert(off_type == Register or off_type == i13); From e963d5be0b3dbe87f68014920cd1b93109318c32 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 21:08:50 +0700 Subject: [PATCH 08/12] stage2: sparcv9: Simplify debug info emission, remove unused formats --- src/arch/sparcv9/Emit.zig | 56 +++++++++++---------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index aea25f2ffc..4da2215c46 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -101,22 +101,22 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { } } -fn mirDebugPrologueEnd(self: *Emit) !void { - switch (self.debug_output) { +fn mirDebugPrologueEnd(emit: *Emit) !void { + switch (emit.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); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, } } -fn mirDebugEpilogueBegin(self: *Emit) !void { - switch (self.debug_output) { +fn mirDebugEpilogueBegin(emit: *Emit) !void { + switch (emit.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); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, @@ -232,10 +232,10 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { // 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; - switch (self.debug_output) { +fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { + const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); + const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; + switch (emit.debug_output) { .dwarf => |dbg_out| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to @@ -248,38 +248,12 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; } dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); - self.prev_di_pc = self.code.items.len; - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; + emit.prev_di_pc = emit.code.items.len; + emit.prev_di_line = line; + emit.prev_di_column = column; + emit.prev_di_pc = emit.code.items.len; }, - .plan9 => |dbg_out| { - if (delta_pc <= 0) return; // only do this when the pc changes - // we have already checked the target in the linker to make sure it is compatable - const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; - - // increasing the line number - try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); - // increasing the pc - const d_pc_p9 = @intCast(i64, delta_pc) - quant; - if (d_pc_p9 > 0) { - // minus one because if its the last one, we want to leave space to change the line which is one quanta - try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); - if (dbg_out.pcop_change_index.*) |pci| - dbg_out.dbg_line.items[pci] += 1; - dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); - } else if (d_pc_p9 == 0) { - // we don't need to do anything, because adding the quant does it for us - } else unreachable; - if (dbg_out.start_line.* == null) - dbg_out.start_line.* = self.prev_di_line; - dbg_out.end_line.* = line; - // only do this if the pc changed - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; - }, - .none => {}, + else => {}, } } From bc5b5df2c72d9b345b5cd242915aba2d2b975747 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 21:16:24 +0700 Subject: [PATCH 09/12] stage2: sparcv9: Update test case --- test/cases/sparcv9-linux/hello_world.zig | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/test/cases/sparcv9-linux/hello_world.zig b/test/cases/sparcv9-linux/hello_world.zig index 327a8c56ad..1aea9da7a3 100644 --- a/test/cases/sparcv9-linux/hello_world.zig +++ b/test/cases/sparcv9-linux/hello_world.zig @@ -1,23 +1,18 @@ const msg = "Hello, World!\n"; -pub export fn _start() noreturn { +fn length() usize { + return msg.len; +} + +pub fn main() void { asm volatile ("ta 0x6d" : : [number] "{g1}" (4), [arg1] "{o0}" (1), [arg2] "{o1}" (@ptrToInt(msg)), - [arg3] "{o2}" (msg.len), + [arg3] "{o2}" (length()), : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" ); - - asm volatile ("ta 0x6d" - : - : [number] "{g1}" (1), - [arg1] "{o0}" (0), - : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" - ); - - unreachable; } // run From 5a6f0d2e51d3d7fb3f88099611f0347d0d27e889 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 22 Apr 2022 21:30:56 +0700 Subject: [PATCH 10/12] stage2: sparcv9: Update Mir tag doc comments --- src/arch/sparcv9/Mir.zig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 8916eafe80..d2772116ee 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -39,24 +39,24 @@ pub const Inst = struct { // in The SPARC Architecture Manual, Version 9. /// A.2 Add - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. add, /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) - /// It uses the branch_predict field. + /// This uses the branch_predict field. bpcc, /// A.8 Call and Link - /// It uses the branch_link field. + /// This uses the branch_link field. call, /// A.24 Jump and Link - /// It uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. jmpl, /// A.27 Load Integer - /// Those uses the arithmetic_3op field. + /// 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. @@ -66,29 +66,29 @@ pub const Inst = struct { ldx, /// A.31 Logical Operations - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. @"or", /// A.40 No Operation - /// It uses the nop field. + /// This uses the nop field. nop, /// A.45 RETURN - /// It uses the arithmetic_2op field. + /// This Thisuses the arithmetic_2op field. @"return", /// A.46 SAVE and RESTORE - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. save, restore, /// A.48 SETHI - /// It uses the sethi field. + /// This uses the sethi field. sethi, /// A.49 Shift - /// Those uses the shift field. + /// This uses the shift field. // TODO add other operations. sllx, @@ -103,12 +103,12 @@ pub const Inst = struct { stx, /// A.56 Subtract - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. sub, /// A.61 Trap on Integer Condition Codes (Tcc) - /// It uses the trap field. + /// This uses the trap field. tcc, }; From 64927aa782bb2105012da508975c5b3d0d10f477 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 19:38:55 +0200 Subject: [PATCH 11/12] sparcv9: fix typo in def comment --- src/arch/sparcv9/Mir.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index d2772116ee..ef0be93f4c 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -75,7 +75,7 @@ pub const Inst = struct { nop, /// A.45 RETURN - /// This Thisuses the arithmetic_2op field. + /// This uses the arithmetic_2op field. @"return", /// A.46 SAVE and RESTORE From 9985699943edb4bf4f3dae9b57e0e2017c23c4bf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 20:00:13 +0200 Subject: [PATCH 12/12] libstd: map sparcv9 to qemu-sparc64 for test-runner --- lib/std/zig/system/NativeTargetInfo.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index f917ee8e34..9055d1c215 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -931,6 +931,7 @@ pub fn getExternalExecutor( .riscv64 => Executor{ .qemu = "qemu-riscv64" }, .s390x => Executor{ .qemu = "qemu-s390x" }, .sparc => Executor{ .qemu = "qemu-sparc" }, + .sparcv9 => Executor{ .qemu = "qemu-sparc64" }, .x86_64 => Executor{ .qemu = "qemu-x86_64" }, else => return bad_result, };