mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
stage2: clean up SPU Mk II code
* move SPU code from std to self hosted compiler * change std lib comments to be descriptive rather than prescriptive * avoid usingnamespace * fix case style of error codes * remove duplication of producer_string * generalize handling of less than 64 bit arch pointers * clean up SPU II related test harness code
This commit is contained in:
parent
24efbf5ddf
commit
54f3b0a560
@ -1,3 +0,0 @@
|
||||
pub usingnamespace @import("spu/defines.zig");
|
||||
|
||||
pub const interpreter = @import("spu/interpreter.zig").Interpreter;
|
||||
@ -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,
|
||||
};
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user