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 fcd3ecbec6..50a0246989 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 6e37692093..374361ebce 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 542862caca..0000000000 --- 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 1ec99546c6..0000000000 --- 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 d578c3aa22..8410aa398e 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 11e6714b75..0000000000 --- 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 dfe3e7cbdf..03bdf73475 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);