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, }; diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7d93916fc1..35d9a78dcd 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); @@ -338,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 }, }, }, }); @@ -380,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", @@ -392,7 +415,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, @@ -753,15 +780,7 @@ 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, - }, - }, - }); + try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -884,7 +903,20 @@ 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 }, + }, + }, + }); + + // 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", .{}); @@ -899,7 +931,20 @@ 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 }, + }, + }, + }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, }); } @@ -995,11 +1040,29 @@ 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); - const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); return result_index; @@ -1125,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); @@ -1132,48 +1229,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, @@ -1335,7 +1401,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 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.*)); }, @@ -1365,11 +1432,49 @@ 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 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); + }, .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, 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 = tag, + .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 b811a3567f..4da2215c46 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(), @@ -56,8 +55,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), @@ -77,6 +75,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), @@ -88,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; @@ -109,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 => {}, @@ -163,6 +155,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)), @@ -170,6 +163,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, } @@ -177,6 +174,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)), @@ -184,6 +182,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, } @@ -230,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 @@ -246,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 => {}, } } diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index c79ebdcac1..ef0be93f4c 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 @@ -41,27 +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 - /// 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. + /// This uses the arithmetic_3op field. jmpl, - jmpl_i, /// 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. @@ -71,39 +66,49 @@ 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 uses 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, + /// 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. + /// 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, }; @@ -115,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 @@ -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 { @@ -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); } } }; diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index 4cb10a99ea..a9001c7dc7 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,6 +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. +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 diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index bc8b8822b7..e66b24f617 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); } @@ -1049,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), 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