diff --git a/lib/std/spu.zig b/lib/std/spu.zig deleted file mode 100644 index 275ef698ea..0000000000 --- a/lib/std/spu.zig +++ /dev/null @@ -1,3 +0,0 @@ -pub usingnamespace @import("spu/defines.zig"); - -pub const interpreter = @import("spu/interpreter.zig").Interpreter; diff --git a/lib/std/spu/defines.zig b/lib/std/spu/defines.zig deleted file mode 100644 index 0f7e6b03be..0000000000 --- a/lib/std/spu/defines.zig +++ /dev/null @@ -1,158 +0,0 @@ -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, -}; diff --git a/lib/std/std.zig b/lib/std/std.zig index e6287ac5b7..2ff44f5e41 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -80,9 +80,6 @@ pub const valgrind = @import("valgrind.zig"); pub const zig = @import("zig.zig"); pub const start = @import("start.zig"); -// TODO move this -pub const spu = @import("spu.zig"); - // This forces the start.zig file to be imported, and the comptime logic inside that // file decides whether to export any appropriate start symbols. comptime { diff --git a/lib/std/target.zig b/lib/std/target.zig index fcd90357fb..080ac65f5c 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -663,7 +663,8 @@ pub const Target = struct { renderscript32, renderscript64, ve, - // Non-LLVM targets go here + // Stage1 currently assumes that architectures above this comment + // map one-to-one with the ZigLLVM_ArchType enum. spu_2, pub fn isARM(arch: Arch) bool { diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig index 0a95dce663..542862caca 100644 --- a/src-self-hosted/codegen/spu-mk2.zig +++ b/src-self-hosted/codegen/spu-mk2.zig @@ -1,4 +1,163 @@ -pub usingnamespace @import("std").spu; +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, diff --git a/lib/std/spu/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig similarity index 95% rename from lib/std/spu/interpreter.zig rename to src-self-hosted/codegen/spu-mk2/interpreter.zig index d008955a93..1ec99546c6 100644 --- a/lib/std/spu/interpreter.zig +++ b/src-self-hosted/codegen/spu-mk2/interpreter.zig @@ -1,6 +1,9 @@ const std = @import("std"); const log = std.log.scoped(.SPU_2_Interpreter); -usingnamespace @import("defines.zig"); +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 { @@ -32,7 +35,7 @@ pub fn Interpreter(comptime Bus: type) type { .when_zero => self.fr.zero, .overflow => self.fr.carry, ExecutionCondition.greater_or_equal_zero => !self.fr.negative, - else => return error.unimplemented, + else => return error.Unimplemented, }; if (execute) { @@ -134,7 +137,7 @@ pub fn Interpreter(comptime Bus: type) type { (val0 & 0xFF) | 0xFF00 else (val0 & 0xFF), - else => return error.unimplemented, + else => return error.Unimplemented, }; switch (instruction.output) { @@ -146,7 +149,7 @@ pub fn Interpreter(comptime Bus: type) type { .jump => { self.ip = output; }, - else => return error.unimplemented, + else => return error.Unimplemented, } if (instruction.modify_flags) { self.fr.negative = (output & 0x8000) != 0; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 59b01dc762..7a5680dfbf 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -7,7 +7,7 @@ const Package = @import("Package.zig"); const Type = @import("type.zig").Type; const build_options = @import("build_options"); -const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; +pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; pub const Options = struct { target: std.Target, diff --git a/src-self-hosted/link/Elf.zig b/src-self-hosted/link/Elf.zig index 53798053c2..eeb0289b05 100644 --- a/src-self-hosted/link/Elf.zig +++ b/src-self-hosted/link/Elf.zig @@ -14,12 +14,10 @@ const leb128 = std.debug.leb; const Package = @import("../Package.zig"); const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; -const build_options = @import("build_options"); const link = @import("../link.zig"); const File = link.File; const Elf = @This(); -const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; const default_entry_addr = 0x8000000; // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. @@ -249,8 +247,8 @@ fn openFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf { .allocator = allocator, }, .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 16, 32 => .p32, - 64 => .p64, + 0 ... 32 => .p32, + 33 ... 64 => .p64, else => return error.UnsupportedELFArchitecture, }, }; @@ -278,8 +276,8 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Elf .file = file, }, .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { - 16, 32 => .p32, - 64 => .p64, + 0 ... 32 => .p32, + 33 ... 64 => .p64, else => return error.UnsupportedELFArchitecture, }, .shdr_table_dirty = true, @@ -346,7 +344,7 @@ fn getDebugLineProgramEnd(self: Elf) u32 { /// Returns end pos of collision, if any. fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { - const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32; + const small_ptr = self.ptr_width == .p32; const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); if (start < ehdr_size) return ehdr_size; @@ -487,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. // we'll need to re-use that function anyway, in case the GOT grows and overlaps something // else in virtual memory. - const got_addr = if (self.base.options.target.cpu.arch.ptrBitWidth() == 16) @as(u32, 0x8000) else 0x4000000; + const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; try self.program_headers.append(self.base.allocator, .{ .p_type = elf.PT_LOAD, .p_offset = off, @@ -864,7 +862,7 @@ pub fn flush(self: *Elf, module: *Module) !void { // Write the form for the compile unit, which must match the abbrev table above. const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path); const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path); - const producer_strp = try self.makeDebugString(producer_string); + const producer_strp = try self.makeDebugString(link.producer_string); // Currently only one compilation unit is supported, so the address range is simply // identical to the main program header virtual address and memory size. const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; @@ -2152,29 +2150,28 @@ pub fn deleteExport(self: *Elf, exp: Export) void { fn writeProgHeader(self: *Elf, index: usize) !void { const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); const offset = self.program_headers.items[index].p_offset; - switch (self.base.options.target.cpu.arch.ptrBitWidth()) { - 32 => { + switch (self.ptr_width) { + .p32 => { var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; if (foreign_endian) { bswapAllFields(elf.Elf32_Phdr, &phdr[0]); } return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, - 64 => { + .p64 => { var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]}; if (foreign_endian) { bswapAllFields(elf.Elf64_Phdr, &phdr[0]); } return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset); }, - else => return error.UnsupportedArchitecture, } } fn writeSectHeader(self: *Elf, index: usize) !void { const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); - switch (self.base.options.target.cpu.arch.ptrBitWidth()) { - 32 => { + switch (self.ptr_width) { + .p32 => { var shdr: [1]elf.Elf32_Shdr = undefined; shdr[0] = sectHeaderTo32(self.sections.items[index]); if (foreign_endian) { @@ -2183,7 +2180,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr); return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, - 64 => { + .p64 => { var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; if (foreign_endian) { bswapAllFields(elf.Elf64_Shdr, &shdr[0]); @@ -2191,14 +2188,13 @@ fn writeSectHeader(self: *Elf, index: usize) !void { const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr); return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); }, - else => return error.UnsupportedArchitecture, } } fn writeOffsetTableEntry(self: *Elf, index: usize) !void { const shdr = &self.sections.items[self.got_section_index.?]; const phdr = &self.program_headers.items[self.phdr_got_index.?]; - const entry_size: u16 = self.base.options.target.cpu.arch.ptrBitWidth() / 8; + const entry_size: u16 = self.archPtrWidthBytes(); if (self.offset_table_count_dirty) { // TODO Also detect virtual address collisions. const allocated_size = self.allocatedSize(shdr.sh_offset); @@ -2351,6 +2347,7 @@ fn writeAllGlobalSymbols(self: *Elf) !void { } } +/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. fn ptrWidthBytes(self: Elf) u8 { return switch (self.ptr_width) { .p32 => 4, @@ -2358,6 +2355,12 @@ fn ptrWidthBytes(self: Elf) u8 { }; } +/// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes +/// in a 32-bit ELF file. +fn archPtrWidthBytes(self: Elf) u8 { + return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8); +} + /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. const dbg_line_vaddr_reloc_index = 3; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 3f02d78b9b..f9c9121817 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -571,95 +571,6 @@ pub const TestContext = struct { std.debug.assert(!case.cbe); update_node.estimated_total_items = 4; - if (case.target.cpu_arch) |arch| { - if (arch == .spu_2) { - 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 = std.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", null); - 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.readHeader(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}); - std.process.exit(1); - } - if (phdr.p_paddr != phdr.p_vaddr) { - std.debug.print("Physical address does not match virtual address in ELF header!\n", .{}); - std.process.exit(1); - } - if (phdr.p_filesz != phdr.p_memsz) { - std.debug.print("Physical size does not match virtual size in ELF header!\n", .{}); - std.process.exit(1); - } - 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!", .{}); - std.process.exit(1); - } - 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", .{}); - std.process.exit(1); - } - } - - var exec_node = update_node.start("execute", null); - 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", .{}); - std.process.exit(1); - } - } - } - } - } var exec_result = x: { var exec_node = update_node.start("execute", null); exec_node.activate(); @@ -672,7 +583,10 @@ pub const TestContext = struct { switch (case.target.getExternalExecutor()) { .native => try argv.append(exe_path), - .unavailable => return, // No executor available; pass test. + .unavailable => { + try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name); + return; // Pass test. + }, .qemu => |qemu_bin_name| if (enable_qemu) { // TODO Ability for test cases to specify whether to link libc. @@ -745,4 +659,115 @@ 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", null); + 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.readHeader(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}); + std.process.exit(1); + } + if (phdr.p_paddr != phdr.p_vaddr) { + std.debug.print("Physical address does not match virtual address in ELF header!\n", .{}); + std.process.exit(1); + } + if (phdr.p_filesz != phdr.p_memsz) { + std.debug.print("Physical size does not match virtual size in ELF header!\n", .{}); + std.process.exit(1); + } + 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!", .{}); + std.process.exit(1); + } + 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", .{}); + std.process.exit(1); + } + } + + var exec_node = update_node.start("execute", null); + 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", .{}); + std.process.exit(1); + } + } + } + } }; diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 832d9a44db..627cfc7fe0 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -26,6 +26,8 @@ const wasi = std.zig.CrossTarget{ pub fn addCases(ctx: *TestContext) !void { try @import("zir.zig").addCases(ctx); try @import("cbe.zig").addCases(ctx); + try @import("spu-ii.zig").addCases(ctx); + { var case = ctx.exe("hello world with updates", linux_x64); @@ -785,5 +787,4 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\extern var foo; , &[_][]const u8{":4:1: error: unable to infer variable type"}); - try @import("spu-ii.zig").addCases(ctx); }