authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-27 21:18:14+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-27 21:18:14+02:00
logc6204ba3c2e83772e076c7c79095f4093dad0f94
tree2f1070b3f8d7577f02e9732692ff19c966e0a595
parentc3723c05f0349efb6f2b28655f886d58853a9d3a
parent521cc292e6ff43cffc0f54d9b1a5243ba0a766e6

Merge pull request 'spork8 backend' (#36627) from spork8 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36627

14 files changed, 1254 insertions(+), 166 deletions(-)

lib/std/Build/Configuration.zig+1
......@@ -2341,6 +2341,7 @@ pub const TargetQuery = struct {
23412341 sheb,
23422342 sparc,
23432343 sparc64,
2344 spork8,
23442345 spirv32,
23452346 spirv64,
23462347 thumb,
lib/std/Target.zig+170-143
......@@ -105,6 +105,7 @@ pub const Os = struct {
105105 .plan9 => arch.plan9Ext(),
106106 else => switch (arch) {
107107 .wasm32, .wasm64 => ".wasm",
108 .spork8 => ".bin",
108109 else => "",
109110 },
110111 };
......@@ -791,6 +792,7 @@ pub const s390x = @import("Target/s390x.zig");
791792pub const sh = @import("Target/generic.zig");
792793pub const sparc = @import("Target/sparc.zig");
793794pub const spirv = @import("Target/spirv.zig");
795pub const spork8 = @import("Target/generic.zig");
794796pub const ve = @import("Target/ve.zig");
795797pub const wasm = @import("Target/wasm.zig");
796798pub const x86 = @import("Target/x86.zig");
......@@ -1079,6 +1081,7 @@ pub const ObjectFormat = enum {
10791081 else => switch (arch) {
10801082 .spirv32, .spirv64 => .spirv,
10811083 .wasm32, .wasm64 => .wasm,
1084 .spork8 => .raw,
10821085 else => .elf,
10831086 },
10841087 };
......@@ -1128,6 +1131,7 @@ pub fn toElfMachine(target: *const Target) std.elf.EM {
11281131 .spirv64,
11291132 .wasm32,
11301133 .wasm64,
1134 .spork8,
11311135 => .NONE,
11321136 };
11331137}
......@@ -1195,6 +1199,7 @@ pub fn toCoffMachine(target: *const Target) std.coff.IMAGE.FILE.MACHINE {
11951199 .xcore,
11961200 .xtensa,
11971201 .xtensaeb,
1202 .spork8,
11981203 => .UNKNOWN,
11991204 };
12001205}
......@@ -1399,6 +1404,7 @@ pub const Cpu = struct {
13991404 sheb,
14001405 sparc,
14011406 sparc64,
1407 spork8,
14021408 spirv32,
14031409 spirv64,
14041410 thumb,
......@@ -1451,6 +1457,7 @@ pub const Cpu = struct {
14511457 xcore,
14521458 xtensa,
14531459 z80,
1460 spork8,
14541461 };
14551462
14561463 pub inline fn family(arch: Arch) Family {
......@@ -1489,6 +1496,7 @@ pub const Cpu = struct {
14891496 .x86_16, .x86, .x86_64 => .x86,
14901497 .xcore => .xcore,
14911498 .xtensa, .xtensaeb => .xtensa,
1499 .spork8 => .spork8,
14921500 };
14931501 }
14941502
......@@ -1753,6 +1761,7 @@ pub const Cpu = struct {
17531761 .sparc,
17541762 .sparc64,
17551763 .xtensaeb,
1764 .spork8,
17561765 => .big,
17571766
17581767 // GPU endianness is opaque. For now, assume little endian.
......@@ -2011,6 +2020,9 @@ pub const Cpu = struct {
20112020 .ez80_cet,
20122021 .ez80_tiflags,
20132022 => &.{.ez80},
2023
2024 .spork8,
2025 => &.{.spork8},
20142026 };
20152027 }
20162028 };
......@@ -2980,6 +2992,7 @@ pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 {
29802992 .avr,
29812993 .msp430,
29822994 .x86_16,
2995 .spork8,
29832996 => 16,
29842997
29852998 .ez80,
......@@ -3104,6 +3117,8 @@ pub fn stackAlignment(target: *const Target) u16 {
31043117
31053118 .kvx => return 32,
31063119
3120 .spork8 => return 256,
3121
31073122 else => {},
31083123 }
31093124
......@@ -3123,6 +3138,7 @@ pub fn stackGrowth(target: *const Target) StackGrowth {
31233138 return switch (target.cpu.arch) {
31243139 .hppa,
31253140 .hppa64,
3141 .spork8,
31263142 => .up,
31273143 else => .down,
31283144 };
......@@ -3210,7 +3226,7 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 {
32103226
32113227/// Returns `null` if no C ABI is defined for this target.
32123228pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
3213 switch (target.os.tag) {
3229 return switch (target.os.tag) {
32143230 .freestanding,
32153231 .other,
32163232 .ashetos,
......@@ -3218,49 +3234,56 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
32183234 .msp430,
32193235 .x86_16,
32203236 => switch (c_type) {
3221 .char => return 8,
3222 .short, .ushort, .int, .uint => return 16,
3223 .float, .long, .ulong => return 32,
3224 .longlong, .ulonglong, .double, .longdouble => return 64,
3237 .char => 8,
3238 .short, .ushort, .int, .uint => 16,
3239 .float, .long, .ulong => 32,
3240 .longlong, .ulonglong, .double, .longdouble => 64,
32253241 },
32263242 .avr => switch (c_type) {
3227 .char => return 8,
3228 .short, .ushort, .int, .uint => return 16,
3229 .long, .ulong, .float, .double, .longdouble => return 32,
3230 .longlong, .ulonglong => return 64,
3243 .char => 8,
3244 .short, .ushort, .int, .uint => 16,
3245 .long, .ulong, .float, .double, .longdouble => 32,
3246 .longlong, .ulonglong => 64,
3247 },
3248 // https://github.com/benanderman/spork-8/blob/main/Programming.md
3249 .spork8 => switch (c_type) {
3250 .char => 8,
3251 .short, .ushort, .int, .uint => 16,
3252 .long, .ulong, .float => 32,
3253 .double, .longdouble, .longlong, .ulonglong => 64,
32313254 },
32323255 .mips64,
32333256 .mips64el,
32343257 => switch (c_type) {
3235 .char => return 8,
3236 .short, .ushort => return 16,
3237 .int, .uint, .float => return 32,
3258 .char => 8,
3259 .short, .ushort => 16,
3260 .int, .uint, .float => 32,
32383261 .long, .ulong => switch (target.abi) {
3239 .abin32 => return 32,
3240 else => return 64,
3262 .abin32 => 32,
3263 else => 64,
32413264 },
3242 .longlong, .ulonglong, .double => return 64,
3243 .longdouble => return 128,
3265 .longlong, .ulonglong, .double => 64,
3266 .longdouble => 128,
32443267 },
32453268 .x86_64 => switch (c_type) {
3246 .char => return 8,
3247 .short, .ushort => return 16,
3248 .int, .uint, .float => return 32,
3269 .char => 8,
3270 .short, .ushort => 16,
3271 .int, .uint, .float => 32,
32493272 .long, .ulong => switch (target.abi) {
3250 .x32 => return 32,
3251 else => return 64,
3273 .x32 => 32,
3274 else => 64,
32523275 },
3253 .longlong, .ulonglong, .double => return 64,
3254 .longdouble => return 80,
3276 .longlong, .ulonglong, .double => 64,
3277 .longdouble => 80,
32553278 },
32563279 else => switch (c_type) {
3257 .char => return 8,
3258 .short, .ushort => return 16,
3259 .int, .uint, .float => return 32,
3260 .long, .ulong => return target.ptrBitWidth(),
3261 .longlong, .ulonglong, .double => return 64,
3280 .char => 8,
3281 .short, .ushort => 16,
3282 .int, .uint, .float => 32,
3283 .long, .ulong => target.ptrBitWidth(),
3284 .longlong, .ulonglong, .double => 64,
32623285 .longdouble => switch (target.cpu.arch) {
3263 .x86 => return 80,
3286 .x86 => 80,
32643287
32653288 .alpha,
32663289 .riscv32,
......@@ -3280,9 +3303,9 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
32803303 .loongarch32,
32813304 .loongarch64,
32823305 .ve,
3283 => return 128,
3306 => 128,
32843307
3285 else => return 64,
3308 else => 64,
32863309 },
32873310 },
32883311 },
......@@ -3309,60 +3332,60 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
33093332 .mips64,
33103333 .mips64el,
33113334 => switch (c_type) {
3312 .char => return 8,
3313 .short, .ushort => return 16,
3314 .int, .uint, .float => return 32,
3335 .char => 8,
3336 .short, .ushort => 16,
3337 .int, .uint, .float => 32,
33153338 .long, .ulong => switch (target.abi) {
3316 .gnuabin32, .muslabin32, .abin32 => return 32,
3317 else => return 64,
3339 .gnuabin32, .muslabin32, .abin32 => 32,
3340 else => 64,
33183341 },
3319 .longlong, .ulonglong, .double => return 64,
3320 .longdouble => return 128,
3342 .longlong, .ulonglong, .double => 64,
3343 .longdouble => 128,
33213344 },
33223345 .x86_64 => switch (c_type) {
3323 .char => return 8,
3324 .short, .ushort => return 16,
3325 .int, .uint, .float => return 32,
3346 .char => 8,
3347 .short, .ushort => 16,
3348 .int, .uint, .float => 32,
33263349 .long, .ulong => switch (target.abi) {
3327 .gnux32, .muslx32, .x32 => return 32,
3328 else => return 64,
3350 .gnux32, .muslx32, .x32 => 32,
3351 else => 64,
33293352 },
3330 .longlong, .ulonglong, .double => return 64,
3331 .longdouble => return 80,
3353 .longlong, .ulonglong, .double => 64,
3354 .longdouble => 80,
33323355 },
33333356 else => switch (c_type) {
3334 .char => return 8,
3335 .short, .ushort => return 16,
3336 .int, .uint, .float => return 32,
3337 .long, .ulong => return target.ptrBitWidth(),
3338 .longlong, .ulonglong, .double => return 64,
3357 .char => 8,
3358 .short, .ushort => 16,
3359 .int, .uint, .float => 32,
3360 .long, .ulong => target.ptrBitWidth(),
3361 .longlong, .ulonglong, .double => 64,
33393362 .longdouble => switch (target.cpu.arch) {
33403363 .x86 => switch (target.abi) {
3341 .android => return 64,
3342 else => return 80,
3364 .android => 64,
3365 else => 80,
33433366 },
33443367
33453368 .powerpc,
33463369 .powerpcle,
33473370 => switch (target.abi) {
3348 .musleabi, .musleabihf => return 64,
3371 .musleabi, .musleabihf => 64,
33493372 else => switch (target.os.tag) {
33503373 .netbsd,
33513374 .openbsd,
3352 => return 64,
3353 else => return 128,
3375 => 64,
3376 else => 128,
33543377 },
33553378 },
33563379
33573380 .powerpc64,
33583381 .powerpc64le,
33593382 => switch (target.abi) {
3360 .musl => return 64,
3383 .musl => 64,
33613384 else => switch (target.os.tag) {
33623385 .freebsd,
33633386 .openbsd,
3364 => return 64,
3365 else => return 128,
3387 => 64,
3388 else => 128,
33663389 },
33673390 },
33683391
......@@ -3382,43 +3405,43 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
33823405 .loongarch32,
33833406 .loongarch64,
33843407 .ve,
3385 => return 128,
3408 => 128,
33863409
3387 else => return 64,
3410 else => 64,
33883411 },
33893412 },
33903413 },
33913414
33923415 .windows, .uefi => switch (target.cpu.arch) {
33933416 .x86 => switch (c_type) {
3394 .char => return 8,
3395 .short, .ushort => return 16,
3396 .int, .uint, .float => return 32,
3397 .long, .ulong => return 32,
3398 .longlong, .ulonglong, .double => return 64,
3417 .char => 8,
3418 .short, .ushort => 16,
3419 .int, .uint, .float => 32,
3420 .long, .ulong => 32,
3421 .longlong, .ulonglong, .double => 64,
33993422 .longdouble => switch (target.abi) {
3400 .gnu => return 80,
3401 else => return 64,
3423 .gnu => 80,
3424 else => 64,
34023425 },
34033426 },
34043427 .x86_64 => switch (c_type) {
3405 .char => return 8,
3406 .short, .ushort => return 16,
3407 .int, .uint, .float => return 32,
3408 .long, .ulong => return 32,
3409 .longlong, .ulonglong, .double => return 64,
3428 .char => 8,
3429 .short, .ushort => 16,
3430 .int, .uint, .float => 32,
3431 .long, .ulong => 32,
3432 .longlong, .ulonglong, .double => 64,
34103433 .longdouble => switch (target.abi) {
3411 .gnu => return 80,
3412 else => return 64,
3434 .gnu => 80,
3435 else => 64,
34133436 },
34143437 },
34153438 else => switch (c_type) {
3416 .char => return 8,
3417 .short, .ushort => return 16,
3418 .int, .uint, .float => return 32,
3419 .long, .ulong => return 32,
3420 .longlong, .ulonglong, .double => return 64,
3421 .longdouble => return 64,
3439 .char => 8,
3440 .short, .ushort => 16,
3441 .int, .uint, .float => 32,
3442 .long, .ulong => 32,
3443 .longlong, .ulonglong, .double => 64,
3444 .longdouble => 64,
34223445 },
34233446 },
34243447
......@@ -3430,75 +3453,75 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
34303453 .visionos,
34313454 .watchos,
34323455 => switch (c_type) {
3433 .char => return 8,
3434 .short, .ushort => return 16,
3435 .int, .uint, .float => return 32,
3456 .char => 8,
3457 .short, .ushort => 16,
3458 .int, .uint, .float => 32,
34363459 .long, .ulong => switch (target.cpu.arch) {
3437 .x86_64 => return 64,
3460 .x86_64 => 64,
34383461 else => switch (target.abi) {
3439 .ilp32 => return 32,
3440 else => return 64,
3462 .ilp32 => 32,
3463 else => 64,
34413464 },
34423465 },
3443 .longlong, .ulonglong, .double => return 64,
3466 .longlong, .ulonglong, .double => 64,
34443467 .longdouble => switch (target.cpu.arch) {
3445 .x86_64 => return 80,
3446 else => return 64,
3468 .x86_64 => 80,
3469 else => 64,
34473470 },
34483471 },
34493472
34503473 .nvcl, .cuda => switch (c_type) {
3451 .char => return 8,
3452 .short, .ushort => return 16,
3453 .int, .uint, .float => return 32,
3474 .char => 8,
3475 .short, .ushort => 16,
3476 .int, .uint, .float => 32,
34543477 .long, .ulong => switch (target.cpu.arch) {
3455 .nvptx => return 32,
3456 .nvptx64 => return 64,
3457 else => return 64,
3478 .nvptx => 32,
3479 .nvptx64 => 64,
3480 else => 64,
34583481 },
3459 .longlong, .ulonglong, .double => return 64,
3460 .longdouble => return 64,
3482 .longlong, .ulonglong, .double => 64,
3483 .longdouble => 64,
34613484 },
34623485
34633486 .amdhsa, .amdpal, .mesa3d => switch (c_type) {
3464 .char => return 8,
3465 .short, .ushort => return 16,
3466 .int, .uint, .float => return 32,
3467 .long, .ulong, .longlong, .ulonglong, .double => return 64,
3468 .longdouble => return 128,
3487 .char => 8,
3488 .short, .ushort => 16,
3489 .int, .uint, .float => 32,
3490 .long, .ulong, .longlong, .ulonglong, .double => 64,
3491 .longdouble => 128,
34693492 },
34703493
34713494 .opencl, .vulkan => switch (c_type) {
3472 .char => return 8,
3473 .short, .ushort => return 16,
3474 .int, .uint, .float => return 32,
3475 .long, .ulong, .double => return 64,
3476 .longlong, .ulonglong => return 128,
3495 .char => 8,
3496 .short, .ushort => 16,
3497 .int, .uint, .float => 32,
3498 .long, .ulong, .double => 64,
3499 .longlong, .ulonglong => 128,
34773500 // Note: The OpenCL specification does not guarantee a particular size for long double,
34783501 // but clang uses 128 bits.
3479 .longdouble => return 128,
3502 .longdouble => 128,
34803503 },
34813504
34823505 .@"3ds" => switch (c_type) {
3483 .char => return 8,
3484 .short, .ushort => return 16,
3485 .int, .uint, .float, .long, .ulong => return 32,
3486 .longlong, .ulonglong, .double, .longdouble => return 64,
3506 .char => 8,
3507 .short, .ushort => 16,
3508 .int, .uint, .float, .long, .ulong => 32,
3509 .longlong, .ulonglong, .double, .longdouble => 64,
34873510 },
34883511
34893512 .wiiu => switch (c_type) {
3490 .char => return 8,
3491 .short, .ushort => return 16,
3492 .int, .uint, .float, .long, .ulong => return 32,
3493 .longlong, .ulonglong, .double, .longdouble => return 64,
3513 .char => 8,
3514 .short, .ushort => 16,
3515 .int, .uint, .float, .long, .ulong => 32,
3516 .longlong, .ulonglong, .double, .longdouble => 64,
34943517 },
34953518
34963519 .@"switch" => switch (c_type) {
3497 .char => return 8,
3498 .short, .ushort => return 16,
3499 .int, .uint, .float => return 32,
3500 .long, .ulong, .longlong, .ulonglong, .double => return 64,
3501 .longdouble => return 128,
3520 .char => 8,
3521 .short, .ushort => 16,
3522 .int, .uint, .float => 32,
3523 .long, .ulong, .longlong, .ulonglong, .double => 64,
3524 .longdouble => 128,
35023525 },
35033526
35043527 .gba => switch (c_type) {
......@@ -3509,41 +3532,41 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
35093532 },
35103533
35113534 .psx => switch (c_type) {
3512 .char => return 8,
3513 .short, .ushort => return 16,
3514 .int, .uint, .long, .ulong, .float => return 32,
3515 .longlong, .ulonglong, .double, .longdouble => return 64,
3535 .char => 8,
3536 .short, .ushort => 16,
3537 .int, .uint, .long, .ulong, .float => 32,
3538 .longlong, .ulonglong, .double, .longdouble => 64,
35163539 },
35173540 .ps4, .ps5 => switch (c_type) {
3518 .char => return 8,
3519 .short, .ushort => return 16,
3520 .int, .uint, .float => return 32,
3521 .long, .ulong => return 64,
3522 .longlong, .ulonglong, .double => return 64,
3523 .longdouble => return 80,
3541 .char => 8,
3542 .short, .ushort => 16,
3543 .int, .uint, .float => 32,
3544 .long, .ulong => 64,
3545 .longlong, .ulonglong, .double => 64,
3546 .longdouble => 80,
35243547 },
35253548 .psp, .vita => switch (c_type) {
3526 .char => return 8,
3527 .short, .ushort => return 16,
3528 .int, .uint, .float => return 32,
3529 .long, .ulong => return 64,
3530 .longlong, .ulonglong, .double, .longdouble => return 64,
3549 .char => 8,
3550 .short, .ushort => 16,
3551 .int, .uint, .float => 32,
3552 .long, .ulong => 64,
3553 .longlong, .ulonglong, .double, .longdouble => 64,
35313554 },
35323555 .tios => switch (c_type) {
3533 .char => return 8,
3534 .short, .ushort => return 16,
3535 .int, .uint => return 24,
3536 .long, .ulong, .float, .double => return 32,
3537 .longlong, .ulonglong, .longdouble => return 64,
3556 .char => 8,
3557 .short, .ushort => 16,
3558 .int, .uint => 24,
3559 .long, .ulong, .float, .double => 32,
3560 .longlong, .ulonglong, .longdouble => 64,
35383561 },
35393562
3540 .opengl => return null,
3563 .opengl => null,
35413564
35423565 .ps3,
35433566 .contiki,
35443567 .managarm,
35453568 => @panic("specify the C integer and float type sizes for this OS"),
3546 }
3569 };
35473570}
35483571
35493572/// Returns `null` if no C ABI is defined for this target.
......@@ -3552,6 +3575,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 {
35523575 switch (target.cpu.arch) {
35533576 .avr,
35543577 .ez80,
3578 .spork8,
35553579 => return 1,
35563580 .x86 => switch (target.os.tag) {
35573581 .windows, .uefi => switch (c_type) {
......@@ -3650,6 +3674,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 {
36503674
36513675 .avr,
36523676 .ez80,
3677 .spork8,
36533678 => unreachable, // Handled above.
36543679 }),
36553680 );
......@@ -3659,6 +3684,7 @@ pub fn cMaxIntAlignment(target: *const Target) u16 {
36593684 return switch (target.cpu.arch) {
36603685 .avr,
36613686 .ez80,
3687 .spork8,
36623688 => 1,
36633689
36643690 .msp430,
......@@ -3804,6 +3830,7 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention
38043830 .nvptx, .nvptx64 => .nvptx_device,
38053831 .spirv32, .spirv64 => .spirv_device,
38063832 .ez80 => .ez80_cet,
3833 .spork8 => .spork8,
38073834 };
38083835}
38093836
lib/std/debug.zig+2-2
......@@ -75,8 +75,8 @@ pub fn TargetInfo(os: std.Target.Os.Tag, arch: std.Target.Cpu.Arch) type {
7575 else => @import("debug/SelfInfo/Elf.zig"),
7676 },
7777 .macho => @import("debug/SelfInfo/MachO.zig"),
78 .plan9, .spirv, .wasm => void,
79 .c, .hex, .raw => unreachable,
78 .plan9, .spirv, .wasm, .raw, .hex => void,
79 .c => unreachable,
8080 };
8181}
8282
lib/std/lang.zig+7
......@@ -391,6 +391,10 @@ pub const CallingConvention = union(enum(u8)) {
391391 ez80_cet,
392392 ez80_tiflags,
393393
394 // Calling convention used by
395 // [snake2p example program](https://github.com/benanderman/spork-8/blob/1bce10a2c3a3888a3f4ca8208112afbc5973fda4/Code/programs/snake2p.asm)
396 spork8,
397
394398 /// Options shared across most calling conventions.
395399 pub const CommonOptions = struct {
396400 /// The boundary the stack is aligned to when the function is called.
......@@ -1319,6 +1323,9 @@ pub const CompilerBackend = enum(u64) {
13191323 /// The reference implementation self-hosted compiler of Zig, using the
13201324 /// loongarch backend.
13211325 stage2_loongarch = 13,
1326 /// The Zig Software Foundation self-hosted implementation of Zig. Backend
1327 /// originally contributed by Ben Anderman in 2026.
1328 zsf_spork8 = 14,
13221329
13231330 _,
13241331};
lib/std/zig/llvm/Builder.zig+2
......@@ -162,6 +162,7 @@ pub fn tripleForTarget(allocator: Allocator, target: *const std.Target) ![]const
162162 .propeller,
163163 .sh,
164164 .sheb,
165 .spork8,
165166 .x86_16,
166167 .xtensaeb,
167168 => unreachable, // Gated by hasLlvmSupport().
......@@ -577,6 +578,7 @@ pub const DataLayout = struct {
577578 .sheb,
578579 .x86_16,
579580 .xtensaeb,
581 .spork8,
580582 => unreachable,
581583 };
582584 }
src/Zcu.zig+5
......@@ -4042,6 +4042,7 @@ pub fn atomicPtrAlignment(
40424042 const target = zcu.getTarget();
40434043 const max_atomic_bits: u16 = switch (target.cpu.arch) {
40444044 .ez80,
4045 .spork8,
40454046 => 8,
40464047
40474048 .aarch64,
......@@ -4697,6 +4698,10 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum)
46974698 .loongarch64_lp64, .loongarch32_ilp32, .naked => true,
46984699 else => false,
46994700 },
4701 .zsf_spork8 => switch (cc) {
4702 .spork8, .naked => true,
4703 else => false,
4704 },
47004705 };
47014706 if (!backend_ok) return .{ .bad_backend = backend };
47024707 return .ok;
src/codegen.zig+7
......@@ -38,6 +38,7 @@ fn devFeatureForBackend(backend: std.lang.CompilerBackend) dev.Feature {
3838 .stage2_powerpc => unreachable,
3939 .stage2_riscv64 => .riscv64_backend,
4040 .stage2_sparc64 => .sparc64_backend,
41 .zsf_spork8 => .spork8_backend,
4142 .stage2_spirv => .spirv_backend,
4243 .stage2_wasm => .wasm_backend,
4344 .stage2_x86 => .x86_backend,
......@@ -58,6 +59,7 @@ fn importBackend(comptime backend: std.lang.CompilerBackend) type {
5859 .stage2_riscv64 => @import("codegen/riscv64/CodeGen.zig"),
5960 .stage2_sparc64 => @import("codegen/sparc64/CodeGen.zig"),
6061 .stage2_spirv => @import("codegen/spirv/CodeGen.zig"),
62 .zsf_spork8 => @import("codegen/spork8/CodeGen.zig"),
6163 .stage2_wasm => @import("codegen/wasm/CodeGen.zig"),
6264 .stage2_x86, .stage2_x86_64 => @import("codegen/x86_64/CodeGen.zig"),
6365 _ => unreachable,
......@@ -78,6 +80,7 @@ pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*co
7880 .stage2_x86,
7981 .stage2_riscv64,
8082 .stage2_sparc64,
83 .zsf_spork8,
8184 .stage2_spirv,
8285 => |backend| {
8386 dev.check(devFeatureForBackend(backend));
......@@ -107,6 +110,7 @@ pub const AnyMir = union {
107110 wasm: if (dev.env.supports(.wasm_backend)) @import("codegen/wasm/Mir.zig") else noreturn,
108111 c: if (dev.env.supports(.c_backend)) @import("codegen/c.zig").Mir else noreturn,
109112 spirv: if (dev.env.supports(.spirv_backend)) @import("codegen/spirv/Mir.zig") else noreturn,
113 spork8: if (dev.env.supports(.spork8_backend)) @import("codegen/spork8/Mir.zig") else noreturn,
110114
111115 pub inline fn tag(comptime backend: std.lang.CompilerBackend) []const u8 {
112116 return switch (backend) {
......@@ -118,6 +122,7 @@ pub const AnyMir = union {
118122 .stage2_wasm => "wasm",
119123 .stage2_c => "c",
120124 .stage2_spirv => "spirv",
125 .zsf_spork8 => "spork8",
121126 else => unreachable,
122127 };
123128 }
......@@ -135,6 +140,7 @@ pub const AnyMir = union {
135140 .stage2_wasm,
136141 .stage2_c,
137142 .stage2_spirv,
143 .zsf_spork8,
138144 => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa),
139145 }
140146 }
......@@ -164,6 +170,7 @@ pub fn generateFunction(
164170 .stage2_x86_64,
165171 .stage2_wasm,
166172 .stage2_c,
173 .zsf_spork8,
167174 .stage2_spirv,
168175 => |backend| {
169176 dev.check(devFeatureForBackend(backend));
src/codegen/llvm.zig+2
......@@ -4393,6 +4393,7 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const
43934393 .spirv_vertex,
43944394 .spirv_task,
43954395 .spirv_mesh,
4396 .spork8,
43964397 => null,
43974398 };
43984399}
......@@ -4720,6 +4721,7 @@ pub fn initializeLLVMTarget(io: Io, arch: std.Target.Cpu.Arch) void {
47204721 .propeller,
47214722 .sh,
47224723 .sheb,
4724 .spork8,
47234725 .x86_16,
47244726 .xtensaeb,
47254727 => unreachable,
src/codegen/spork8/CodeGen.zig created+676
......@@ -0,0 +1,676 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5
6const CodeGen = @This();
7const link = @import("../../link.zig");
8const Spork8 = link.File.Spork8;
9const Zcu = @import("../../Zcu.zig");
10const InternPool = @import("../../InternPool.zig");
11const Air = @import("../../Air.zig");
12const Liveness = Air.Liveness;
13const Mir = @import("Mir.zig");
14
15air: Air,
16liveness: Liveness,
17gpa: Allocator,
18pt: Zcu.PerThread,
19owner_nav: InternPool.Nav.Index,
20func_index: InternPool.Index,
21mir_instructions: std.MultiArrayList(Mir.Inst),
22/// Contains extra data for MIR
23mir_extra: std.ArrayListUnmanaged(u32),
24
25pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
26 return comptime &.initMany(&.{
27 .expand_bit_cast_safe,
28 .expand_int_cast_safe,
29 .expand_int_from_float_safe,
30 .expand_int_from_float_optimized_safe,
31 .expand_add_safe,
32 .expand_sub_safe,
33 .expand_mul_safe,
34
35 .expand_packed_load,
36 .expand_packed_store,
37 .expand_packed_agg_field_val,
38 .expand_packed_aggregate_init,
39 .expand_array_to_vector,
40
41 .scalarize_add,
42 .scalarize_add_optimized,
43 .scalarize_add_wrap,
44 .scalarize_add_sat,
45 .scalarize_sub,
46 .scalarize_sub_optimized,
47 .scalarize_sub_wrap,
48 .scalarize_sub_sat,
49 .scalarize_mul,
50 .scalarize_mul_optimized,
51 .scalarize_mul_wrap,
52 .scalarize_mul_sat,
53 .scalarize_div_float,
54 .scalarize_div_float_optimized,
55 .scalarize_div_trunc,
56 .scalarize_div_trunc_optimized,
57 .scalarize_div_floor,
58 .scalarize_div_floor_optimized,
59 .scalarize_div_ceil,
60 .scalarize_div_ceil_optimized,
61 .scalarize_div_exact,
62 .scalarize_div_exact_optimized,
63 .scalarize_rem,
64 .scalarize_rem_optimized,
65 .scalarize_mod,
66 .scalarize_mod_optimized,
67 .scalarize_max,
68 .scalarize_min,
69 .scalarize_add_with_overflow,
70 .scalarize_sub_with_overflow,
71 .scalarize_mul_with_overflow,
72 .scalarize_shl_with_overflow,
73 .scalarize_bit_and,
74 .scalarize_bit_or,
75 .scalarize_shr,
76 .scalarize_shr_exact,
77 .scalarize_shl,
78 .scalarize_shl_exact,
79 .scalarize_shl_sat,
80 .scalarize_xor,
81 .scalarize_not,
82 .scalarize_clz,
83 .scalarize_ctz,
84 .scalarize_popcount,
85 .scalarize_byte_swap,
86 .scalarize_bit_reverse,
87 .scalarize_sqrt,
88 .scalarize_sin,
89 .scalarize_cos,
90 .scalarize_tan,
91 .scalarize_exp,
92 .scalarize_exp2,
93 .scalarize_log,
94 .scalarize_log2,
95 .scalarize_log10,
96 .scalarize_abs,
97 .scalarize_floor,
98 .scalarize_ceil,
99 .scalarize_round,
100 .scalarize_trunc_float,
101 .scalarize_neg,
102 .scalarize_neg_optimized,
103 .scalarize_cmp_vector,
104 .scalarize_cmp_vector_optimized,
105 .scalarize_fptrunc,
106 .scalarize_fpext,
107 .scalarize_int_cast,
108 .scalarize_ptr_cast,
109 .scalarize_ptr_from_int,
110 .scalarize_int_from_ptr,
111 .scalarize_trunc,
112 .scalarize_int_from_float,
113 .scalarize_int_from_float_optimized,
114 .scalarize_float_from_int,
115 .scalarize_reduce,
116 .scalarize_reduce_optimized,
117 .scalarize_shuffle_one,
118 .scalarize_shuffle_two,
119 .scalarize_select,
120 .scalarize_mul_add,
121
122 .scalarize_bit_cast_padded_elems,
123 });
124}
125
126pub fn generate(
127 bin_file: *link.File,
128 pt: Zcu.PerThread,
129 func_index: InternPool.Index,
130 air: *const Air,
131 liveness: *const ?Air.Liveness,
132) link.Error!Mir {
133 _ = bin_file;
134 const zcu = pt.zcu;
135 const gpa = zcu.gpa;
136 const cg = zcu.funcInfo(func_index);
137
138 var code_gen: CodeGen = .{
139 .gpa = gpa,
140 .pt = pt,
141 .air = air.*,
142 .liveness = liveness.*.?,
143 .owner_nav = cg.owner_nav,
144 .func_index = func_index,
145 .mir_instructions = .empty,
146 .mir_extra = .empty,
147 };
148 defer code_gen.deinit();
149
150 return generateInner(&code_gen) catch |err| switch (err) {
151 error.AlreadyReported,
152 error.OutOfMemory,
153 => |e| return e,
154 };
155}
156
157pub fn deinit(cg: *CodeGen) void {
158 cg.* = undefined;
159}
160
161const InnerError = error{
162 AlreadyReported,
163 OutOfMemory,
164};
165
166fn generateInner(cg: *CodeGen) InnerError!Mir {
167 // Generate MIR for function body
168 try cg.genBody(cg.air.getMainBody());
169
170 try cg.mir_extra.shrinkToLen(cg.gpa);
171
172 return .{
173 .instructions = cg.mir_instructions.toOwnedSlice(),
174 .extra = cg.mir_extra.toOwnedSliceAssert(),
175 };
176}
177
178fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
179 const zcu = cg.pt.zcu;
180 const ip = &zcu.intern_pool;
181
182 for (body) |inst| {
183 if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) continue;
184 try cg.genInst(inst);
185 }
186}
187
188fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
189 const air_tags = cg.air.instructions.items(.tag);
190 return switch (air_tags[@backingInt(inst)]) {
191 .inferred_alloc, .inferred_alloc_comptime => unreachable,
192
193 .add,
194 .add_sat,
195 .add_wrap,
196 .sub,
197 .sub_sat,
198 .sub_wrap,
199 .mul,
200 .mul_sat,
201 .mul_wrap,
202 .div_float,
203 .div_exact,
204 .div_trunc,
205 .div_floor,
206 .bit_and,
207 .bit_or,
208 .rem,
209 .mod,
210 .shl,
211 .shl_exact,
212 .shl_sat,
213 .shr,
214 .shr_exact,
215 .xor,
216 .max,
217 .min,
218 .mul_add,
219
220 .sqrt,
221 .sin,
222 .cos,
223 .tan,
224 .exp,
225 .exp2,
226 .log,
227 .log2,
228 .log10,
229 .floor,
230 .ceil,
231 .round,
232 .trunc_float,
233 .neg,
234
235 .abs,
236
237 .add_with_overflow,
238 .sub_with_overflow,
239 .shl_with_overflow,
240 .mul_with_overflow,
241
242 .clz,
243 .ctz,
244
245 .cmp_eq,
246 .cmp_gte,
247 .cmp_gt,
248 .cmp_lte,
249 .cmp_lt,
250 .cmp_neq,
251
252 .cmp_vector,
253
254 .array_elem_val,
255 .array_to_slice,
256 .alloc,
257 .arg,
258 .block,
259 .breakpoint,
260 .br,
261 .repeat,
262 .switch_dispatch,
263 .cond_br,
264 .fptrunc,
265 .fpext,
266 .int_from_float,
267 .float_from_int,
268 .get_union_tag,
269
270 .@"try",
271 .try_cold,
272 .try_ptr,
273 .try_ptr_cold,
274
275 .dbg_stmt,
276 .dbg_empty_stmt,
277 .dbg_inline_block,
278 .dbg_var_ptr,
279 .dbg_var_val,
280 .dbg_arg_inline,
281
282 .call,
283 .call_always_tail,
284 .call_never_tail,
285 .call_never_inline,
286
287 .is_err,
288 .is_non_err,
289
290 .is_null,
291 .is_non_null,
292 .is_null_ptr,
293 .is_non_null_ptr,
294
295 .load,
296 .loop,
297 .memset,
298 .memset_safe,
299 .not,
300 .optional_payload,
301 .optional_payload_ptr,
302 .optional_payload_ptr_set,
303 .ptr_add,
304 .ptr_sub,
305 .ptr_elem_ptr,
306 .ptr_elem_val,
307 .ret,
308 .ret_safe,
309 .ret_ptr,
310 .ret_load,
311 .splat,
312 .select,
313 .reduce,
314 .aggregate_init,
315 .union_init,
316 .prefetch,
317 .popcount,
318 .byte_swap,
319 .bit_reverse,
320
321 .slice,
322 .slice_len,
323 .slice_elem_val,
324 .slice_elem_ptr,
325 .slice_ptr,
326 .ptr_slice_len_ptr,
327 .ptr_slice_ptr_ptr,
328 .store,
329 .store_safe,
330
331 .set_union_tag,
332 .struct_field_ptr,
333 .struct_field_ptr_index_0,
334 .struct_field_ptr_index_1,
335 .struct_field_ptr_index_2,
336 .struct_field_ptr_index_3,
337 .field_parent_ptr,
338
339 .switch_br,
340 .loop_switch_br,
341 .trunc,
342
343 .wrap_optional,
344 .unwrap_errunion_payload,
345 .unwrap_errunion_payload_ptr,
346 .unwrap_errunion_err,
347 .unwrap_errunion_err_ptr,
348 .wrap_errunion_payload,
349 .wrap_errunion_err,
350 .errunion_payload_ptr_set,
351 .error_name,
352
353 .wasm_memory_size,
354 .wasm_memory_grow,
355
356 .memcpy,
357
358 .ret_addr,
359 .tag_name,
360
361 .error_set_has_value,
362 .frame_addr,
363
364 .is_err_ptr,
365 .is_non_err_ptr,
366
367 .err_return_trace,
368 .set_err_return_trace,
369 .save_err_return_trace_index,
370 .is_named_enum_value,
371 .addrspace_cast,
372 .c_va_arg,
373 .c_va_copy,
374 .c_va_end,
375 .c_va_start,
376 .memmove,
377
378 .atomic_load,
379 .atomic_store_unordered,
380 .atomic_store_monotonic,
381 .atomic_store_release,
382 .atomic_store_seq_cst,
383 .atomic_rmw,
384 .cmpxchg_weak,
385 .cmpxchg_strong,
386
387 .add_optimized,
388 .sub_optimized,
389 .mul_optimized,
390 .div_float_optimized,
391 .div_trunc_optimized,
392 .div_floor_optimized,
393 .div_exact_optimized,
394 .rem_optimized,
395 .mod_optimized,
396 .neg_optimized,
397 .cmp_lt_optimized,
398 .cmp_lte_optimized,
399 .cmp_eq_optimized,
400 .cmp_gte_optimized,
401 .cmp_gt_optimized,
402 .cmp_neq_optimized,
403 .cmp_vector_optimized,
404 .reduce_optimized,
405 .int_from_float_optimized,
406 .add_safe,
407 .sub_safe,
408 .mul_safe,
409 .div_ceil,
410 .div_ceil_optimized,
411 .bit_cast,
412 .bit_cast_safe,
413 .ptr_cast,
414 .ptr_from_int,
415 .int_from_ptr,
416 .error_cast,
417 .error_from_int,
418 .int_from_error,
419 .union_from_enum,
420 .int_cast,
421 .int_cast_safe,
422 .agg_field_val,
423 .array_to_vector,
424 .int_from_float_safe,
425 .int_from_float_optimized_safe,
426 .shuffle_one,
427 .shuffle_two,
428 .cmp_lte_errors_len,
429 .runtime_nav_ptr,
430 .spirv_runtime_array_len,
431 .legalize_vec_store_elem,
432 .legalize_vec_elem_val,
433 .legalize_compiler_rt_call,
434 => |tag| return cg.fail("TODO: implement spork8 inst: {t}", .{tag}),
435
436 .unreach => cg.airUnreachable(inst),
437 .assembly => cg.airAssembly(inst),
438 .trap => cg.airTrap(inst),
439
440 .work_item_id,
441 .work_group_size,
442 .work_group_id,
443 => unreachable,
444 };
445}
446
447fn airUnreachable(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
448 _ = cg;
449 _ = inst;
450}
451
452fn airTrap(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
453 _ = inst;
454 try cg.addTag(.halt);
455}
456
457fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
458 const unwrapped_asm = cg.air.unwrapAsm(inst);
459 const outputs = unwrapped_asm.outputs;
460 // const inputs = unwrapped_asm.inputs;
461
462 const zcu = cg.pt.zcu;
463 // const output_ty = cg.typeOfIndex(inst);
464
465 if (outputs.len != 0) {
466 @panic("TODO: Support assembly outputs");
467 }
468
469 var constValues: std.array_hash_map.String(u8) = .empty;
470 defer constValues.deinit(zcu.gpa);
471 {
472 var it = unwrapped_asm.iterateInputs();
473 while (it.next()) |input| {
474 const constraint = input.constraint;
475 if (!mem.eql(u8, constraint, "I")) {
476 return cg.fail("assembly constraint {q} not supported", .{constraint});
477 }
478 const operand = input.operand.toInterned() orelse {
479 return cg.fail("immediate argument to inline assembly must be compile-time value", .{});
480 };
481 const name = input.name;
482
483 const value = switch (zcu.intern_pool.indexToKey(operand)) {
484 .int => |val| v: {
485 if (val.ty != .u8_type) {
486 return cg.fail("non-u8 type used in inline assembly value: {}", .{val.ty});
487 }
488 break :v val.storage.u64;
489 },
490 else => return cg.fail("non-int operands not supported", .{}),
491 };
492
493 try constValues.put(zcu.gpa, name, @intCast(value));
494 }
495 }
496
497 {
498 var lines = mem.tokenizeScalar(u8, unwrapped_asm.source, '\n');
499 while (lines.next()) |line| {
500 var tokens = mem.tokenizeScalar(u8, line, ' ');
501 // If there's no tokens, then it must be a blank line, so just skip it.
502 const op = tokens.next() orelse continue;
503 const instType = std.meta.stringToEnum(AsmInstType, op) orelse return cg.fail("invalid asm instruction: {q}", .{op});
504 switch (instType) {
505 .LoadI => {
506 const registerString = tokens.next() orelse return cg.fail("missing register for LoadI instruction", .{});
507 const register = std.meta.stringToEnum(Register, registerString) orelse return cg.fail("invalid register: {q}", .{registerString});
508 const value = tokens.next() orelse return cg.fail("missing immediate value for LoadI", .{});
509 const intValue = v: {
510 if (mem.startsWith(u8, value, "%[")) {
511 const name = value[2 .. value.len - 1];
512 break :v constValues.get(name) orelse return cg.fail("constraint name {q} not included in constraints for inline asm", .{name});
513 } else {
514 break :v std.fmt.parseInt(u8, value, 0) catch |err|
515 return cg.fail("invalid LoadI immediate value: {t}", .{err});
516 }
517 };
518 if (register != .OutA) {
519 return cg.fail("TODO: support other variants of LoadI", .{});
520 }
521 try cg.addTagImm8(.load_i_outa, intValue);
522 },
523 else => return cg.fail("TODO: support asm instruction: {t}", .{instType}),
524 }
525 }
526 }
527}
528
529pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
530 try cg.mir_instructions.append(cg.gpa, inst);
531}
532
533pub fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
534 try cg.addInst(.{ .tag = tag, .data = .{ .nothing = {} } });
535}
536
537pub fn addTagImm8(cg: *CodeGen, tag: Mir.Inst.Tag, imm8: u8) error{OutOfMemory}!void {
538 try cg.addInst(.{ .tag = tag, .data = .{ .imm8 = imm8 } });
539}
540
541fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } {
542 const zcu = cg.pt.zcu;
543 const func = zcu.funcInfo(cg.func_index);
544 return zcu.codegenFail(func.owner_nav, fmt, args);
545}
546
547fn extraLen(cg: *const CodeGen) u32 {
548 return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off);
549}
550
551const AsmInstType = enum(u8) {
552 /// Set the memory address high byte to a register value.
553 SetPageReg,
554 /// Set the memory address high byte to a constant value.
555 SetPageI,
556 /// Set the memory address low byte to a register value.
557 SetAddrReg,
558 /// Set the memory address low byte to a constant value.
559 SetAddrI,
560 /// Load a value from a constant address into a register.
561 Load,
562 /// Load a constant value into a register.
563 LoadI,
564 /// Load a value from a constant address (setting low byte only) into a register.
565 LoadP,
566 /// Load a value from the currently set memory address into a register, and increment the address n times.
567 LoadInc,
568 /// Load a value from an offset on the current stack frame into a register.
569 LoadStck,
570 /// Store a value to a constant address from a register.
571 Store,
572 /// Store a constant value into a constant address.
573 StoreI,
574 /// Store a value to a constant address (low byte only) from a register.
575 StoreP,
576 /// Store a value from the currently set memory address from a register, and increment the address n times.
577 StoreInc,
578 /// Store a value to an offset on the current stack frame, from a register.
579 StoreStck,
580 /// Store a value to an offset on the next stack frame, from a register.
581 StoreNStck,
582 /// Store a value to an offset on the previous stack frame, from a register.
583 StorePStck,
584 /// Store a constant value to an offset on the current stack frame.
585 StoreStckI,
586 /// Store a constant value to an offset on the next stack frame.
587 StoreNStckI,
588 /// Store a constant value to an offset on the previous stack frame.
589 StorePStckI,
590 /// Copy a value from one register to another register.
591 Copy,
592 /// Jump to a constant location.
593 Jump,
594 /// Jump to a register A (high byte) + register B (low byte).
595 JumpReg,
596 /// Jump to a location pointed to by memory at the current memory address (high byte first).
597 JumpMem,
598 /// Call a function.
599 Call,
600 /// Return from a function.
601 Return,
602 /// Compare A to a constant value (sets flags, but discards result).
603 CmpI,
604 /// Compare A to a constant value with bitwise AND (sets flags, but discards result).
605 CmpAndI,
606 /// Compare A to a value from memory (sets flags, but discards result).
607 Cmp,
608 /// Compare A to a value in memory with bitwise AND (sets flags, but discards result).
609 CmpAnd,
610 /// Compare A to a value from a register (sets flags, but discards result).
611 CmpReg,
612 /// Compare A to a value from a register with bitwise AND (sets flags, but discards result).
613 CmpAndReg,
614 /// Shift B left by 1.
615 ShiftL,
616 /// Shift B right by 1.
617 ShiftR,
618 /// Rotate B left by 1.
619 RotateL,
620 /// Rotate B right by 1.
621 RotateR,
622 /// Add a constant value to A.
623 AddI,
624 /// Subtract a constant value from A.
625 SubI,
626 /// Bitwise-AND A with a constant value.
627 AndI,
628 /// Add a constant value to A, without updating flags.
629 AddINF,
630 /// Subtract a constant value from A, without updating flags.
631 SubINF,
632 /// Bitwise-AND A with a constant value, without updating flags.
633 AndINF,
634 /// Add register B to A -> A.
635 AccumulateAdd,
636 /// Subtract register B from A -> A.
637 AccumulateSub,
638 /// A & B -> A.
639 AccumulateAnd,
640 /// Bitwise OR B with A -> A.
641 OrI,
642 /// Bitwise OR a constant value with A -> A.
643 XorI,
644 /// Invert register A.
645 Not,
646 /// Add a value from memory to A.
647 Add,
648 /// Subtract a value from memory from A.
649 Sub,
650 /// AND A with a value from memory.
651 And,
652 /// OR A with a value from memory.
653 Or,
654 /// XOR A with a value from memory.
655 Xor,
656 /// No-op.
657 Nop,
658 /// No-op with 1 extra clock cycle.
659 Nop1,
660 /// No-op with 2 extra clock cycles.
661 Nop2,
662 /// Halt - stop the program forever (until reset).
663 Halt,
664};
665
666const Register = enum(u8) {
667 A,
668 B,
669 C,
670 PCnt,
671 MAdr,
672 Stack,
673 OutA,
674 Shift,
675 Swap,
676};
src/codegen/spork8/Mir.zig created+54
......@@ -0,0 +1,54 @@
1const Mir = @This();
2const InternPool = @import("../../InternPool.zig");
3
4const builtin = @import("builtin");
5const std = @import("std");
6const assert = std.debug.assert;
7
8instructions: std.MultiArrayList(Inst).Slice,
9
10extra: []const u32,
11
12pub const Inst = struct {
13 tag: Tag,
14 data: Data,
15
16 /// The position of a given MIR isntruction with the instruction list.
17 pub const Index = enum(u32) {
18 _,
19 };
20
21 pub const Tag = enum(u8) {
22 /// imm8
23 set_page_i = 0x04,
24 /// imm8
25 set_addr_i = 0x09,
26 /// imm8
27 load_i_outa = 0x15,
28 /// index
29 jump = 0x68,
30 /// nothing
31 halt = 0xE3,
32 };
33
34 /// All instructions contain a 4-byte payload, which is contained within
35 /// this union. `Tag` determines which union tag is active, as well as
36 /// how to interpret the data within.
37 pub const Data = union {
38 imm8: u8,
39 index: Index,
40 nothing: void,
41
42 comptime {
43 switch (builtin.mode) {
44 .Debug, .ReleaseSafe => {},
45 .ReleaseFast, .ReleaseSmall => assert(@sizeOf(Data) == 4),
46 }
47 }
48 };
49};
50
51pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
52 mir.instructions.deinit(gpa);
53 mir.* = undefined;
54}
src/dev.zig+17
......@@ -60,6 +60,10 @@ pub const Env = enum {
6060 /// - `zig build-* -fincremental -fno-llvm -fno-lld -target loongarch(32/64)-linux --listen=-`
6161 @"loongarch-linux",
6262
63 /// - sema
64 /// - `zig build-* -fno-llvm -fno-lld -target spork8-* --listen=-`
65 spork8,
66
6367 pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool {
6468 return switch (dev_env) {
6569 .full => true,
......@@ -102,6 +106,7 @@ pub const Env = enum {
102106 .sparc64_backend,
103107 .spirv_backend,
104108 .loongarch_backend,
109 .spork8_backend,
105110 .lld_linker,
106111 .coff_linker,
107112 .coff2_linker,
......@@ -112,6 +117,7 @@ pub const Env = enum {
112117 .wasm_linker,
113118 .spirv_linker,
114119 .plan9_linker,
120 .spork8_linker,
115121 .jit_command,
116122 => true,
117123 .cc_command,
......@@ -239,6 +245,15 @@ pub const Env = enum {
239245 => true,
240246 else => Env.sema.supports(feature),
241247 },
248 .spork8 => switch (feature) {
249 .stdio_listen,
250 .incremental,
251 .legalize,
252 .spork8_backend,
253 .spork8_linker,
254 => true,
255 else => Env.sema.supports(feature),
256 },
242257 };
243258 }
244259
......@@ -303,6 +318,7 @@ pub const Feature = enum {
303318 sparc64_backend,
304319 spirv_backend,
305320 loongarch_backend,
321 spork8_backend,
306322
307323 lld_linker,
308324 coff_linker,
......@@ -314,6 +330,7 @@ pub const Feature = enum {
314330 wasm_linker,
315331 spirv_linker,
316332 plan9_linker,
333 spork8_linker,
317334};
318335
319336/// Makes the code following the call to this function unreachable if `feature` is disabled.
src/link.zig+13-1
......@@ -673,6 +673,7 @@ pub const File = struct {
673673 });
674674 },
675675 .plan9 => unreachable,
676 .spork8 => dev.check(.spork8_linker),
676677 }
677678 }
678679
......@@ -750,6 +751,7 @@ pub const File = struct {
750751 },
751752 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
752753 .plan9 => unreachable,
754 .spork8 => dev.check(.spork8_linker),
753755 }
754756 }
755757
......@@ -1025,6 +1027,7 @@ pub const File = struct {
10251027 .spirv => unreachable,
10261028 .wasm => unreachable,
10271029 .plan9 => unreachable,
1030 .spork8 => unreachable,
10281031 inline else => |tag| {
10291032 dev.check(tag.devFeature());
10301033 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
......@@ -1047,6 +1050,7 @@ pub const File = struct {
10471050 .spirv => unreachable,
10481051 .wasm => unreachable,
10491052 .plan9 => unreachable,
1053 .spork8 => unreachable,
10501054 inline else => |tag| {
10511055 dev.check(tag.devFeature());
10521056 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align);
......@@ -1064,6 +1068,7 @@ pub const File = struct {
10641068 .spirv => unreachable,
10651069 .wasm => unreachable,
10661070 .plan9 => unreachable,
1071 .spork8 => unreachable,
10671072 inline else => |tag| {
10681073 dev.check(tag.devFeature());
10691074 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
......@@ -1088,6 +1093,7 @@ pub const File = struct {
10881093 .spirv,
10891094 .plan9,
10901095 .lld,
1096 .spork8,
10911097 => return .unimplemented,
10921098 inline else => |tag| {
10931099 dev.check(tag.devFeature());
......@@ -1266,6 +1272,7 @@ pub const File = struct {
12661272 c,
12671273 wasm,
12681274 spirv,
1275 spork8,
12691276 plan9,
12701277 lld,
12711278
......@@ -1280,6 +1287,7 @@ pub const File = struct {
12801287 .spirv => SpirV,
12811288 .lld => Lld,
12821289 .plan9 => comptime unreachable,
1290 .spork8 => Spork8,
12831291 };
12841292 }
12851293
......@@ -1293,7 +1301,10 @@ pub const File = struct {
12931301 .c => .c,
12941302 .spirv => .spirv,
12951303 .hex => @panic("TODO implement hex object format"),
1296 .raw => @panic("TODO implement raw object format"),
1304 // This may seem surprising at first, but with a little massaging, the spork8 linker
1305 // could and probably should be generalized into a "raw linker" which is used to output
1306 // bare machine code for any architecture for which a corresponding backend exists.
1307 .raw => .spork8,
12971308 };
12981309 }
12991310
......@@ -1373,6 +1384,7 @@ pub const File = struct {
13731384 pub const Lld = @import("link/Lld.zig");
13741385 pub const C = @import("link/C.zig");
13751386 pub const Coff2 = @import("link/Coff.zig");
1387 pub const Spork8 = @import("link/Spork8.zig");
13761388 pub const Elf = @import("link/Elf.zig");
13771389 pub const Elf2 = @import("link/Elf2.zig");
13781390 pub const MachO = @import("link/MachO.zig");
src/link/Spork8.zig created+271
......@@ -0,0 +1,271 @@
1const Spork8 = @This();
2const builtin = @import("builtin");
3const build_options = @import("build_options");
4
5const std = @import("std");
6const Io = std.Io;
7const Allocator = std.mem.Allocator;
8const assert = std.debug.assert;
9const Path = std.Build.Cache.Path;
10const log = std.log.scoped(.link);
11
12const Air = @import("../Air.zig");
13const InternPool = @import("../InternPool.zig");
14const Zcu = @import("../Zcu.zig");
15const CodeGen = @import("../codegen/spork8/CodeGen.zig");
16const codegen = @import("../codegen.zig");
17const Mir = @import("../codegen/spork8/Mir.zig");
18const link = @import("../link.zig");
19const Compilation = @import("../Compilation.zig");
20const Liveness = @import("../Air/Liveness.zig");
21const dev = @import("../dev.zig");
22const Value = @import("../Value.zig");
23
24base: link.File,
25/// All MIR instructions for all Zcu functions.
26mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
27/// Corresponds to `mir_instructions`.
28mir_extra: std.ArrayListUnmanaged(u32) = .empty,
29/// When the key is an enum type, this represents a `@tagName` function.
30zcu_funcs: std.array_hash_map.Auto(InternPool.Index, ZcuFunc) = .empty,
31
32pub fn open(
33 arena: Allocator,
34 comp: *Compilation,
35 emit: Path,
36 options: link.File.OpenOptions,
37) !*Spork8 {
38 // TODO: restore saved linker state, don't truncate the file, and
39 // participate in incremental compilation.
40 return createEmpty(arena, comp, emit, options);
41}
42
43pub fn createEmpty(
44 arena: Allocator,
45 comp: *Compilation,
46 emit: Path,
47 options: link.File.OpenOptions,
48) !*Spork8 {
49 const target = comp.root_mod.resolved_target.result;
50 assert(target.ofmt == .raw);
51 assert(comp.config.output_mode == .Exe);
52 const io = comp.io;
53
54 const spork8 = try arena.create(Spork8);
55 spork8.* = .{
56 .base = .{
57 .tag = .spork8,
58 .comp = comp,
59 .emit = emit,
60 .gc_sections = options.gc_sections orelse true,
61 .print_gc_sections = options.print_gc_sections,
62 .stack_size = options.stack_size orelse switch (target.os.tag) {
63 .freestanding => 1 * 1024 * 1024, // 1 MiB
64 else => 16 * 1024 * 1024, // 16 MiB
65 },
66 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
67 .file = null,
68 .build_id = options.build_id,
69 },
70 };
71 errdefer spork8.base.destroy();
72
73 spork8.base.file = try emit.root_dir.handle.createFile(io, emit.sub_path, .{
74 .truncate = true,
75 .read = true,
76 });
77
78 return spork8;
79}
80
81pub fn deinit(spork8: *Spork8) void {
82 const gpa = spork8.base.comp.gpa;
83 _ = gpa;
84}
85
86pub fn updateFunc(
87 spork8: *Spork8,
88 pt: Zcu.PerThread,
89 func_index: InternPool.Index,
90 any_mir: *const codegen.AnyMir,
91) !void {
92 dev.check(.spork8_backend);
93 // This linker implementation only works with `std.lang.CompilerBackend.zsf_spork8`.
94 const mir = &any_mir.spork8;
95 const zcu = pt.zcu;
96 const gpa = zcu.gpa;
97 const ip = &zcu.intern_pool;
98 const owner_nav = zcu.funcInfo(func_index).owner_nav;
99
100 log.debug("updateFunc {f}", .{ip.getNav(owner_nav).fqn.fmt(ip)});
101
102 // For Spork8, we do not lower the MIR to code just yet. That lowering happens during `flush`,
103 // after garbage collection, which can affect function and global indexes, which affects the
104 // LEB integer encoding, which affects the output binary size.
105
106 // However, we do move the MIR into a more efficient in-memory representation, where the arrays
107 // for all functions are packed together rather than keeping them each in their own `Mir`.
108 const mir_instructions_off: u32 = @intCast(spork8.mir_instructions.len);
109 const mir_extra_off: u32 = @intCast(spork8.mir_extra.items.len);
110 {
111 // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices.
112 const old_len = spork8.mir_instructions.len;
113 try spork8.mir_instructions.resize(gpa, old_len + mir.instructions.len);
114 const dest_slice = spork8.mir_instructions.slice().subslice(old_len, mir.instructions.len);
115 const src_slice = mir.instructions;
116 @memcpy(dest_slice.items(.tag), src_slice.items(.tag));
117 @memcpy(dest_slice.items(.data), src_slice.items(.data));
118 }
119 try spork8.mir_extra.appendSlice(gpa, mir.extra);
120
121 try spork8.zcu_funcs.ensureUnusedCapacity(gpa, 1);
122
123 // This converts AIR to MIR but does not yet lower to Spork8 code.
124 spork8.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{
125 .instructions_off = mir_instructions_off,
126 .instructions_len = @intCast(mir.instructions.len),
127 .extra_off = mir_extra_off,
128 .extra_len = @intCast(mir.extra.len),
129 } });
130}
131
132pub const ZcuFunc = union {
133 function: Function,
134
135 pub const Function = extern struct {
136 /// Index into `Spork8.mir_instructions`.
137 instructions_off: u32,
138 /// This is unused except for as a safety slice bound and could be removed.
139 instructions_len: u32,
140 /// Index into `Spork8.mir_extra`.
141 extra_off: u32,
142 /// This is unused except for as a safety slice bound and could be removed.
143 extra_len: u32,
144 };
145
146 /// Index into `Spork8.zcu_funcs`.
147 /// Note that swapRemove is sometimes performed on `zcu_funcs`.
148 pub const Index = enum(u32) {
149 _,
150
151 pub fn key(i: @This(), spork8: *const Spork8) *InternPool.Index {
152 return &spork8.zcu_funcs.keys()[@backingInt(i)];
153 }
154
155 pub fn value(i: @This(), spork8: *const Spork8) *ZcuFunc {
156 return &spork8.zcu_funcs.values()[@backingInt(i)];
157 }
158 };
159};
160
161// Generate code for the "Nav", storing it in memory to be later written to
162// the file on flush().
163pub fn updateNav(spork8: *Spork8, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
164 _ = spork8;
165 const zcu = pt.zcu;
166 const ip = &zcu.intern_pool;
167 const nav = ip.getNav(nav_index);
168 log.debug("updateNav {f}", .{nav.fqn.fmt(ip)});
169}
170
171pub fn updateLineNumber(spork8: *Spork8, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
172 _ = spork8;
173 _ = pt;
174 _ = ti_id;
175}
176
177pub fn deleteExport(
178 spork8: *Spork8,
179 exported: Zcu.Exported,
180 name: InternPool.NullTerminatedString,
181) void {
182 const zcu = spork8.base.comp.zcu.?;
183 const ip = &zcu.intern_pool;
184 const name_slice = name.toSlice(ip);
185 switch (exported) {
186 .nav => |nav_index| {
187 log.debug("deleteExport '{s}' nav={d}", .{ name_slice, @backingInt(nav_index) });
188 },
189 .uav => |uav_index| {
190 log.debug("deleteExport '{s}' uav={d}", .{ name_slice, @backingInt(uav_index) });
191 },
192 }
193}
194
195pub fn updateExports(
196 spork8: *Spork8,
197 pt: Zcu.PerThread,
198 export_indices: []const Zcu.Export.Index,
199) !void {
200 _ = spork8;
201 const zcu = pt.zcu;
202 const ip = &zcu.intern_pool;
203
204 for (export_indices) |export_idx| {
205 const exp = export_idx.ptr(zcu);
206 const name_slice = exp.opts.name.toSlice(ip);
207 switch (exp.exported) {
208 .nav => |nav_index| {
209 log.debug("updateExports {q} nav={d}", .{ name_slice, @backingInt(nav_index) });
210 },
211 .uav => |uav_index| {
212 log.debug("updateExports {q} uav={d}", .{ name_slice, @backingInt(uav_index) });
213 },
214 }
215 }
216}
217
218pub fn loadInput(spork8: *Spork8, input: link.Input) !void {
219 _ = input;
220 const comp = spork8.base.comp;
221 const diags = &comp.link_diags;
222 return diags.failParse("spork8 does not support linking files together", .{});
223}
224
225pub fn flush(
226 spork8: *Spork8,
227 arena: Allocator,
228 tid: Zcu.PerThread.Id,
229 prog_node: std.Progress.Node,
230) link.Error!void {
231 const sub_prog_node = prog_node.start("Spork8 Flush", 0);
232 defer sub_prog_node.end();
233 const io = spork8.base.comp.io;
234 const diags = &spork8.base.comp.link_diags;
235
236 _ = arena;
237 _ = tid;
238
239 // Finally, write the entire binary into the file.
240 var buffer: [1000]u8 = undefined;
241 var file_writer = spork8.base.file.?.writer(io, &buffer);
242 mirToMC(spork8, &file_writer.interface) catch |err| switch (err) {
243 error.WriteFailed => return diags.fail("failed writing to file: {t}", .{file_writer.err.?}),
244 };
245 file_writer.end() catch |err| switch (err) {
246 error.WriteFailed => return diags.fail("failed writing to file: {t}", .{file_writer.err.?}),
247 else => |e| return diags.fail("failed writing to file: {t}", .{e}),
248 };
249}
250
251fn mirToMC(spork8: *Spork8, w: *Io.Writer) !void {
252 for (spork8.mir_instructions.items(.tag), spork8.mir_instructions.items(.data)) |tag, data| {
253 switch (tag) {
254 .set_page_i => @panic("TODO"),
255 .set_addr_i => @panic("TODO"),
256 .load_i_outa => {
257 try w.writeByte(@backingInt(tag));
258 try w.writeByte(data.imm8);
259 },
260 .jump => @panic("TODO"),
261 .halt => try w.writeByte(@backingInt(tag)),
262 }
263 }
264}
265
266pub fn prelink(spork8: *Spork8, prog_node: std.Progress.Node) link.Error!void {
267 const sub_prog_node = prog_node.start("Spork8 Prelink", 0);
268 defer sub_prog_node.end();
269
270 _ = spork8;
271}
src/target.zig+27-20
......@@ -271,6 +271,7 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat)
271271 .sheb,
272272 .x86_16,
273273 .xtensaeb,
274 .spork8,
274275 => false,
275276 };
276277}
......@@ -395,26 +396,29 @@ pub fn classifyCompilerRtLibName(name: []const u8) CompilerRtClassification {
395396}
396397
397398pub fn hasDebugInfo(target: *const std.Target) bool {
398 return switch (target.cpu.arch) {
399 // TODO: We should make newer PTX versions depend on older ones so we'd just check `ptx75`.
400 .nvptx, .nvptx64 => target.cpu.hasAny(.nvptx, &.{
401 .ptx75,
402 .ptx76,
403 .ptx77,
404 .ptx78,
405 .ptx80,
406 .ptx81,
407 .ptx82,
408 .ptx83,
409 .ptx84,
410 .ptx85,
411 .ptx86,
412 .ptx87,
413 .ptx88,
414 .ptx90,
415 }),
416 .bpfel, .bpfeb => false,
417 else => true,
399 return switch (target.ofmt) {
400 .raw, .hex => false,
401 else => switch (target.cpu.arch) {
402 // TODO: We should make newer PTX versions depend on older ones so we'd just check `ptx75`.
403 .nvptx, .nvptx64 => target.cpu.hasAny(.nvptx, &.{
404 .ptx75,
405 .ptx76,
406 .ptx77,
407 .ptx78,
408 .ptx80,
409 .ptx81,
410 .ptx82,
411 .ptx83,
412 .ptx84,
413 .ptx85,
414 .ptx86,
415 .ptx87,
416 .ptx88,
417 .ptx90,
418 }),
419 .bpfel, .bpfeb => false,
420 else => true,
421 },
418422 };
419423}
420424
......@@ -433,6 +437,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_onl
433437 }
434438 switch (target.cpu.arch) {
435439 .spirv32, .spirv64 => return .no,
440 .spork8 => return .no,
436441 // Remove this once https://github.com/ziglang/zig/issues/23714 is fixed
437442 .amdgcn => return .no,
438443 else => {},
......@@ -445,6 +450,7 @@ pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_onl
445450
446451pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only, llvm_lld_only } {
447452 switch (target.cpu.arch) {
453 .spork8 => return .no,
448454 .spirv32, .spirv64 => return .no,
449455 // Remove this once https://github.com/ziglang/zig/issues/23715 is fixed
450456 .nvptx, .nvptx64 => return .no,
......@@ -929,6 +935,7 @@ pub fn zigBackend(target: *const std.Target, use_llvm: bool) std.lang.CompilerBa
929935 .wasm32, .wasm64 => .stage2_wasm,
930936 .x86 => .stage2_x86,
931937 .x86_64 => .stage2_x86_64,
938 .spork8 => .zsf_spork8,
932939 else => .other,
933940 };
934941}