Merge pull request #11487 from koachan/sparc64-codegen
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 = {} };
|
||||
|
||||
@@ -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 => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user