From fdf19984b8af9781cd68dda3fcbcc44ac8ade80e Mon Sep 17 00:00:00 2001 From: Nathan Bourgeois Date: Sat, 21 Mar 2026 01:47:21 -0400 Subject: [PATCH 1/4] std.Target: add psp os --- lib/std/Target.zig | 20 ++++++++++++++++++-- lib/std/heap.zig | 10 ++++++++++ lib/std/start.zig | 2 +- src/Compilation.zig | 1 + src/codegen/llvm.zig | 1 + 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 1b0c617598..36c7c522fb 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -53,6 +53,7 @@ pub const Os = struct { ps3, ps4, ps5, + psp, vita, emscripten, @@ -194,6 +195,7 @@ pub const Os = struct { .@"3ds", + .psp, .vita, .wasi, @@ -612,6 +614,13 @@ pub const Os = struct { }, }, + .psp => .{ + .semver = .{ + .min = .{ .major = 1, .minor = 0, .patch = 0 }, + .max = .{ .major = 6, .minor = 61, .patch = 0 }, + }, + }, + .vita => .{ .semver = .{ // 1.3 is the first public release @@ -919,6 +928,7 @@ pub const Abi = enum { .tvos, .visionos, .watchos, + .psp, .ps3, .ps4, .ps5, @@ -2023,7 +2033,10 @@ pub const Cpu = struct { .lanai => &lanai.cpu.v11, // clang does not have a generic lanai model. .loongarch64 => &loongarch.cpu.la64v1_0, .m68k => &m68k.cpu.M68000, - .mips, .mipsel => &mips.cpu.mips32r2, + .mips, .mipsel => switch (os.tag) { + .psp => &mips.cpu.mips2, // mips2 with some custom instructions, no trap instructions + else => &mips.cpu.mips32r2, + }, .mips64, .mips64el => &mips.cpu.mips64r2, .msp430 => &msp430.cpu.msp430, .nvptx, .nvptx64 => &nvptx.cpu.sm_52, @@ -2204,6 +2217,7 @@ pub fn requiresLibC(target: *const Target) bool { .amdhsa, .ps4, .ps5, + .psp, .vita, .mesa3d, .contiki, @@ -2379,6 +2393,7 @@ pub const DynamicLinker = struct { .ps3, .ps4, .ps5, + .psp, .vita, => .none, }; @@ -2783,6 +2798,7 @@ pub const DynamicLinker = struct { .@"3ds", + .psp, .vita, .emscripten, @@ -3338,7 +3354,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 { .longlong, .ulonglong, .double => return 64, .longdouble => return 80, }, - .vita => switch (c_type) { + .psp, .vita => switch (c_type) { .char => return 8, .short, .ushort => return 16, .int, .uint, .float => return 32, diff --git a/lib/std/heap.zig b/lib/std/heap.zig index ef91461edf..9bf1aeffbb 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -805,6 +805,11 @@ const page_size_min_default: ?usize = switch (builtin.os.tag) { .x86, .x86_64 => 16 << 10, else => null, }, + .psp => switch (builtin.cpu.arch) { + // minimum block allocation by testing sceKernel + .mips, .mipsel => 1 << 8, // 256 + else => null, + }, // system/lib/libc/musl/arch/emscripten/bits/limits.h .emscripten => 64 << 10, .linux => switch (builtin.cpu.arch) { @@ -963,6 +968,11 @@ const page_size_max_default: ?usize = switch (builtin.os.tag) { .x86, .x86_64 => 16 << 10, else => null, }, + .psp => switch (builtin.cpu.arch) { + // minimum block allocation by testing sceKernel + .mips, .mipsel => 1 << 8, // 256 + else => null, + }, // system/lib/libc/musl/arch/emscripten/bits/limits.h .emscripten => 64 << 10, .linux => switch (builtin.cpu.arch) { diff --git a/lib/std/start.zig b/lib/std/start.zig index fd195af8a5..f6b4064ebd 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -65,7 +65,7 @@ comptime { // case it's not required to provide an entrypoint such as main. if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); } else switch (native_os) { - .other, .freestanding, .@"3ds", .vita => {}, + .other, .freestanding, .@"3ds", .psp, .vita => {}, else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), } } diff --git a/src/Compilation.zig b/src/Compilation.zig index 53a2874990..00ddd0001e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -6420,6 +6420,7 @@ fn addCommonCCArgs( .illumos => try argv.append("__illumos__"), // Homebrew targets without LLVM support; use communities's preferred macros. .@"3ds" => try argv.append("-D__3DS__"), + .psp => try argv.append("-D__PSP__"), .vita => try argv.append("-D__vita__"), else => {}, } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ec9d176fe5..8c359b9587 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -247,6 +247,7 @@ pub fn targetTriple(allocator: Allocator, target: *const std.Target) ![]const u8 .opengl, .other, .plan9, + .psp, .vita, => "unknown", }; From 2037dba90f5815f6fc3137c2485e908700c009e5 Mon Sep 17 00:00:00 2001 From: Nathan Bourgeois Date: Sat, 21 Mar 2026 03:23:11 -0400 Subject: [PATCH 2/4] std.posix: Minimal set to build an Io on PSP --- lib/std/Io/Dir.zig | 2 +- lib/std/posix.zig | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 3e38849f7f..948c12b66f 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -52,7 +52,7 @@ pub const max_path_bytes = switch (native_os) { /// On WASI, file name components are encoded as valid UTF-8. /// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. pub const max_name_bytes = switch (native_os) { - .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .illumos, .serenity => std.posix.NAME_MAX, + .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .illumos, .serenity, .psp => std.posix.NAME_MAX, // Haiku's NAME_MAX includes the null terminator, so subtract one. .haiku => std.posix.NAME_MAX - 1, // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 36e038c641..2b3c858fb3 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -38,6 +38,22 @@ pub const system = if (use_libc) else switch (native_os) { .linux => linux, .plan9 => std.os.plan9, + .psp => struct { + pub const fd_t = i32; + pub const pid_t = void; + pub const pollfd = void; + pub const uid_t = void; + pub const gid_t = void; + pub const mode_t = u32; + pub const nlink_t = u32; + pub const blksize_t = u32; + pub const ino_t = u64; + pub const IFNAMESIZE = {}; + pub const SIG = void; + + // https://github.com/pspdev/newlib/blob/9e0a073634ad73e8e088f2e071c55a9fe5d39709/newlib/libc/sys/psp/sys/dirent.h#L19 + pub const NAME_MAX = 255; + }, else => struct { pub const pid_t = void; pub const pollfd = void; From c824ce954ee81d438613ea7cf1e9a8111f730732 Mon Sep 17 00:00:00 2001 From: Nathan Bourgeois Date: Sat, 21 Mar 2026 19:39:04 -0400 Subject: [PATCH 3/4] misc: Add allegrex CPU & features, run tool, update semver --- lib/std/Target.zig | 6 ++++-- lib/std/Target/mips.zig | 15 +++++++++++++++ tools/update_cpu_features.zig | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 36c7c522fb..53cbd49e31 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -616,7 +616,9 @@ pub const Os = struct { .psp => .{ .semver = .{ - .min = .{ .major = 1, .minor = 0, .patch = 0 }, + // https://www.psdevwiki.com/psp/Official_Firmware_(OFW)#1.XX_Kernel + // It appears that the kernel started with semver for 1.XX before later changing to MAJ.MINPATCH in later releases (e.g. 3.60, 6.61) + .min = .{ .major = 1, .minor = 0, .patch = 3 }, .max = .{ .major = 6, .minor = 61, .patch = 0 }, }, }, @@ -2034,7 +2036,7 @@ pub const Cpu = struct { .loongarch64 => &loongarch.cpu.la64v1_0, .m68k => &m68k.cpu.M68000, .mips, .mipsel => switch (os.tag) { - .psp => &mips.cpu.mips2, // mips2 with some custom instructions, no trap instructions + .psp => &mips.cpu.allegrex, else => &mips.cpu.mips32r2, }, .mips64, .mips64el => &mips.cpu.mips64r2, diff --git a/lib/std/Target/mips.zig b/lib/std/Target/mips.zig index fb6bb1abab..2a1bedd713 100644 --- a/lib/std/Target/mips.zig +++ b/lib/std/Target/mips.zig @@ -49,6 +49,7 @@ pub const Feature = enum { noabicalls, nomadd4, nooddspreg, + notraps, p5600, ptr64, single_float, @@ -353,6 +354,11 @@ pub const all_features = blk: { .description = "Disable odd numbered single-precision registers", .dependencies = featureSet(&[_]Feature{}), }; + result[@intFromEnum(Feature.notraps)] = .{ + .llvm_name = null, + .description = "Disable trap instructions", + .dependencies = featureSet(&[_]Feature{}), + }; result[@intFromEnum(Feature.p5600)] = .{ .llvm_name = "p5600", .description = "The P5600 Processor", @@ -419,6 +425,15 @@ pub const all_features = blk: { }; pub const cpu = struct { + pub const allegrex: CpuModel = .{ + .name = "allegrex", + .llvm_name = null, + .features = featureSet(&[_]Feature{ + .mips2, + .notraps, + .single_float, + }), + }; pub const generic: CpuModel = .{ .name = "generic", .llvm_name = "generic", diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index eaa6a9afd2..636f45fa86 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -1299,6 +1299,20 @@ const targets = [_]ArchTarget{ .name = "Mips", .td_name = "Mips", }, + .extra_features = &.{ + .{ + .zig_name = "notraps", + .desc = "Disable trap instructions", + .deps = &.{}, + }, + }, + .extra_cpus = &.{ + .{ + .llvm_name = null, + .zig_name = "allegrex", + .features = &.{ "mips2", "single_float", "notraps" }, + }, + }, }, .{ .zig_name = "nvptx", From 164d01c1dc258068c23016ec2110e2b6080b8a2d Mon Sep 17 00:00:00 2001 From: Nathan Bourgeois Date: Sat, 21 Mar 2026 21:32:22 -0400 Subject: [PATCH 4/4] codegen: notraps on mips causes break and infinite loop on `@trap()` --- src/codegen/llvm.zig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8c359b9587..44e55b5de2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9537,7 +9537,25 @@ pub const FuncGen = struct { fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !void { _ = inst; - _ = try self.wip.callIntrinsic(.normal, .none, .trap, &.{}, &.{}, ""); + const target = self.ng.object.target; + if ((target.cpu.arch == .mips or target.cpu.arch == .mipsel) and + target.cpu.has(.mips, .notraps)) + { + // Emit a MIPS `break` instruction followed by an infinite loop (to fulfill the noreturn) + // since this CPU does not support trap instructions. + const o = self.ng.object; + _ = try self.wip.callAsm( + .none, + try o.builder.fnType(.void, &.{}, .normal), + .{ .sideeffect = true }, + try o.builder.string("break\n0:\nj 0b\nnop"), + try o.builder.string("~{memory}"), + &.{}, + "", + ); + } else { + _ = try self.wip.callIntrinsic(.normal, .none, .trap, &.{}, &.{}, ""); + } _ = try self.wip.@"unreachable"(); }