mirror of
https://github.com/ziglang/zig.git
synced 2026-02-17 06:49:23 +00:00
Merge pull request #11434 from koachan/sparc64-codegen
This commit is contained in:
commit
2635e4ca6e
@ -716,6 +716,9 @@ pub const CompilerBackend = enum(u64) {
|
||||
/// The reference implementation self-hosted compiler of Zig, using the
|
||||
/// riscv64 backend.
|
||||
stage2_riscv64 = 9,
|
||||
/// The reference implementation self-hosted compiler of Zig, using the
|
||||
/// sparcv9 backend.
|
||||
stage2_sparcv9 = 10,
|
||||
|
||||
_,
|
||||
};
|
||||
@ -761,7 +764,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
|
||||
builtin.zig_backend == .stage2_aarch64 or
|
||||
builtin.zig_backend == .stage2_x86_64 or
|
||||
builtin.zig_backend == .stage2_x86 or
|
||||
builtin.zig_backend == .stage2_riscv64)
|
||||
builtin.zig_backend == .stage2_riscv64 or
|
||||
builtin.zig_backend == .stage2_sparcv9)
|
||||
{
|
||||
while (true) {
|
||||
@breakpoint();
|
||||
|
||||
@ -29,6 +29,7 @@ comptime {
|
||||
builtin.zig_backend == .stage2_aarch64 or
|
||||
builtin.zig_backend == .stage2_arm or
|
||||
builtin.zig_backend == .stage2_riscv64 or
|
||||
builtin.zig_backend == .stage2_sparcv9 or
|
||||
(builtin.zig_backend == .stage2_llvm and native_os != .linux) or
|
||||
(builtin.zig_backend == .stage2_llvm and native_arch != .x86_64))
|
||||
{
|
||||
@ -165,6 +166,14 @@ fn exit2(code: usize) noreturn {
|
||||
: "rcx", "r11", "memory"
|
||||
);
|
||||
},
|
||||
.sparcv9 => {
|
||||
asm volatile ("ta 0x6d"
|
||||
:
|
||||
: [number] "{g1}" (1),
|
||||
[arg1] "{o0}" (code),
|
||||
: "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
|
||||
);
|
||||
},
|
||||
else => @compileError("TODO"),
|
||||
},
|
||||
// exits(0)
|
||||
|
||||
@ -4531,6 +4531,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
|
||||
.i386 => .stage2_x86,
|
||||
.aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
|
||||
.riscv64 => .stage2_riscv64,
|
||||
.sparcv9 => .stage2_sparcv9,
|
||||
else => .other,
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,298 @@
|
||||
//! This file contains the functionality for lowering SPARCv9 MIR into
|
||||
//! 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");
|
||||
const ErrorMsg = Module.ErrorMsg;
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
const DW = std.dwarf;
|
||||
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,
|
||||
debug_output: DebugInfoOutput,
|
||||
target: *const std.Target,
|
||||
err_msg: ?*ErrorMsg = null,
|
||||
src_loc: Module.SrcLoc,
|
||||
code: *std.ArrayList(u8),
|
||||
|
||||
prev_di_line: u32,
|
||||
prev_di_column: u32,
|
||||
/// Relative to the beginning of `code`.
|
||||
prev_di_pc: usize,
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
EmitFail,
|
||||
};
|
||||
|
||||
pub fn emitMir(
|
||||
emit: *Emit,
|
||||
) InnerError!void {
|
||||
const mir_tags = emit.mir.instructions.items(.tag);
|
||||
|
||||
// Emit machine code
|
||||
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(),
|
||||
|
||||
.add => try emit.mirArithmetic3Op(inst),
|
||||
|
||||
.bpcc => @panic("TODO implement sparcv9 bpcc"),
|
||||
|
||||
.call => @panic("TODO implement sparcv9 call"),
|
||||
|
||||
.jmpl => @panic("TODO implement sparcv9 jmpl"),
|
||||
.jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
|
||||
|
||||
.ldub => try emit.mirArithmetic3Op(inst),
|
||||
.lduh => try emit.mirArithmetic3Op(inst),
|
||||
.lduw => try emit.mirArithmetic3Op(inst),
|
||||
.ldx => try emit.mirArithmetic3Op(inst),
|
||||
|
||||
.@"or" => try emit.mirArithmetic3Op(inst),
|
||||
|
||||
.nop => try emit.mirNop(),
|
||||
|
||||
.@"return" => try emit.mirArithmetic2Op(inst),
|
||||
|
||||
.save => try emit.mirArithmetic3Op(inst),
|
||||
.restore => try emit.mirArithmetic3Op(inst),
|
||||
|
||||
.sethi => try emit.mirSethi(inst),
|
||||
|
||||
.sllx => @panic("TODO implement sparcv9 sllx"),
|
||||
|
||||
.sub => try emit.mirArithmetic3Op(inst),
|
||||
|
||||
.tcc => try emit.mirTrap(inst),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
.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)),
|
||||
.ldx => try emit.writeInstruction(Instruction.ldx(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)),
|
||||
.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)),
|
||||
.ldx => try emit.writeInstruction(Instruction.ldx(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 mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const data = emit.mir.instructions.items(.data)[inst].sethi;
|
||||
|
||||
const imm = data.imm;
|
||||
const rd = data.rd;
|
||||
|
||||
assert(tag == .sethi);
|
||||
try emit.writeInstruction(Instruction.sethi(imm, rd));
|
||||
}
|
||||
|
||||
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;
|
||||
switch (self.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
|
||||
// both the PC and the line number at the same time.
|
||||
try dbg_out.dbg_line.ensureUnusedCapacity(11);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
|
||||
leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
|
||||
if (delta_line != 0) {
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
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;
|
||||
},
|
||||
.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 => {},
|
||||
}
|
||||
}
|
||||
|
||||
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 (Section 3.1 of the ISA specification).
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -6,6 +6,264 @@
|
||||
//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
|
||||
//! so that, for example, the smaller encodings of jump instructions can be used.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Mir = @This();
|
||||
const bits = @import("bits.zig");
|
||||
const Air = @import("../../Air.zig");
|
||||
|
||||
const Instruction = bits.Instruction;
|
||||
const Register = bits.Register;
|
||||
|
||||
instructions: std.MultiArrayList(Inst).Slice,
|
||||
|
||||
/// The meaning of this data is determined by `Inst.Tag` value.
|
||||
extra: []const u32,
|
||||
|
||||
pub const Inst = struct {
|
||||
tag: Tag,
|
||||
/// The meaning of this depends on `tag`.
|
||||
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
|
||||
dbg_epilogue_begin,
|
||||
/// Pseudo-instruction: Update debug line
|
||||
dbg_line,
|
||||
|
||||
// All the real instructions are ordered by their section number
|
||||
// in The SPARC Architecture Manual, Version 9.
|
||||
|
||||
/// A.2 Add
|
||||
/// Those 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.
|
||||
bpcc,
|
||||
|
||||
/// A.8 Call and Link
|
||||
/// It 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.
|
||||
jmpl,
|
||||
jmpl_i,
|
||||
|
||||
/// A.27 Load Integer
|
||||
/// Those 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.
|
||||
ldub,
|
||||
lduh,
|
||||
lduw,
|
||||
ldx,
|
||||
|
||||
/// A.31 Logical Operations
|
||||
/// Those uses the arithmetic_3op field.
|
||||
// TODO add other operations.
|
||||
@"or",
|
||||
|
||||
/// A.40 No Operation
|
||||
/// It uses the nop field.
|
||||
nop,
|
||||
|
||||
/// A.45 RETURN
|
||||
/// It uses the arithmetic_2op field.
|
||||
@"return",
|
||||
|
||||
/// A.46 SAVE and RESTORE
|
||||
/// Those uses the arithmetic_3op field.
|
||||
save,
|
||||
restore,
|
||||
|
||||
/// A.48 SETHI
|
||||
/// It uses the sethi field.
|
||||
sethi,
|
||||
|
||||
/// A.49 Shift
|
||||
/// Those uses the shift field.
|
||||
// TODO add other operations.
|
||||
sllx,
|
||||
|
||||
/// A.56 Subtract
|
||||
/// Those uses the arithmetic_3op field.
|
||||
// TODO add other operations.
|
||||
sub,
|
||||
|
||||
/// A.61 Trap on Integer Condition Codes (Tcc)
|
||||
/// It uses the trap field.
|
||||
tcc,
|
||||
};
|
||||
|
||||
/// The position of an MIR instruction within the `Mir` instructions array.
|
||||
pub const Index = u32;
|
||||
|
||||
/// All instructions have a 8-byte payload, which is contained within
|
||||
/// this union. `Tag` determines which union field is active, as well as
|
||||
/// 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
|
||||
dbg_line_column: struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
},
|
||||
|
||||
/// Two operand arithmetic.
|
||||
/// if is_imm true then it uses the imm field of rs2_or_imm,
|
||||
/// otherwise it uses rs2 field.
|
||||
///
|
||||
/// Used by e.g. return
|
||||
arithmetic_2op: struct {
|
||||
is_imm: bool,
|
||||
rs1: Register,
|
||||
rs2_or_imm: union {
|
||||
rs2: Register,
|
||||
imm: i13,
|
||||
},
|
||||
},
|
||||
|
||||
/// Three operand arithmetic.
|
||||
/// if is_imm true then it uses the imm field of rs2_or_imm,
|
||||
/// otherwise it uses rs2 field.
|
||||
///
|
||||
/// Used by e.g. add, sub
|
||||
arithmetic_3op: struct {
|
||||
is_imm: bool,
|
||||
rd: Register,
|
||||
rs1: Register,
|
||||
rs2_or_imm: union {
|
||||
rs2: Register,
|
||||
imm: i13,
|
||||
},
|
||||
},
|
||||
|
||||
/// Branch and link (always unconditional).
|
||||
/// Used by e.g. call
|
||||
branch_link: struct {
|
||||
inst: Index,
|
||||
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 {
|
||||
annul: bool = false,
|
||||
pt: bool = true,
|
||||
ccr: Instruction.CCR,
|
||||
cond: Instruction.Condition,
|
||||
inst: Index,
|
||||
},
|
||||
|
||||
/// No additional data
|
||||
///
|
||||
/// Used by e.g. flushw
|
||||
nop: void,
|
||||
|
||||
/// SETHI operands.
|
||||
///
|
||||
/// Used by sethi
|
||||
sethi: struct {
|
||||
rd: Register,
|
||||
imm: u22,
|
||||
},
|
||||
|
||||
/// Shift operands.
|
||||
/// if is_imm true then it uses the imm field of rs2_or_imm,
|
||||
/// otherwise it uses rs2 field.
|
||||
///
|
||||
/// Used by e.g. add, sub
|
||||
shift: struct {
|
||||
is_imm: bool,
|
||||
width: Instruction.ShiftWidth,
|
||||
rd: Register,
|
||||
rs1: Register,
|
||||
rs2_or_imm: union {
|
||||
rs2: Register,
|
||||
imm: u6,
|
||||
},
|
||||
},
|
||||
|
||||
/// Trap.
|
||||
/// if is_imm true then it uses the imm field of rs2_or_imm,
|
||||
/// otherwise it uses rs2 field.
|
||||
///
|
||||
/// Used by e.g. tcc
|
||||
trap: struct {
|
||||
is_imm: bool = true,
|
||||
cond: Instruction.Condition,
|
||||
ccr: Instruction.CCR = .icc,
|
||||
rs1: Register = .g0,
|
||||
rs2_or_imm: union {
|
||||
rs2: Register,
|
||||
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 {
|
||||
mir.instructions.deinit(gpa);
|
||||
gpa.free(mir.extra);
|
||||
mir.* = undefined;
|
||||
}
|
||||
|
||||
/// Returns the requested data, as well as the new index which is at the start of the
|
||||
/// trailers for the object.
|
||||
pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
|
||||
const fields = std.meta.fields(T);
|
||||
var i: usize = index;
|
||||
var result: T = undefined;
|
||||
inline for (fields) |field| {
|
||||
@field(result, field.name) = switch (field.field_type) {
|
||||
u32 => mir.extra[i],
|
||||
i32 => @bitCast(i32, mir.extra[i]),
|
||||
else => @compileError("bad field type"),
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
return .{
|
||||
.data = result,
|
||||
.end = i,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,12 +1,25 @@
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
|
||||
// Register windowing mechanism will take care of preserving registers
|
||||
// so no need to do it manually
|
||||
pub const callee_preserved_regs = [_]Register{};
|
||||
// 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
|
||||
// something, though, as those are shared with the callee and might be
|
||||
// thrashed by it.
|
||||
pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 };
|
||||
|
||||
// Try to allocate i, l, o, then g sets of registers, in order of priority.
|
||||
pub const allocatable_regs = [_]Register{
|
||||
// zig fmt: off
|
||||
.@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5",
|
||||
.l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7,
|
||||
.o0, .o1, .o2, .o3, .o4, .o5,
|
||||
.g1, .g4, .g5,
|
||||
// zig fmt: on
|
||||
};
|
||||
|
||||
pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
|
||||
pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
|
||||
|
||||
pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
|
||||
pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
|
||||
pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
|
||||
pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
|
||||
|
||||
@ -164,27 +164,33 @@ pub const Instruction = union(enum) {
|
||||
// name them with letters since there's no official naming scheme.
|
||||
// TODO: need to rename the minor formats to a more descriptive name.
|
||||
|
||||
// I am using regular structs instead of packed ones to avoid
|
||||
// endianness-dependent behavior when constructing the actual
|
||||
// assembly instructions.
|
||||
// See also: https://github.com/ziglang/zig/issues/10113
|
||||
// TODO: change it back to packed structs once the issue is resolved.
|
||||
|
||||
// Format 1 (op = 1): CALL
|
||||
format_1: packed struct {
|
||||
format_1: struct {
|
||||
op: u2 = 0b01,
|
||||
disp30: u30,
|
||||
},
|
||||
|
||||
// Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)
|
||||
format_2a: packed struct {
|
||||
format_2a: struct {
|
||||
op: u2 = 0b00,
|
||||
rd: u5,
|
||||
op2: u3,
|
||||
imm22: u22,
|
||||
},
|
||||
format_2b: packed struct {
|
||||
format_2b: struct {
|
||||
op: u2 = 0b00,
|
||||
a: u1,
|
||||
cond: u4,
|
||||
op2: u3,
|
||||
disp22: u22,
|
||||
},
|
||||
format_2c: packed struct {
|
||||
format_2c: struct {
|
||||
op: u2 = 0b00,
|
||||
a: u1,
|
||||
cond: u4,
|
||||
@ -194,7 +200,7 @@ pub const Instruction = union(enum) {
|
||||
p: u1,
|
||||
disp19: u19,
|
||||
},
|
||||
format_2d: packed struct {
|
||||
format_2d: struct {
|
||||
op: u2 = 0b00,
|
||||
a: u1,
|
||||
fixed: u1 = 0b0,
|
||||
@ -207,7 +213,7 @@ pub const Instruction = union(enum) {
|
||||
},
|
||||
|
||||
// Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store
|
||||
format_3a: packed struct {
|
||||
format_3a: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -224,7 +230,7 @@ pub const Instruction = union(enum) {
|
||||
i: u1 = 0b1,
|
||||
simm13: u13,
|
||||
},
|
||||
format_3c: packed struct {
|
||||
format_3c: struct {
|
||||
op: u2,
|
||||
reserved1: u5 = 0b00000,
|
||||
op3: u6,
|
||||
@ -241,7 +247,7 @@ pub const Instruction = union(enum) {
|
||||
i: u1 = 0b1,
|
||||
simm13: u13,
|
||||
},
|
||||
format_3e: packed struct {
|
||||
format_3e: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -260,7 +266,7 @@ pub const Instruction = union(enum) {
|
||||
rcond: u3,
|
||||
simm10: u10,
|
||||
},
|
||||
format_3g: packed struct {
|
||||
format_3g: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -269,7 +275,7 @@ pub const Instruction = union(enum) {
|
||||
reserved: u8 = 0b00000000,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3h: packed struct {
|
||||
format_3h: struct {
|
||||
op: u2 = 0b10,
|
||||
fixed1: u5 = 0b00000,
|
||||
op3: u6 = 0b101000,
|
||||
@ -279,7 +285,7 @@ pub const Instruction = union(enum) {
|
||||
cmask: u3,
|
||||
mmask: u4,
|
||||
},
|
||||
format_3i: packed struct {
|
||||
format_3i: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -288,13 +294,13 @@ pub const Instruction = union(enum) {
|
||||
imm_asi: u8,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3j: packed struct {
|
||||
format_3j: struct {
|
||||
op: u2,
|
||||
impl_dep1: u5,
|
||||
op3: u6,
|
||||
impl_dep2: u19,
|
||||
},
|
||||
format_3k: packed struct {
|
||||
format_3k: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -304,7 +310,7 @@ pub const Instruction = union(enum) {
|
||||
reserved: u7 = 0b0000000,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3l: packed struct {
|
||||
format_3l: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -314,7 +320,7 @@ pub const Instruction = union(enum) {
|
||||
reserved: u7 = 0b0000000,
|
||||
shcnt32: u5,
|
||||
},
|
||||
format_3m: packed struct {
|
||||
format_3m: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -324,7 +330,7 @@ pub const Instruction = union(enum) {
|
||||
reserved: u6 = 0b000000,
|
||||
shcnt64: u6,
|
||||
},
|
||||
format_3n: packed struct {
|
||||
format_3n: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -332,7 +338,7 @@ pub const Instruction = union(enum) {
|
||||
opf: u9,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3o: packed struct {
|
||||
format_3o: struct {
|
||||
op: u2,
|
||||
fixed: u3 = 0b000,
|
||||
cc1: u1,
|
||||
@ -342,7 +348,7 @@ pub const Instruction = union(enum) {
|
||||
opf: u9,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3p: packed struct {
|
||||
format_3p: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -350,20 +356,20 @@ pub const Instruction = union(enum) {
|
||||
opf: u9,
|
||||
rs2: u5,
|
||||
},
|
||||
format_3q: packed struct {
|
||||
format_3q: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
rs1: u5,
|
||||
reserved: u14 = 0b00000000000000,
|
||||
},
|
||||
format_3r: packed struct {
|
||||
format_3r: struct {
|
||||
op: u2,
|
||||
fcn: u5,
|
||||
op3: u6,
|
||||
reserved: u19 = 0b0000000000000000000,
|
||||
},
|
||||
format_3s: packed struct {
|
||||
format_3s: struct {
|
||||
op: u2,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -371,7 +377,7 @@ pub const Instruction = union(enum) {
|
||||
},
|
||||
|
||||
//Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc
|
||||
format_4a: packed struct {
|
||||
format_4a: struct {
|
||||
op: u2 = 0b10,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -392,7 +398,7 @@ pub const Instruction = union(enum) {
|
||||
cc0: u1,
|
||||
simm11: u11,
|
||||
},
|
||||
format_4c: packed struct {
|
||||
format_4c: struct {
|
||||
op: u2 = 0b10,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -415,7 +421,7 @@ pub const Instruction = union(enum) {
|
||||
cc0: u1,
|
||||
simm11: u11,
|
||||
},
|
||||
format_4e: packed struct {
|
||||
format_4e: struct {
|
||||
op: u2 = 0b10,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -426,7 +432,7 @@ pub const Instruction = union(enum) {
|
||||
reserved: u4 = 0b0000,
|
||||
sw_trap: u7,
|
||||
},
|
||||
format_4f: packed struct {
|
||||
format_4f: struct {
|
||||
op: u2 = 0b10,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -436,7 +442,7 @@ pub const Instruction = union(enum) {
|
||||
opf_low: u5,
|
||||
rs2: u5,
|
||||
},
|
||||
format_4g: packed struct {
|
||||
format_4g: struct {
|
||||
op: u2 = 0b10,
|
||||
rd: u5,
|
||||
op3: u6,
|
||||
@ -512,40 +518,43 @@ pub const Instruction = union(enum) {
|
||||
pub fn toU32(self: Instruction) u32 {
|
||||
// TODO: Remove this once packed structs work.
|
||||
return switch (self) {
|
||||
.format_1 => |v| @bitCast(u32, v),
|
||||
.format_2a => |v| @bitCast(u32, v),
|
||||
.format_2b => |v| @bitCast(u32, v),
|
||||
.format_2c => |v| @bitCast(u32, v),
|
||||
.format_2d => |v| @bitCast(u32, v),
|
||||
.format_3a => |v| @bitCast(u32, v),
|
||||
.format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30),
|
||||
.format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22),
|
||||
.format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22),
|
||||
.format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19),
|
||||
.format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo),
|
||||
.format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
|
||||
.format_3c => |v| @bitCast(u32, v),
|
||||
.format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2),
|
||||
.format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
|
||||
.format_3e => |v| @bitCast(u32, v),
|
||||
.format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10),
|
||||
.format_3g => |v| @bitCast(u32, v),
|
||||
.format_3h => |v| @bitCast(u32, v),
|
||||
.format_3i => |v| @bitCast(u32, v),
|
||||
.format_3j => |v| @bitCast(u32, v),
|
||||
.format_3k => |v| @bitCast(u32, v),
|
||||
.format_3l => |v| @bitCast(u32, v),
|
||||
.format_3m => |v| @bitCast(u32, v),
|
||||
.format_3n => |v| @bitCast(u32, v),
|
||||
.format_3o => |v| @bitCast(u32, v),
|
||||
.format_3p => |v| @bitCast(u32, v),
|
||||
.format_3q => |v| @bitCast(u32, v),
|
||||
.format_3r => |v| @bitCast(u32, v),
|
||||
.format_3s => |v| @bitCast(u32, v),
|
||||
.format_4a => |v| @bitCast(u32, v),
|
||||
.format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask),
|
||||
.format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2),
|
||||
.format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2),
|
||||
.format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32),
|
||||
.format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64),
|
||||
.format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
|
||||
.format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
|
||||
.format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
|
||||
.format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved),
|
||||
.format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
|
||||
.format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
|
||||
.format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
|
||||
.format_4c => |v| @bitCast(u32, v),
|
||||
.format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
|
||||
.format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
|
||||
.format_4e => |v| @bitCast(u32, v),
|
||||
.format_4f => |v| @bitCast(u32, v),
|
||||
.format_4g => |v| @bitCast(u32, v),
|
||||
.format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap),
|
||||
.format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
|
||||
.format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
|
||||
};
|
||||
}
|
||||
|
||||
// SPARCv9 Instruction formats.
|
||||
// See section 6.2 of the SPARCv9 ISA manual.
|
||||
|
||||
fn format1(disp: i32) Instruction {
|
||||
const udisp = @bitCast(u32, disp);
|
||||
|
||||
@ -561,7 +570,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 +965,106 @@ 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 ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
|
||||
return switch (s2) {
|
||||
Register => format3a(0b11, 0b00_0001, rs1, rs2, rd),
|
||||
i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
|
||||
return switch (s2) {
|
||||
Register => format3a(0b11, 0b00_0010, rs1, rs2, rd),
|
||||
i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
|
||||
return switch (s2) {
|
||||
Register => format3a(0b11, 0b00_0000, rs1, rs2, rd),
|
||||
i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
|
||||
return switch (s2) {
|
||||
Register => format3a(0b11, 0b00_1011, rs1, rs2, rd),
|
||||
i13 => format3b(0b11, 0b00_1011, 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(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test "Serialize formats" {
|
||||
@ -973,7 +1082,7 @@ test "Serialize formats" {
|
||||
.expected = 0b01_000000000000000000000000000001,
|
||||
},
|
||||
.{
|
||||
.inst = Instruction.format2a(4, .g0, 0),
|
||||
.inst = Instruction.format2a(4, 0, .g0),
|
||||
.expected = 0b00_00000_100_0000000000000000000000,
|
||||
},
|
||||
.{
|
||||
@ -1096,6 +1205,10 @@ test "Serialize formats" {
|
||||
|
||||
for (testcases) |case| {
|
||||
const actual = case.inst.toU32();
|
||||
try testing.expectEqual(case.expected, actual);
|
||||
testing.expectEqual(case.expected, actual) catch |err| {
|
||||
std.debug.print("error: {x}\n", .{err});
|
||||
std.debug.print("case: {x}\n", .{case});
|
||||
return err;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ phdr_load_rw_index: ?u16 = null,
|
||||
phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
|
||||
|
||||
entry_addr: ?u64 = null,
|
||||
page_size: u16,
|
||||
page_size: u32,
|
||||
|
||||
shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
|
||||
shstrtab_index: ?u16 = null,
|
||||
@ -304,7 +304,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
|
||||
};
|
||||
const self = try gpa.create(Elf);
|
||||
errdefer gpa.destroy(self);
|
||||
const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB
|
||||
|
||||
const page_size: u32 = switch (options.target.cpu.arch) {
|
||||
.powerpc64le => 0x10000,
|
||||
.sparcv9 => 0x2000,
|
||||
else => 0x1000,
|
||||
};
|
||||
|
||||
var dwarf: ?Dwarf = if (!options.strip and options.module != null)
|
||||
Dwarf.init(gpa, .elf, options.target)
|
||||
@ -472,7 +477,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
|
||||
return min_pos - start;
|
||||
}
|
||||
|
||||
pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
|
||||
pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
|
||||
var start: u64 = 0;
|
||||
while (self.detectAllocCollision(start, object_size)) |item_end| {
|
||||
start = mem.alignForwardGeneric(u64, item_end, min_alignment);
|
||||
|
||||
@ -669,6 +669,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 {
|
||||
return switch (target.cpu.arch) {
|
||||
.arm, .armeb => 4,
|
||||
.aarch64, .aarch64_32, .aarch64_be => 4,
|
||||
.sparc, .sparcel, .sparcv9 => 4,
|
||||
.riscv64 => 2,
|
||||
else => 1,
|
||||
};
|
||||
|
||||
@ -16,6 +16,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
try @import("stage2/riscv64.zig").addCases(ctx);
|
||||
try @import("stage2/plan9.zig").addCases(ctx);
|
||||
try @import("stage2/x86_64.zig").addCases(ctx);
|
||||
try @import("stage2/sparcv9.zig").addCases(ctx);
|
||||
// https://github.com/ziglang/zig/issues/10968
|
||||
//try @import("stage2/nvptx.zig").addCases(ctx);
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
{
|
||||
var case = ctx.exe("hello world with updates", macos_aarch64);
|
||||
case.addError("", &[_][]const u8{
|
||||
":108:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
":109:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
});
|
||||
|
||||
// Incorrect return type
|
||||
|
||||
39
test/stage2/sparcv9.zig
Normal file
39
test/stage2/sparcv9.zig
Normal file
@ -0,0 +1,39 @@
|
||||
const std = @import("std");
|
||||
const TestContext = @import("../../src/test.zig").TestContext;
|
||||
|
||||
const linux_sparcv9 = std.zig.CrossTarget{
|
||||
.cpu_arch = .sparcv9,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
{
|
||||
var case = ctx.exe("sparcv9 hello world", linux_sparcv9);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\const msg = "Hello, World!\n";
|
||||
\\
|
||||
\\pub export fn _start() noreturn {
|
||||
\\ asm volatile ("ta 0x6d"
|
||||
\\ :
|
||||
\\ : [number] "{g1}" (4),
|
||||
\\ [arg1] "{o0}" (1),
|
||||
\\ [arg2] "{o1}" (@ptrToInt(msg)),
|
||||
\\ [arg3] "{o2}" (msg.len)
|
||||
\\ : "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;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1925,7 +1925,7 @@ fn addLinuxTestCases(ctx: *TestContext) !void {
|
||||
var case = ctx.exe("hello world with updates", linux_x64);
|
||||
|
||||
case.addError("", &[_][]const u8{
|
||||
":108:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
":109:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
});
|
||||
|
||||
// Incorrect return type
|
||||
@ -2176,7 +2176,7 @@ fn addMacOsTestCases(ctx: *TestContext) !void {
|
||||
{
|
||||
var case = ctx.exe("darwin hello world with updates", macos_x64);
|
||||
case.addError("", &[_][]const u8{
|
||||
":108:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
":109:9: error: struct 'tmp.tmp' has no member named 'main'",
|
||||
});
|
||||
|
||||
// Incorrect return type
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user