From 07606d12daabe8c201dba3d5b27e702ce58d0ffb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 15 May 2021 21:25:42 -0700 Subject: [PATCH] stage2: remove SPU Mark II backend As it stands, the backend is incomplete, and there is no active contributor, making it dead weight. However, anyone is free to resurrect this backend at any time. --- BRANCH_TODO | 2 - src/codegen.zig | 67 ----------- src/codegen/spu-mk2.zig | 170 ---------------------------- src/codegen/spu-mk2/interpreter.zig | 166 --------------------------- src/test.zig | 115 +------------------ test/stage2/spu-ii.zig | 23 ---- test/stage2/test.zig | 1 - 7 files changed, 1 insertion(+), 543 deletions(-) delete mode 100644 src/codegen/spu-mk2.zig delete mode 100644 src/codegen/spu-mk2/interpreter.zig delete mode 100644 test/stage2/spu-ii.zig diff --git a/BRANCH_TODO b/BRANCH_TODO index fcd3ecbec6e74d1efd854c6082c780432c50adb2..50a0246989719fde7fea872b21d3e0298ba0771f 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,5 +1,3 @@ - * get stage2 tests passing - - spu-ii test is saying "unimplemented" for some reason * modify stage2 tests so that only 1 uses _start and the rest use pub fn main * modify stage2 CBE tests so that only 1 uses pub export main and the diff --git a/src/codegen.zig b/src/codegen.zig index 6e37692093c26653908cb0e200d512899ff2a374..374361ebceaf23b51842c236a7ba8b9748336bee 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -117,7 +117,6 @@ pub fn generateSymbol( //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), //.s390x => return Function(.s390x).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), - .spu_2 => return Function(.spu_2).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), //.tce => return Function(.tce).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), //.tcele => return Function(.tcele).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), //.thumb => return Function(.thumb).generateSymbol(bin_file, src_loc, typed_value, code, debug_output), @@ -2204,11 +2203,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .riscv64 => { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32()); }, - .spu_2 => { - try self.code.resize(self.code.items.len + 2); - var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 }; - mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr)); - }, .arm, .armeb => { writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32()); }, @@ -2317,53 +2311,6 @@ 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.value()) |func_value| { - if (info.args.len != 0) { - return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{}); - } - if (func_value.castTag(.function)) |func_payload| { - const func = func_payload.data; - const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { - const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u16, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * 2); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| - @intCast(u16, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * 2) - else - unreachable; - - assert(func.owner_decl.has_tv); - const return_type = func.owner_decl.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 if (func_value.castTag(.extern_fn)) |_| { - return self.fail(inst.base.src, "TODO implement calling extern functions", .{}); - } 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", .{}); - } - }, .arm, .armeb => { for (info.args) |mc_arg, arg_i| { const arg = inst.args[arg_i]; @@ -3176,19 +3123,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!inst.is_volatile and inst.base.isUnused()) return MCValue.dead; switch (arch) { - .spu_2 => { - if (inst.inputs.len > 0 or inst.output_constraint != null) { - return self.fail(inst.base.src, "TODO implement inline asm inputs / outputs for SPU Mark II", .{}); - } - if (mem.eql(u8, inst.asm_source, "undefined0")) { - 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", .{}); - } - }, .arm, .armeb => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { @@ -4503,7 +4437,6 @@ 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"), .arm, .armeb => @import("codegen/arm.zig"), .aarch64, .aarch64_be, .aarch64_32 => @import("codegen/aarch64.zig"), else => struct { diff --git a/src/codegen/spu-mk2.zig b/src/codegen/spu-mk2.zig deleted file mode 100644 index 542862cacaa444033616d002c8fcfb2133fea1a0..0000000000000000000000000000000000000000 --- a/src/codegen/spu-mk2.zig +++ /dev/null @@ -1,170 +0,0 @@ -const std = @import("std"); - -pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter; - -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{}; diff --git a/src/codegen/spu-mk2/interpreter.zig b/src/codegen/spu-mk2/interpreter.zig deleted file mode 100644 index 1ec99546c6cbe1ee30d1473e455824d2f6dc9421..0000000000000000000000000000000000000000 --- a/src/codegen/spu-mk2/interpreter.zig +++ /dev/null @@ -1,166 +0,0 @@ -const std = @import("std"); -const log = std.log.scoped(.SPU_2_Interpreter); -const spu = @import("../spu-mk2.zig"); -const FlagRegister = spu.FlagRegister; -const Instruction = spu.Instruction; -const ExecutionCondition = spu.ExecutionCondition; - -pub fn Interpreter(comptime Bus: type) type { - return struct { - ip: u16 = 0, - sp: u16 = undefined, - bp: u16 = undefined, - fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)), - /// This is set to true when we hit an undefined0 instruction, allowing it to - /// be used as a trap for testing purposes - undefined0: bool = false, - /// This is set to true when we hit an undefined1 instruction, allowing it to - /// be used as a trap for testing purposes. undefined1 is used as a breakpoint. - undefined1: bool = false, - bus: Bus, - - pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void { - var count: usize = 0; - while (size == null or count < size.?) { - count += 1; - var instruction = @bitCast(Instruction, self.bus.read16(self.ip)); - - log.debug("Executing {}\n", .{instruction}); - - self.ip +%= 2; - - const execute = switch (instruction.condition) { - .always => true, - .not_zero => !self.fr.zero, - .when_zero => self.fr.zero, - .overflow => self.fr.carry, - ExecutionCondition.greater_or_equal_zero => !self.fr.negative, - else => return error.Unimplemented, - }; - - if (execute) { - const val0 = switch (instruction.input0) { - .zero => @as(u16, 0), - .immediate => i: { - const val = self.bus.read16(@intCast(u16, self.ip)); - self.ip +%= 2; - break :i val; - }, - else => |e| e: { - // peek or pop; show value at current SP, and if pop, increment sp - const val = self.bus.read16(self.sp); - if (e == .pop) { - self.sp +%= 2; - } - break :e val; - }, - }; - const val1 = switch (instruction.input1) { - .zero => @as(u16, 0), - .immediate => i: { - const val = self.bus.read16(@intCast(u16, self.ip)); - self.ip +%= 2; - break :i val; - }, - else => |e| e: { - // peek or pop; show value at current SP, and if pop, increment sp - const val = self.bus.read16(self.sp); - if (e == .pop) { - self.sp +%= 2; - } - break :e val; - }, - }; - - const output: u16 = switch (instruction.command) { - .get => self.bus.read16(self.bp +% (2 *% val0)), - .set => a: { - self.bus.write16(self.bp +% 2 *% val0, val1); - break :a val1; - }, - .load8 => self.bus.read8(val0), - .load16 => self.bus.read16(val0), - .store8 => a: { - const val = @truncate(u8, val1); - self.bus.write8(val0, val); - break :a val; - }, - .store16 => a: { - self.bus.write16(val0, val1); - break :a val1; - }, - .copy => val0, - .add => a: { - var val: u16 = undefined; - self.fr.carry = @addWithOverflow(u16, val0, val1, &val); - break :a val; - }, - .sub => a: { - var val: u16 = undefined; - self.fr.carry = @subWithOverflow(u16, val0, val1, &val); - break :a val; - }, - .spset => a: { - self.sp = val0; - break :a val0; - }, - .bpset => a: { - self.bp = val0; - break :a val0; - }, - .frset => a: { - const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1); - self.fr = @bitCast(FlagRegister, val); - break :a val; - }, - .bswap => (val0 >> 8) | (val0 << 8), - .bpget => self.bp, - .spget => self.sp, - .ipget => self.ip +% (2 *% val0), - .lsl => val0 << 1, - .lsr => val0 >> 1, - .@"and" => val0 & val1, - .@"or" => val0 | val1, - .xor => val0 ^ val1, - .not => ~val0, - .undefined0 => { - self.undefined0 = true; - // Break out of the loop, and let the caller decide what to do - return; - }, - .undefined1 => { - self.undefined1 = true; - // Break out of the loop, and let the caller decide what to do - return; - }, - .signext => if ((val0 & 0x80) != 0) - (val0 & 0xFF) | 0xFF00 - else - (val0 & 0xFF), - else => return error.Unimplemented, - }; - - switch (instruction.output) { - .discard => {}, - .push => { - self.sp -%= 2; - self.bus.write16(self.sp, output); - }, - .jump => { - self.ip = output; - }, - else => return error.Unimplemented, - } - if (instruction.modify_flags) { - self.fr.negative = (output & 0x8000) != 0; - self.fr.zero = (output == 0x0000); - } - } else { - if (instruction.input0 == .immediate) self.ip +%= 2; - if (instruction.input1 == .immediate) self.ip +%= 2; - break; - } - } - } - }; -} diff --git a/src/test.zig b/src/test.zig index d578c3aa22e2a1eebd9ac95fcdd2522fae55b823..8410aa398e7099231df2821f93d9e461ff79c3b9 100644 --- a/src/test.zig +++ b/src/test.zig @@ -862,10 +862,7 @@ pub const TestContext = struct { }); } else switch (case.target.getExternalExecutor()) { .native => try argv.append(exe_path), - .unavailable => { - try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name); - return; // Pass test. - }, + .unavailable => return, // Pass test. .qemu => |qemu_bin_name| if (enable_qemu) { // TODO Ability for test cases to specify whether to link libc. @@ -953,116 +950,6 @@ pub const TestContext = struct { } } - fn runInterpreterIfAvailable( - self: *TestContext, - gpa: *Allocator, - node: *std.Progress.Node, - case: Case, - tmp_dir: std.fs.Dir, - bin_name: []const u8, - ) !void { - const arch = case.target.cpu_arch orelse return; - switch (arch) { - .spu_2 => return self.runSpu2Interpreter(gpa, node, case, tmp_dir, bin_name), - else => return, - } - } - - fn runSpu2Interpreter( - self: *TestContext, - gpa: *Allocator, - update_node: *std.Progress.Node, - case: Case, - tmp_dir: std.fs.Dir, - bin_name: []const u8, - ) !void { - const spu = @import("codegen/spu-mk2.zig"); - if (case.target.os_tag) |os| { - if (os != .freestanding) { - std.debug.panic("Only freestanding makes sense for SPU-II tests!", .{}); - } - } else { - std.debug.panic("SPU_2 has no native OS, check the test!", .{}); - } - - var interpreter = spu.Interpreter(struct { - RAM: [0x10000]u8 = undefined, - - pub fn read8(bus: @This(), addr: u16) u8 { - return bus.RAM[addr]; - } - pub fn read16(bus: @This(), addr: u16) u16 { - return std.mem.readIntLittle(u16, bus.RAM[addr..][0..2]); - } - - pub fn write8(bus: *@This(), addr: u16, val: u8) void { - bus.RAM[addr] = val; - } - - pub fn write16(bus: *@This(), addr: u16, val: u16) void { - std.mem.writeIntLittle(u16, bus.RAM[addr..][0..2], val); - } - }){ - .bus = .{}, - }; - - { - var load_node = update_node.start("load", 0); - load_node.activate(); - defer load_node.end(); - - var file = try tmp_dir.openFile(bin_name, .{ .read = true }); - defer file.close(); - - const header = try std.elf.Header.read(&file); - var iterator = header.program_header_iterator(&file); - - var none_loaded = true; - - while (try iterator.next()) |phdr| { - if (phdr.p_type != std.elf.PT_LOAD) { - std.debug.print("Encountered unexpected ELF program header: type {}\n", .{phdr.p_type}); - return error.UnexpectedElfProgramHeader; - } - if (phdr.p_paddr != phdr.p_vaddr) { - std.debug.print("Physical address does not match virtual address in ELF header!\n", .{}); - return error.PhysicalAddressMismatchVirt; - } - if (phdr.p_filesz != phdr.p_memsz) { - std.debug.print("Physical size does not match virtual size in ELF header!\n", .{}); - return error.PhysicalSizeMismatchVirt; - } - if ((try file.pread(interpreter.bus.RAM[phdr.p_paddr .. phdr.p_paddr + phdr.p_filesz], phdr.p_offset)) != phdr.p_filesz) { - std.debug.print("Read less than expected from ELF file!", .{}); - return error.ElfFileEof; - } - std.log.scoped(.spu2_test).debug("Loaded 0x{x} bytes to 0x{x:0<4}\n", .{ phdr.p_filesz, phdr.p_paddr }); - none_loaded = false; - } - if (none_loaded) { - std.debug.print("No data found in ELF file!\n", .{}); - return error.EmptyElfFile; - } - } - - var exec_node = update_node.start("execute", 0); - exec_node.activate(); - defer exec_node.end(); - - var blocks: u16 = 1000; - const block_size = 1000; - while (!interpreter.undefined0) { - const pre_ip = interpreter.ip; - if (blocks > 0) { - blocks -= 1; - try interpreter.ExecuteBlock(block_size); - if (pre_ip == interpreter.ip) { - std.debug.print("Infinite loop detected in SPU II test!\n", .{}); - return error.InfiniteLoop; - } - } - } - } }; fn dumpArgs(argv: []const []const u8) void { diff --git a/test/stage2/spu-ii.zig b/test/stage2/spu-ii.zig deleted file mode 100644 index 11e6714b757a76eafcaed5d049167c5cc0d265af..0000000000000000000000000000000000000000 --- a/test/stage2/spu-ii.zig +++ /dev/null @@ -1,23 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const spu = std.zig.CrossTarget{ - .cpu_arch = .spu_2, - .os_tag = .freestanding, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exe("SPU-II Basic Test", spu); - case.addCompareOutput( - \\fn killEmulator() noreturn { - \\ asm volatile ("undefined0"); - \\ unreachable; - \\} - \\ - \\pub export fn _start() noreturn { - \\ killEmulator(); - \\} - , ""); - } -} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index dfe3e7cbdfb23d9fd55979a982883e58b7ced131..03bdf73475c93d9cc34d75232d46e470e356bb39 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -13,7 +13,6 @@ const linux_x64 = std.zig.CrossTarget{ pub fn addCases(ctx: *TestContext) !void { try @import("cbe.zig").addCases(ctx); - try @import("spu-ii.zig").addCases(ctx); try @import("arm.zig").addCases(ctx); try @import("aarch64.zig").addCases(ctx); try @import("llvm.zig").addCases(ctx); -- 2.54.0