Commit 07606d12da

Andrew Kelley <andrew@ziglang.org>
2021-05-16 06:25:42
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.
1 parent 7cd94d2
src/codegen/spu-mk2/interpreter.zig
@@ -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;
-                }
-            }
-        }
-    };
-}
src/codegen/spu-mk2.zig
@@ -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{};
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 {
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 {
test/stage2/spu-ii.zig
@@ -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();
-            \\}
-        , "");
-    }
-}
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);
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