From cdefc6acbaf7f582c6689d1e003405819f797181 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Tue, 21 Jul 2020 21:33:53 -0400 Subject: SPU-II: Implement function calls --- src-self-hosted/codegen/spu-mk2.zig | 167 ++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src-self-hosted/codegen/spu-mk2.zig (limited to 'src-self-hosted/codegen') diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig new file mode 100644 index 0000000000..b44d4123bd --- /dev/null +++ b/src-self-hosted/codegen/spu-mk2.zig @@ -0,0 +1,167 @@ +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, +}; + +pub const Register = enum { + dummy, + + pub fn allocIndex(self: Register) ?u4 { + return null; + } +}; +pub const callee_preserved_regs = [_]Register{}; -- cgit v1.2.3 From f18636fa58905ec85d246cb14c18418f2ce74317 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Wed, 19 Aug 2020 12:48:10 -0400 Subject: SPU-II: Add common definitions --- lib/std/spu.zig | 3 + lib/std/spu/defines.zig | 158 +++++++++++++++++++++++++++++++++++ lib/std/std.zig | 3 + src-self-hosted/codegen/spu-mk2.zig | 160 +----------------------------------- 4 files changed, 166 insertions(+), 158 deletions(-) create mode 100644 lib/std/spu.zig create mode 100644 lib/std/spu/defines.zig (limited to 'src-self-hosted/codegen') diff --git a/lib/std/spu.zig b/lib/std/spu.zig new file mode 100644 index 0000000000..275ef698ea --- /dev/null +++ b/lib/std/spu.zig @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000000..0f7e6b03be --- /dev/null +++ b/lib/std/spu/defines.zig @@ -0,0 +1,158 @@ +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 2ff44f5e41..e6287ac5b7 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -80,6 +80,9 @@ 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/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig index b44d4123bd..0a95dce663 100644 --- a/src-self-hosted/codegen/spu-mk2.zig +++ b/src-self-hosted/codegen/spu-mk2.zig @@ -1,161 +1,4 @@ -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, -}; +pub usingnamespace @import("std").spu; pub const Register = enum { dummy, @@ -164,4 +7,5 @@ pub const Register = enum { return null; } }; + pub const callee_preserved_regs = [_]Register{}; -- cgit v1.2.3 From 54f3b0a560f17effbb582f86ebf83c53916fc697 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 22 Aug 2020 13:36:08 -0700 Subject: 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 --- lib/std/spu.zig | 3 - lib/std/spu/defines.zig | 158 ------------------ lib/std/spu/interpreter.zig | 163 ------------------- lib/std/std.zig | 3 - lib/std/target.zig | 3 +- src-self-hosted/codegen/spu-mk2.zig | 161 ++++++++++++++++++- src-self-hosted/codegen/spu-mk2/interpreter.zig | 166 +++++++++++++++++++ src-self-hosted/link.zig | 2 +- src-self-hosted/link/Elf.zig | 39 ++--- src-self-hosted/test.zig | 205 +++++++++++++----------- test/stage2/test.zig | 3 +- 11 files changed, 467 insertions(+), 439 deletions(-) delete mode 100644 lib/std/spu.zig delete mode 100644 lib/std/spu/defines.zig delete mode 100644 lib/std/spu/interpreter.zig create mode 100644 src-self-hosted/codegen/spu-mk2/interpreter.zig (limited to 'src-self-hosted/codegen') 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/spu/interpreter.zig b/lib/std/spu/interpreter.zig deleted file mode 100644 index d008955a93..0000000000 --- a/lib/std/spu/interpreter.zig +++ /dev/null @@ -1,163 +0,0 @@ -const std = @import("std"); -const log = std.log.scoped(.SPU_2_Interpreter); -usingnamespace @import("defines.zig"); - -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/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/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig new file mode 100644 index 0000000000..1ec99546c6 --- /dev/null +++ b/src-self-hosted/codegen/spu-mk2/interpreter.zig @@ -0,0 +1,166 @@ +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-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); } -- cgit v1.2.3