Commit cdefc6acba
Changed files (2)
src-self-hosted
codegen
src-self-hosted/codegen/spu-mk2.zig
@@ -0,0 +1,167 @@
+const std = @import("std");
+
+pub const ExecutionCondition = enum(u3) {
+ always = 0,
+ when_zero = 1,
+ not_zero = 2,
+ greater_zero = 3,
+ less_than_zero = 4,
+ greater_or_equal_zero = 5,
+ less_or_equal_zero = 6,
+ overflow = 7,
+};
+
+pub const InputBehaviour = enum(u2) {
+ zero = 0,
+ immediate = 1,
+ peek = 2,
+ pop = 3,
+};
+
+pub const OutputBehaviour = enum(u2) {
+ discard = 0,
+ push = 1,
+ jump = 2,
+ jump_relative = 3,
+};
+
+pub const Command = enum(u5) {
+ copy = 0,
+ ipget = 1,
+ get = 2,
+ set = 3,
+ store8 = 4,
+ store16 = 5,
+ load8 = 6,
+ load16 = 7,
+ undefined0 = 8,
+ undefined1 = 9,
+ frget = 10,
+ frset = 11,
+ bpget = 12,
+ bpset = 13,
+ spget = 14,
+ spset = 15,
+ add = 16,
+ sub = 17,
+ mul = 18,
+ div = 19,
+ mod = 20,
+ @"and" = 21,
+ @"or" = 22,
+ xor = 23,
+ not = 24,
+ signext = 25,
+ rol = 26,
+ ror = 27,
+ bswap = 28,
+ asr = 29,
+ lsl = 30,
+ lsr = 31,
+};
+
+pub const Instruction = packed struct {
+ condition: ExecutionCondition,
+ input0: InputBehaviour,
+ input1: InputBehaviour,
+ modify_flags: bool,
+ output: OutputBehaviour,
+ command: Command,
+ reserved: u1 = 0,
+
+ pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void {
+ try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)});
+ try out.writeAll(switch (instr.condition) {
+ .always => " ",
+ .when_zero => "== 0",
+ .not_zero => "!= 0",
+ .greater_zero => " > 0",
+ .less_than_zero => " < 0",
+ .greater_or_equal_zero => ">= 0",
+ .less_or_equal_zero => "<= 0",
+ .overflow => "ovfl",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.input0) {
+ .zero => "zero",
+ .immediate => "imm ",
+ .peek => "peek",
+ .pop => "pop ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.input1) {
+ .zero => "zero",
+ .immediate => "imm ",
+ .peek => "peek",
+ .pop => "pop ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.command) {
+ .copy => "copy ",
+ .ipget => "ipget ",
+ .get => "get ",
+ .set => "set ",
+ .store8 => "store8 ",
+ .store16 => "store16 ",
+ .load8 => "load8 ",
+ .load16 => "load16 ",
+ .undefined0 => "undefined",
+ .undefined1 => "undefined",
+ .frget => "frget ",
+ .frset => "frset ",
+ .bpget => "bpget ",
+ .bpset => "bpset ",
+ .spget => "spget ",
+ .spset => "spset ",
+ .add => "add ",
+ .sub => "sub ",
+ .mul => "mul ",
+ .div => "div ",
+ .mod => "mod ",
+ .@"and" => "and ",
+ .@"or" => "or ",
+ .xor => "xor ",
+ .not => "not ",
+ .signext => "signext ",
+ .rol => "rol ",
+ .ror => "ror ",
+ .bswap => "bswap ",
+ .asr => "asr ",
+ .lsl => "lsl ",
+ .lsr => "lsr ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(switch (instr.output) {
+ .discard => "discard",
+ .push => "push ",
+ .jump => "jmp ",
+ .jump_relative => "rjmp ",
+ });
+ try out.writeAll(" ");
+ try out.writeAll(if (instr.modify_flags)
+ "+ flags"
+ else
+ " ");
+ }
+};
+
+pub const FlagRegister = packed struct {
+ zero: bool,
+ negative: bool,
+ carry: bool,
+ carry_enabled: bool,
+ interrupt0_enabled: bool,
+ interrupt1_enabled: bool,
+ interrupt2_enabled: bool,
+ interrupt3_enabled: bool,
+ reserved: u8 = 0,
+};
+
+pub const Register = enum {
+ dummy,
+
+ pub fn allocIndex(self: Register) ?u4 {
+ return null;
+ }
+};
+pub const callee_preserved_regs = [_]Register{};
src-self-hosted/codegen.zig
@@ -102,6 +102,7 @@ pub fn generateSymbol(
//.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
//.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
//.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
+ .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
//.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
//.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
//.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
@@ -1349,6 +1350,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
}
},
+ .spu_2 => {
+ if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
+ if (info.args.len != 0) {
+ return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
+ }
+ if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
+ const func = func_val.func;
+ const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
+ const got_addr = @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2);
+ const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
+ // First, push the return address, then jump; if noreturn, don't bother with the first step
+ // TODO: implement packed struct -> u16 at comptime and move the bitcast here
+ var instr = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .jump, .command = .load16 };
+ if (return_type.zigTypeTag() == .NoReturn) {
+ try self.code.resize(self.code.items.len + 4);
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
+ return MCValue.unreach;
+ } else {
+ try self.code.resize(self.code.items.len + 8);
+ var push = Instruction{ .condition = .always, .input0 = .immediate, .input1 = .zero, .modify_flags = false, .output = .push, .command = .ipget };
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 8 ..][0..2], @bitCast(u16, push));
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 6 ..][0..2], @as(u16, 4));
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 4 ..][0..2], @bitCast(u16, instr));
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], got_addr);
+ switch (return_type.zigTypeTag()) {
+ .Void => return MCValue{ .none = {} },
+ .NoReturn => unreachable,
+ else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
+ }
+ }
+ } else {
+ return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
+ }
+ } else {
+ return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
+ }
+ },
else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
}
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
@@ -1647,19 +1686,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{});
}
if (mem.eql(u8, inst.asm_source, "undefined0")) {
- // Instructions are 16-bits, plus up to two sixteen bit immediates.
- // Upper three bits of first byte are the execution
- // condition; for now, only always (0b000) is supported.
- // Next, there are two two-bit sequences indicating inputs;
- // we only care to use zero (0b00).
- // The lowest bit of byte one indicates whether flags
- // should be updated; TODO: support that somehow.
- // In all, we use a zero byte for the first half of the
- // instruction.
- // The second byte is 0bOOCCCCCR; OO is output behavior (we
- // use zero, which discards the output), CCCCC is the
- // command (8 for undefined0), R is reserved.
- try self.code.appendSlice(&[_]u8{ 0x00, 0b00010000 });
+ try self.code.resize(self.code.items.len + 2);
+ var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined0 };
+ mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
return MCValue.none;
} else {
return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
@@ -1742,6 +1771,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
/// X => extension to the SIB.index field
/// B => extension to the MODRM.rm field or the SIB.base field
fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
+ std.debug.assert(arch == .x86_64);
// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
var value: u8 = 0x40;
if (arg.b) {
@@ -2289,7 +2319,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
result.stack_byte_count = next_stack_offset;
result.stack_align = 16;
},
- else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
+ else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
}
},
else => if (param_types.len != 0)
@@ -2336,6 +2366,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.i386 => @import("codegen/x86.zig"),
.x86_64 => @import("codegen/x86_64.zig"),
.riscv64 => @import("codegen/riscv64.zig"),
+ .spu_2 => @import("codegen/spu-mk2.zig"),
else => struct {
pub const Register = enum {
dummy,