authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-13 22:59:06+07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-13 16:43:59-04:00
logfb0692334ed64a995690c21525fc3e729e63f424
tree1a7a8f2617bf890c66ba965a6cc49352b9c48629
parentaceb7e18bd1eb25affe8118e01e066c9e6bb0c04

target: Rename sparcv9 -> sparc64

Rename all references of sparcv9 to sparc64, to make Zig align more with other projects. Also, added new function to convert glibc arch name to Zig arch name, since it refers to the architecture as sparcv9. This is based on the suggestion by @kubkon in PR 11847. (https://github.com/ziglang/zig/pull/11487#pullrequestreview-963761757)

44 files changed, 3767 insertions(+), 3713 deletions(-)

CMakeLists.txt+5-5
...@@ -612,11 +612,11 @@ set(ZIG_STAGE2_SOURCES...@@ -612,11 +612,11 @@ set(ZIG_STAGE2_SOURCES
612 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig"612 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig"
613 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig"613 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig"
614 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/abi.zig"614 "${CMAKE_SOURCE_DIR}/src/arch/riscv64/abi.zig"
615 "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/CodeGen.zig"615 "${CMAKE_SOURCE_DIR}/src/arch/sparc64/CodeGen.zig"
616 "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/Emit.zig"616 "${CMAKE_SOURCE_DIR}/src/arch/sparc64/Emit.zig"
617 "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/Mir.zig"617 "${CMAKE_SOURCE_DIR}/src/arch/sparc64/Mir.zig"
618 "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/bits.zig"618 "${CMAKE_SOURCE_DIR}/src/arch/sparc64/bits.zig"
619 "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/abi.zig"619 "${CMAKE_SOURCE_DIR}/src/arch/sparc64/abi.zig"
620 "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig"620 "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig"
621 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig"621 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig"
622 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"622 "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig"
doc/langref.html.in+2-2
...@@ -11392,7 +11392,7 @@ Architectures:...@@ -11392,7 +11392,7 @@ Architectures:
11392 riscv3211392 riscv32
11393 riscv6411393 riscv64
11394 sparc11394 sparc
11395 sparcv911395 sparc64
11396 sparcel11396 sparcel
11397 s390x11397 s390x
11398 thumb11398 thumb
...@@ -11543,7 +11543,7 @@ Available libcs:...@@ -11543,7 +11543,7 @@ Available libcs:
11543 s390x-linux-gnu11543 s390x-linux-gnu
11544 s390x-linux-musl11544 s390x-linux-musl
11545 sparc-linux-gnu11545 sparc-linux-gnu
11546 sparcv9-linux-gnu11546 sparc64-linux-gnu
11547 wasm32-freestanding-musl11547 wasm32-freestanding-musl
11548 wasm32-wasi-musl11548 wasm32-wasi-musl
11549 x86_64-linux-gnu11549 x86_64-linux-gnu
lib/c.zig+1-1
...@@ -625,7 +625,7 @@ fn clone() callconv(.Naked) void {...@@ -625,7 +625,7 @@ fn clone() callconv(.Naked) void {
625 \\ sc625 \\ sc
626 );626 );
627 },627 },
628 .sparcv9 => {628 .sparc64 => {
629 // __clone(func, stack, flags, arg, ptid, tls, ctid)629 // __clone(func, stack, flags, arg, ptid, tls, ctid)
630 // i0, i1, i2, i3, i4, i5, sp630 // i0, i1, i2, i3, i4, i5, sp
631 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)631 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
lib/compiler_rt/clear_cache.zig+1-1
...@@ -36,7 +36,7 @@ pub fn clear_cache(start: usize, end: usize) callconv(.C) void {...@@ -36,7 +36,7 @@ pub fn clear_cache(start: usize, end: usize) callconv(.C) void {
36 else => false,36 else => false,
37 };37 };
38 const sparc = switch (arch) {38 const sparc = switch (arch) {
39 .sparc, .sparcv9, .sparcel => true,39 .sparc, .sparc64, .sparcel => true,
40 else => false,40 else => false,
41 };41 };
42 const apple = switch (os) {42 const apple = switch (os) {
lib/std/Thread.zig+1-1
...@@ -859,7 +859,7 @@ const LinuxThreadImpl = struct {...@@ -859,7 +859,7 @@ const LinuxThreadImpl = struct {
859 [len] "r" (self.mapped.len),859 [len] "r" (self.mapped.len),
860 : "memory"860 : "memory"
861 ),861 ),
862 .sparcv9 => asm volatile (862 .sparc64 => asm volatile (
863 \\ # SPARCs really don't like it when active stack frames863 \\ # SPARCs really don't like it when active stack frames
864 \\ # is unmapped (it will result in a segfault), so we864 \\ # is unmapped (it will result in a segfault), so we
865 \\ # force-deactivate it by running `restore` until865 \\ # force-deactivate it by running `restore` until
lib/std/builtin.zig+3-3
...@@ -727,8 +727,8 @@ pub const CompilerBackend = enum(u64) {...@@ -727,8 +727,8 @@ pub const CompilerBackend = enum(u64) {
727 /// riscv64 backend.727 /// riscv64 backend.
728 stage2_riscv64 = 9,728 stage2_riscv64 = 9,
729 /// The reference implementation self-hosted compiler of Zig, using the729 /// The reference implementation self-hosted compiler of Zig, using the
730 /// sparcv9 backend.730 /// sparc64 backend.
731 stage2_sparcv9 = 10,731 stage2_sparc64 = 10,
732732
733 _,733 _,
734};734};
...@@ -775,7 +775,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -775,7 +775,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
775 builtin.zig_backend == .stage2_x86_64 or775 builtin.zig_backend == .stage2_x86_64 or
776 builtin.zig_backend == .stage2_x86 or776 builtin.zig_backend == .stage2_x86 or
777 builtin.zig_backend == .stage2_riscv64 or777 builtin.zig_backend == .stage2_riscv64 or
778 builtin.zig_backend == .stage2_sparcv9)778 builtin.zig_backend == .stage2_sparc64)
779 {779 {
780 while (true) {780 while (true) {
781 @breakpoint();781 @breakpoint();
lib/std/c/linux.zig+2-2
...@@ -115,7 +115,7 @@ pub const _errno = switch (native_abi) {...@@ -115,7 +115,7 @@ pub const _errno = switch (native_abi) {
115};115};
116116
117pub const Stat = switch (native_arch) {117pub const Stat = switch (native_arch) {
118 .sparcv9 => extern struct {118 .sparc64 => extern struct {
119 dev: u64,119 dev: u64,
120 __pad1: u16,120 __pad1: u16,
121 ino: ino_t,121 ino: ino_t,
...@@ -345,7 +345,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (native_abi) {...@@ -345,7 +345,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (native_abi) {
345 .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (native_arch) {345 .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (native_arch) {
346 .aarch64 => 48,346 .aarch64 => 48,
347 .x86_64 => if (native_abi == .gnux32) 40 else 32,347 .x86_64 => if (native_abi == .gnux32) 40 else 32,
348 .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40,348 .mips64, .powerpc64, .powerpc64le, .sparc64 => 40,
349 else => if (@sizeOf(usize) == 8) 40 else 24,349 else => if (@sizeOf(usize) == 8) 40 else 24,
350 },350 },
351 .android => if (@sizeOf(usize) == 8) 40 else 4,351 .android => if (@sizeOf(usize) == 8) 40 else 4,
lib/std/c/minix.zig+1-1
...@@ -11,7 +11,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) {...@@ -11,7 +11,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) {
11 .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.cpu.arch) {11 .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.cpu.arch) {
12 .aarch64 => 48,12 .aarch64 => 48,
13 .x86_64 => if (builtin.abi == .gnux32) 40 else 32,13 .x86_64 => if (builtin.abi == .gnux32) 40 else 32,
14 .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40,14 .mips64, .powerpc64, .powerpc64le, .sparc64 => 40,
15 else => if (@sizeOf(usize) == 8) 40 else 24,15 else => if (@sizeOf(usize) == 8) 40 else 24,
16 },16 },
17 else => unreachable,17 else => unreachable,
lib/std/c/netbsd.zig+3-3
...@@ -99,14 +99,14 @@ const pthread_spin_t = switch (builtin.cpu.arch) {...@@ -99,14 +99,14 @@ const pthread_spin_t = switch (builtin.cpu.arch) {
99 .powerpc, .powerpc64, .powerpc64le => i32,99 .powerpc, .powerpc64, .powerpc64le => i32,
100 .i386, .x86_64 => u8,100 .i386, .x86_64 => u8,
101 .arm, .armeb, .thumb, .thumbeb => i32,101 .arm, .armeb, .thumb, .thumbeb => i32,
102 .sparc, .sparcel, .sparcv9 => u8,102 .sparc, .sparcel, .sparc64 => u8,
103 .riscv32, .riscv64 => u32,103 .riscv32, .riscv64 => u32,
104 else => @compileError("undefined pthread_spin_t for this arch"),104 else => @compileError("undefined pthread_spin_t for this arch"),
105};105};
106106
107const padded_pthread_spin_t = switch (builtin.cpu.arch) {107const padded_pthread_spin_t = switch (builtin.cpu.arch) {
108 .i386, .x86_64 => u32,108 .i386, .x86_64 => u32,
109 .sparc, .sparcel, .sparcv9 => u32,109 .sparc, .sparcel, .sparc64 => u32,
110 else => pthread_spin_t,110 else => pthread_spin_t,
111};111};
112112
...@@ -1070,7 +1070,7 @@ pub const ucontext_t = extern struct {...@@ -1070,7 +1070,7 @@ pub const ucontext_t = extern struct {
1070 .i386 => 4,1070 .i386 => 4,
1071 .mips, .mipsel, .mips64, .mips64el => 14,1071 .mips, .mipsel, .mips64, .mips64el => 14,
1072 .arm, .armeb, .thumb, .thumbeb => 1,1072 .arm, .armeb, .thumb, .thumbeb => 1,
1073 .sparc, .sparcel, .sparcv9 => if (@sizeOf(usize) == 4) 43 else 8,1073 .sparc, .sparcel, .sparc64 => if (@sizeOf(usize) == 4) 43 else 8,
1074 else => 0,1074 else => 0,
1075 }1075 }
1076 ]u32,1076 ]u32,
lib/std/c/openbsd.zig+1-1
...@@ -1263,7 +1263,7 @@ pub const E = enum(u16) {...@@ -1263,7 +1263,7 @@ pub const E = enum(u16) {
12631263
1264const _MAX_PAGE_SHIFT = switch (builtin.cpu.arch) {1264const _MAX_PAGE_SHIFT = switch (builtin.cpu.arch) {
1265 .i386 => 12,1265 .i386 => 12,
1266 .sparcv9 => 13,1266 .sparc64 => 13,
1267};1267};
1268pub const MINSIGSTKSZ = 1 << _MAX_PAGE_SHIFT;1268pub const MINSIGSTKSZ = 1 << _MAX_PAGE_SHIFT;
1269pub const SIGSTKSZ = MINSIGSTKSZ + (1 << _MAX_PAGE_SHIFT) * 4;1269pub const SIGSTKSZ = MINSIGSTKSZ + (1 << _MAX_PAGE_SHIFT) * 4;
lib/std/debug.zig+1-1
...@@ -401,7 +401,7 @@ pub const StackIterator = struct {...@@ -401,7 +401,7 @@ pub const StackIterator = struct {
401 fp: usize,401 fp: usize,
402402
403 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {403 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
404 if (native_arch == .sparcv9) {404 if (native_arch == .sparc64) {
405 // Flush all the register windows on stack.405 // Flush all the register windows on stack.
406 asm volatile (406 asm volatile (
407 \\ flushw407 \\ flushw
lib/std/elf.zig+1-1
...@@ -1511,7 +1511,7 @@ pub const EM = enum(u16) {...@@ -1511,7 +1511,7 @@ pub const EM = enum(u16) {
1511 .RISCV => .riscv64,1511 .RISCV => .riscv64,
1512 .X86_64 => .x86_64,1512 .X86_64 => .x86_64,
1513 .BPF => .bpfel,1513 .BPF => .bpfel,
1514 .SPARCV9 => .sparcv9,1514 .SPARCV9 => .sparc64,
1515 .S390 => .s390x,1515 .S390 => .s390x,
1516 .SPU_2 => .spu_2,1516 .SPU_2 => .spu_2,
1517 // there's many cases we don't (yet) handle, or will never have a1517 // there's many cases we don't (yet) handle, or will never have a
lib/std/mem.zig+1-1
...@@ -18,7 +18,7 @@ pub const page_size = switch (builtin.cpu.arch) {...@@ -18,7 +18,7 @@ pub const page_size = switch (builtin.cpu.arch) {
18 .macos, .ios, .watchos, .tvos => 16 * 1024,18 .macos, .ios, .watchos, .tvos => 16 * 1024,
19 else => 4 * 1024,19 else => 4 * 1024,
20 },20 },
21 .sparcv9 => 8 * 1024,21 .sparc64 => 8 * 1024,
22 else => 4 * 1024,22 else => 4 * 1024,
23};23};
2424
lib/std/os/linux.zig+6-6
...@@ -38,7 +38,7 @@ const arch_bits = switch (native_arch) {...@@ -38,7 +38,7 @@ const arch_bits = switch (native_arch) {
38 .aarch64 => @import("linux/arm64.zig"),38 .aarch64 => @import("linux/arm64.zig"),
39 .arm, .thumb => @import("linux/arm-eabi.zig"),39 .arm, .thumb => @import("linux/arm-eabi.zig"),
40 .riscv64 => @import("linux/riscv64.zig"),40 .riscv64 => @import("linux/riscv64.zig"),
41 .sparcv9 => @import("linux/sparc64.zig"),41 .sparc64 => @import("linux/sparc64.zig"),
42 .mips, .mipsel => @import("linux/mips.zig"),42 .mips, .mipsel => @import("linux/mips.zig"),
43 .powerpc => @import("linux/powerpc.zig"),43 .powerpc => @import("linux/powerpc.zig"),
44 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),44 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
...@@ -1098,7 +1098,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -1098,7 +1098,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
10981098
1099 const result = switch (native_arch) {1099 const result = switch (native_arch) {
1100 // The sparc version of rt_sigaction needs the restorer function to be passed as an argument too.1100 // The sparc version of rt_sigaction needs the restorer function to be passed as an argument too.
1101 .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size),1101 .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size),
1102 else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),1102 else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),
1103 };1103 };
1104 if (getErrno(result) != .SUCCESS) return result;1104 if (getErrno(result) != .SUCCESS) return result;
...@@ -1698,7 +1698,7 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize {...@@ -1698,7 +1698,7 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize {
16981698
1699pub const E = switch (native_arch) {1699pub const E = switch (native_arch) {
1700 .mips, .mipsel => @import("linux/errno/mips.zig").E,1700 .mips, .mipsel => @import("linux/errno/mips.zig").E,
1701 .sparc, .sparcel, .sparcv9 => @import("linux/errno/sparc.zig").E,1701 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,
1702 else => @import("linux/errno/generic.zig").E,1702 else => @import("linux/errno/generic.zig").E,
1703};1703};
17041704
...@@ -4090,7 +4090,7 @@ pub const V = switch (native_arch) {...@@ -4090,7 +4090,7 @@ pub const V = switch (native_arch) {
4090 pub const LNEXT = 15;4090 pub const LNEXT = 15;
4091 pub const DISCARD = 16;4091 pub const DISCARD = 16;
4092 },4092 },
4093 .sparc, .sparcv9 => struct {4093 .sparc, .sparc64 => struct {
4094 pub const INTR = 0;4094 pub const INTR = 0;
4095 pub const QUIT = 1;4095 pub const QUIT = 1;
4096 pub const ERASE = 2;4096 pub const ERASE = 2;
...@@ -5427,7 +5427,7 @@ pub const AUDIT = struct {...@@ -5427,7 +5427,7 @@ pub const AUDIT = struct {
5427 .aarch64 => .AARCH64,5427 .aarch64 => .AARCH64,
5428 .arm, .thumb => .ARM,5428 .arm, .thumb => .ARM,
5429 .riscv64 => .RISCV64,5429 .riscv64 => .RISCV64,
5430 .sparcv9 => .SPARC64,5430 .sparc64 => .SPARC64,
5431 .mips => .MIPS,5431 .mips => .MIPS,
5432 .mipsel => .MIPSEL,5432 .mipsel => .MIPSEL,
5433 .powerpc => .PPC,5433 .powerpc => .PPC,
...@@ -5454,7 +5454,7 @@ pub const AUDIT = struct {...@@ -5454,7 +5454,7 @@ pub const AUDIT = struct {
5454 RISCV64 = toAudit(.riscv64),5454 RISCV64 = toAudit(.riscv64),
5455 S390X = toAudit(.s390x),5455 S390X = toAudit(.s390x),
5456 SPARC = toAudit(.sparc),5456 SPARC = toAudit(.sparc),
5457 SPARC64 = toAudit(.sparcv9),5457 SPARC64 = toAudit(.sparc64),
5458 X86_64 = toAudit(.x86_64),5458 X86_64 = toAudit(.x86_64),
54595459
5460 fn toAudit(arch: std.Target.Cpu.Arch) u32 {5460 fn toAudit(arch: std.Target.Cpu.Arch) u32 {
lib/std/os/linux/ioctl.zig+1-1
...@@ -10,7 +10,7 @@ const bits = switch (@import("builtin").cpu.arch) {...@@ -10,7 +10,7 @@ const bits = switch (@import("builtin").cpu.arch) {
10 .powerpc64,10 .powerpc64,
11 .powerpc64le,11 .powerpc64le,
12 .sparc,12 .sparc,
13 .sparcv9,13 .sparc64,
14 .sparcel,14 .sparcel,
15 => .{ .size = 13, .dir = 3, .none = 1, .read = 2, .write = 4 },15 => .{ .size = 13, .dir = 3, .none = 1, .read = 2, .write = 4 },
16 else => .{ .size = 14, .dir = 2, .none = 0, .read = 2, .write = 1 },16 else => .{ .size = 14, .dir = 2, .none = 0, .read = 2, .write = 1 },
lib/std/os/linux/tls.zig+2-2
...@@ -49,7 +49,7 @@ const TLSVariant = enum {...@@ -49,7 +49,7 @@ const TLSVariant = enum {
4949
50const tls_variant = switch (native_arch) {50const tls_variant = switch (native_arch) {
51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
52 .x86_64, .i386, .sparcv9 => TLSVariant.VariantII,52 .x86_64, .i386, .sparc64 => TLSVariant.VariantII,
53 else => @compileError("undefined tls_variant for this architecture"),53 else => @compileError("undefined tls_variant for this architecture"),
54};54};
5555
...@@ -174,7 +174,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -174,7 +174,7 @@ pub fn setThreadPointer(addr: usize) void {
174 : [addr] "r" (addr),174 : [addr] "r" (addr),
175 );175 );
176 },176 },
177 .sparcv9 => {177 .sparc64 => {
178 asm volatile (178 asm volatile (
179 \\ mov %[addr], %%g7179 \\ mov %[addr], %%g7
180 :180 :
lib/std/start.zig+3-3
...@@ -29,7 +29,7 @@ comptime {...@@ -29,7 +29,7 @@ comptime {
29 builtin.zig_backend == .stage2_aarch64 or29 builtin.zig_backend == .stage2_aarch64 or
30 builtin.zig_backend == .stage2_arm or30 builtin.zig_backend == .stage2_arm or
31 builtin.zig_backend == .stage2_riscv64 or31 builtin.zig_backend == .stage2_riscv64 or
32 builtin.zig_backend == .stage2_sparcv9)32 builtin.zig_backend == .stage2_sparc64)
33 {33 {
34 if (builtin.output_mode == .Exe) {34 if (builtin.output_mode == .Exe) {
35 if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) {35 if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) {
...@@ -164,7 +164,7 @@ fn exit2(code: usize) noreturn {...@@ -164,7 +164,7 @@ fn exit2(code: usize) noreturn {
164 : "rcx", "r11", "memory"164 : "rcx", "r11", "memory"
165 );165 );
166 },166 },
167 .sparcv9 => {167 .sparc64 => {
168 asm volatile ("ta 0x6d"168 asm volatile ("ta 0x6d"
169 :169 :
170 : [number] "{g1}" (1),170 : [number] "{g1}" (1),
...@@ -323,7 +323,7 @@ fn _start() callconv(.Naked) noreturn {...@@ -323,7 +323,7 @@ fn _start() callconv(.Naked) noreturn {
323 : "r0"323 : "r0"
324 );324 );
325 },325 },
326 .sparcv9 => {326 .sparc64 => {
327 // argc is stored after a register window (16 registers) plus stack bias327 // argc is stored after a register window (16 registers) plus stack bias
328 argc_argv_ptr = asm (328 argc_argv_ptr = asm (
329 \\ mov %%g0, %%i6329 \\ mov %%g0, %%i6
lib/std/target.zig+15-15
...@@ -785,7 +785,7 @@ pub const Target = struct {...@@ -785,7 +785,7 @@ pub const Target = struct {
785 riscv32,785 riscv32,
786 riscv64,786 riscv64,
787 sparc,787 sparc,
788 sparcv9,788 sparc64,
789 sparcel,789 sparcel,
790 s390x,790 s390x,
791 tce,791 tce,
...@@ -884,7 +884,7 @@ pub const Target = struct {...@@ -884,7 +884,7 @@ pub const Target = struct {
884884
885 pub fn isSPARC(arch: Arch) bool {885 pub fn isSPARC(arch: Arch) bool {
886 return switch (arch) {886 return switch (arch) {
887 .sparc, .sparcel, .sparcv9 => true,887 .sparc, .sparcel, .sparc64 => true,
888 else => false,888 else => false,
889 };889 };
890 }890 }
...@@ -964,7 +964,7 @@ pub const Target = struct {...@@ -964,7 +964,7 @@ pub const Target = struct {
964 .bpfel => .BPF,964 .bpfel => .BPF,
965 .bpfeb => .BPF,965 .bpfeb => .BPF,
966 .csky => .CSKY,966 .csky => .CSKY,
967 .sparcv9 => .SPARCV9,967 .sparc64 => .SPARCV9,
968 .s390x => .S390,968 .s390x => .S390,
969 .ve => .NONE,969 .ve => .NONE,
970 .spu_2 => .SPU_2,970 .spu_2 => .SPU_2,
...@@ -1025,7 +1025,7 @@ pub const Target = struct {...@@ -1025,7 +1025,7 @@ pub const Target = struct {
1025 .bpfel => .Unknown,1025 .bpfel => .Unknown,
1026 .bpfeb => .Unknown,1026 .bpfeb => .Unknown,
1027 .csky => .Unknown,1027 .csky => .Unknown,
1028 .sparcv9 => .Unknown,1028 .sparc64 => .Unknown,
1029 .s390x => .Unknown,1029 .s390x => .Unknown,
1030 .ve => .Unknown,1030 .ve => .Unknown,
1031 .spu_2 => .Unknown,1031 .spu_2 => .Unknown,
...@@ -1092,7 +1092,7 @@ pub const Target = struct {...@@ -1092,7 +1092,7 @@ pub const Target = struct {
1092 .powerpc64,1092 .powerpc64,
1093 .thumbeb,1093 .thumbeb,
1094 .sparc,1094 .sparc,
1095 .sparcv9,1095 .sparc64,
1096 .tce,1096 .tce,
1097 .lanai,1097 .lanai,
1098 .s390x,1098 .s390x,
...@@ -1159,7 +1159,7 @@ pub const Target = struct {...@@ -1159,7 +1159,7 @@ pub const Target = struct {
1159 .amdgcn,1159 .amdgcn,
1160 .bpfel,1160 .bpfel,
1161 .bpfeb,1161 .bpfeb,
1162 .sparcv9,1162 .sparc64,
1163 .s390x,1163 .s390x,
1164 .ve,1164 .ve,
1165 .spirv64,1165 .spirv64,
...@@ -1177,7 +1177,7 @@ pub const Target = struct {...@@ -1177,7 +1177,7 @@ pub const Target = struct {
1177 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",1177 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
1178 .amdgcn => "amdgpu",1178 .amdgcn => "amdgpu",
1179 .riscv32, .riscv64 => "riscv",1179 .riscv32, .riscv64 => "riscv",
1180 .sparc, .sparcv9, .sparcel => "sparc",1180 .sparc, .sparc64, .sparcel => "sparc",
1181 .s390x => "systemz",1181 .s390x => "systemz",
1182 .i386, .x86_64 => "x86",1182 .i386, .x86_64 => "x86",
1183 .nvptx, .nvptx64 => "nvptx",1183 .nvptx, .nvptx64 => "nvptx",
...@@ -1200,7 +1200,7 @@ pub const Target = struct {...@@ -1200,7 +1200,7 @@ pub const Target = struct {
1200 .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features,1200 .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features,
1201 .amdgcn => &amdgpu.all_features,1201 .amdgcn => &amdgpu.all_features,
1202 .riscv32, .riscv64 => &riscv.all_features,1202 .riscv32, .riscv64 => &riscv.all_features,
1203 .sparc, .sparcv9, .sparcel => &sparc.all_features,1203 .sparc, .sparc64, .sparcel => &sparc.all_features,
1204 .spirv32, .spirv64 => &spirv.all_features,1204 .spirv32, .spirv64 => &spirv.all_features,
1205 .s390x => &systemz.all_features,1205 .s390x => &systemz.all_features,
1206 .i386, .x86_64 => &x86.all_features,1206 .i386, .x86_64 => &x86.all_features,
...@@ -1225,7 +1225,7 @@ pub const Target = struct {...@@ -1225,7 +1225,7 @@ pub const Target = struct {
1225 .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu),1225 .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu),
1226 .amdgcn => comptime allCpusFromDecls(amdgpu.cpu),1226 .amdgcn => comptime allCpusFromDecls(amdgpu.cpu),
1227 .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu),1227 .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu),
1228 .sparc, .sparcv9, .sparcel => comptime allCpusFromDecls(sparc.cpu),1228 .sparc, .sparc64, .sparcel => comptime allCpusFromDecls(sparc.cpu),
1229 .s390x => comptime allCpusFromDecls(systemz.cpu),1229 .s390x => comptime allCpusFromDecls(systemz.cpu),
1230 .i386, .x86_64 => comptime allCpusFromDecls(x86.cpu),1230 .i386, .x86_64 => comptime allCpusFromDecls(x86.cpu),
1231 .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu),1231 .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu),
...@@ -1286,7 +1286,7 @@ pub const Target = struct {...@@ -1286,7 +1286,7 @@ pub const Target = struct {
1286 .riscv32 => &riscv.cpu.generic_rv32,1286 .riscv32 => &riscv.cpu.generic_rv32,
1287 .riscv64 => &riscv.cpu.generic_rv64,1287 .riscv64 => &riscv.cpu.generic_rv64,
1288 .sparc, .sparcel => &sparc.cpu.generic,1288 .sparc, .sparcel => &sparc.cpu.generic,
1289 .sparcv9 => &sparc.cpu.v9,1289 .sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline
1290 .s390x => &systemz.cpu.generic,1290 .s390x => &systemz.cpu.generic,
1291 .i386 => &x86.cpu._i386,1291 .i386 => &x86.cpu._i386,
1292 .x86_64 => &x86.cpu.x86_64,1292 .x86_64 => &x86.cpu.x86_64,
...@@ -1587,7 +1587,7 @@ pub const Target = struct {...@@ -1587,7 +1587,7 @@ pub const Target = struct {
1587 .powerpc, .powerpcle => return copy(&result, "/lib/ld.so.1"),1587 .powerpc, .powerpcle => return copy(&result, "/lib/ld.so.1"),
1588 .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),1588 .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"),
1589 .s390x => return copy(&result, "/lib64/ld64.so.1"),1589 .s390x => return copy(&result, "/lib64/ld64.so.1"),
1590 .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"),1590 .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"),
1591 .x86_64 => return copy(&result, switch (self.abi) {1591 .x86_64 => return copy(&result, switch (self.abi) {
1592 .gnux32 => "/libx32/ld-linux-x32.so.2",1592 .gnux32 => "/libx32/ld-linux-x32.so.2",
1593 else => "/lib64/ld-linux-x86-64.so.2",1593 else => "/lib64/ld-linux-x86-64.so.2",
...@@ -1735,7 +1735,7 @@ pub const Target = struct {...@@ -1735,7 +1735,7 @@ pub const Target = struct {
1735 .mips64,1735 .mips64,
1736 .mips64el,1736 .mips64el,
1737 .sparc,1737 .sparc,
1738 .sparcv9,1738 .sparc64,
1739 .sparcel,1739 .sparcel,
1740 .powerpc,1740 .powerpc,
1741 .powerpcle,1741 .powerpcle,
...@@ -1760,7 +1760,7 @@ pub const Target = struct {...@@ -1760,7 +1760,7 @@ pub const Target = struct {
1760 .mips64,1760 .mips64,
1761 .mips64el,1761 .mips64el,
1762 .sparc,1762 .sparc,
1763 .sparcv9,1763 .sparc64,
1764 .sparcel,1764 .sparcel,
1765 .powerpc,1765 .powerpc,
1766 .powerpcle,1766 .powerpcle,
...@@ -1810,14 +1810,14 @@ pub const Target = struct {...@@ -1810,14 +1810,14 @@ pub const Target = struct {
1810 // 1. Better machine code when loading into SIMD register.1810 // 1. Better machine code when loading into SIMD register.
1811 // 2. The C ABI wants 16 for extern structs.1811 // 2. The C ABI wants 16 for extern structs.
1812 // 3. 16-byte cmpxchg needs 16-byte alignment.1812 // 3. 16-byte cmpxchg needs 16-byte alignment.
1813 // Same logic for riscv64, powerpc64, mips64, sparcv9.1813 // Same logic for riscv64, powerpc64, mips64, sparc64.
1814 .x86_64,1814 .x86_64,
1815 .riscv64,1815 .riscv64,
1816 .powerpc64,1816 .powerpc64,
1817 .powerpc64le,1817 .powerpc64le,
1818 .mips64,1818 .mips64,
1819 .mips64el,1819 .mips64el,
1820 .sparcv9,1820 .sparc64,
18211821
1822 // Even LLVMABIAlignmentOfType(i128) agrees on these targets.1822 // Even LLVMABIAlignmentOfType(i128) agrees on these targets.
1823 .aarch64,1823 .aarch64,
lib/std/zig/system/NativeTargetInfo.zig+1-1
...@@ -931,7 +931,7 @@ pub fn getExternalExecutor(...@@ -931,7 +931,7 @@ pub fn getExternalExecutor(
931 .riscv64 => Executor{ .qemu = "qemu-riscv64" },931 .riscv64 => Executor{ .qemu = "qemu-riscv64" },
932 .s390x => Executor{ .qemu = "qemu-s390x" },932 .s390x => Executor{ .qemu = "qemu-s390x" },
933 .sparc => Executor{ .qemu = "qemu-sparc" },933 .sparc => Executor{ .qemu = "qemu-sparc" },
934 .sparcv9 => Executor{ .qemu = "qemu-sparc64" },934 .sparc64 => Executor{ .qemu = "qemu-sparc64" },
935 .x86_64 => Executor{ .qemu = "qemu-x86_64" },935 .x86_64 => Executor{ .qemu = "qemu-x86_64" },
936 else => return bad_result,936 else => return bad_result,
937 };937 };
lib/std/zig/system/linux.zig+2-2
...@@ -66,7 +66,7 @@ const SparcCpuinfoImpl = struct {...@@ -66,7 +66,7 @@ const SparcCpuinfoImpl = struct {
66const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);66const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl);
6767
68test "cpuinfo: SPARC" {68test "cpuinfo: SPARC" {
69 try testParser(SparcCpuinfoParser, .sparcv9, &Target.sparc.cpu.niagara2,69 try testParser(SparcCpuinfoParser, .sparc64, &Target.sparc.cpu.niagara2,
70 \\cpu : UltraSparc T2 (Niagara2)70 \\cpu : UltraSparc T2 (Niagara2)
71 \\fpu : UltraSparc T2 integrated FPU71 \\fpu : UltraSparc T2 integrated FPU
72 \\pmu : niagara272 \\pmu : niagara2
...@@ -456,7 +456,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {...@@ -456,7 +456,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
456 .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => {456 .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => {
457 return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null;457 return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null;
458 },458 },
459 .sparcv9 => {459 .sparc64 => {
460 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;460 return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null;
461 },461 },
462 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {462 .powerpc, .powerpcle, .powerpc64, .powerpc64le => {
src/Compilation.zig+1-1
...@@ -4566,7 +4566,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -4566,7 +4566,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
4566 .i386 => .stage2_x86,4566 .i386 => .stage2_x86,
4567 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,4567 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
4568 .riscv64 => .stage2_riscv64,4568 .riscv64 => .stage2_riscv64,
4569 .sparcv9 => .stage2_sparcv9,4569 .sparc64 => .stage2_sparc64,
4570 else => .other,4570 else => .other,
4571 };4571 };
4572 };4572 };
src/arch/sparc64/CodeGen.zig created+1791
...@@ -0,0 +1,1791 @@
1//! SPARCv9 codegen.
2//! This lowers AIR into MIR.
3//! For now this only implements medium/low code model with absolute addressing.
4//! TODO add support for other code models.
5const std = @import("std");
6const assert = std.debug.assert;
7const log = std.log.scoped(.codegen);
8const math = std.math;
9const mem = std.mem;
10const Allocator = mem.Allocator;
11const builtin = @import("builtin");
12const link = @import("../../link.zig");
13const Module = @import("../../Module.zig");
14const TypedValue = @import("../../TypedValue.zig");
15const ErrorMsg = Module.ErrorMsg;
16const Air = @import("../../Air.zig");
17const Mir = @import("Mir.zig");
18const Emit = @import("Emit.zig");
19const Liveness = @import("../../Liveness.zig");
20const Type = @import("../../type.zig").Type;
21const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
22const FnResult = @import("../../codegen.zig").FnResult;
23const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
24const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
25const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs);
26
27const build_options = @import("build_options");
28
29const bits = @import("bits.zig");
30const abi = @import("abi.zig");
31const Register = bits.Register;
32
33const Self = @This();
34
35const InnerError = error{
36 OutOfMemory,
37 CodegenFail,
38 OutOfRegisters,
39};
40
41const RegisterView = enum(u1) {
42 caller,
43 callee,
44};
45
46gpa: Allocator,
47air: Air,
48liveness: Liveness,
49bin_file: *link.File,
50target: *const std.Target,
51mod_fn: *const Module.Fn,
52code: *std.ArrayList(u8),
53debug_output: DebugInfoOutput,
54err_msg: ?*ErrorMsg,
55args: []MCValue,
56ret_mcv: MCValue,
57fn_type: Type,
58arg_index: usize,
59src_loc: Module.SrcLoc,
60stack_align: u32,
61
62/// MIR Instructions
63mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
64/// MIR extra data
65mir_extra: std.ArrayListUnmanaged(u32) = .{},
66
67/// Byte offset within the source file of the ending curly.
68end_di_line: u32,
69end_di_column: u32,
70
71/// The value is an offset into the `Function` `code` from the beginning.
72/// To perform the reloc, write 32-bit signed little-endian integer
73/// which is a relative jump, based on the address following the reloc.
74exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
75
76/// Whenever there is a runtime branch, we push a Branch onto this stack,
77/// and pop it off when the runtime branch joins. This provides an "overlay"
78/// of the table of mappings from instructions to `MCValue` from within the branch.
79/// This way we can modify the `MCValue` for an instruction in different ways
80/// within different branches. Special consideration is needed when a branch
81/// joins with its parent, to make sure all instructions have the same MCValue
82/// across each runtime branch upon joining.
83branch_stack: *std.ArrayList(Branch),
84
85// Key is the block instruction
86blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
87
88register_manager: RegisterManager = .{},
89
90/// Maps offset to what is stored there.
91stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
92
93/// Offset from the stack base, representing the end of the stack frame.
94max_end_stack: u32 = 0,
95/// Represents the current end stack offset. If there is no existing slot
96/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
97next_stack_offset: u32 = 0,
98
99/// Debug field, used to find bugs in the compiler.
100air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
101
102const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
103
104const MCValue = union(enum) {
105 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
106 /// TODO Look into deleting this tag and using `dead` instead, since every use
107 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
108 none,
109 /// Control flow will not allow this value to be observed.
110 unreach,
111 /// No more references to this value remain.
112 dead,
113 /// The value is undefined.
114 undef,
115 /// A pointer-sized integer that fits in a register.
116 /// If the type is a pointer, this is the pointer address in virtual address space.
117 immediate: u64,
118 /// The value is in a target-specific register.
119 register: Register,
120 /// The value is in memory at a hard-coded address.
121 /// If the type is a pointer, it means the pointer address is at this memory location.
122 memory: u64,
123 /// The value is one of the stack variables.
124 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
125 stack_offset: u32,
126 /// The value is a pointer to one of the stack variables (payload is stack offset).
127 ptr_stack_offset: u32,
128
129 fn isMemory(mcv: MCValue) bool {
130 return switch (mcv) {
131 .memory, .stack_offset => true,
132 else => false,
133 };
134 }
135
136 fn isImmediate(mcv: MCValue) bool {
137 return switch (mcv) {
138 .immediate => true,
139 else => false,
140 };
141 }
142
143 fn isMutable(mcv: MCValue) bool {
144 return switch (mcv) {
145 .none => unreachable,
146 .unreach => unreachable,
147 .dead => unreachable,
148
149 .immediate,
150 .memory,
151 .ptr_stack_offset,
152 .undef,
153 => false,
154
155 .register,
156 .stack_offset,
157 => true,
158 };
159 }
160};
161
162const Branch = struct {
163 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
164
165 fn deinit(self: *Branch, gpa: Allocator) void {
166 self.inst_table.deinit(gpa);
167 self.* = undefined;
168 }
169};
170
171const StackAllocation = struct {
172 inst: Air.Inst.Index,
173 /// TODO do we need size? should be determined by inst.ty.abiSize()
174 size: u32,
175};
176
177const BlockData = struct {
178 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
179 /// The first break instruction encounters `null` here and chooses a
180 /// machine code value for the block result, populating this field.
181 /// Following break instructions encounter that value and use it for
182 /// the location to store their block results.
183 mcv: MCValue,
184};
185
186const CallMCValues = struct {
187 args: []MCValue,
188 return_value: MCValue,
189 stack_byte_count: u32,
190 stack_align: u32,
191
192 fn deinit(self: *CallMCValues, func: *Self) void {
193 func.gpa.free(self.args);
194 self.* = undefined;
195 }
196};
197
198const BigTomb = struct {
199 function: *Self,
200 inst: Air.Inst.Index,
201 tomb_bits: Liveness.Bpi,
202 big_tomb_bits: u32,
203 bit_index: usize,
204
205 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
206 const this_bit_index = bt.bit_index;
207 bt.bit_index += 1;
208
209 const op_int = @enumToInt(op_ref);
210 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
211 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
212
213 if (this_bit_index < Liveness.bpi - 1) {
214 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
215 if (!dies) return;
216 } else {
217 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
218 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
219 if (!dies) return;
220 }
221 bt.function.processDeath(op_index);
222 }
223
224 fn finishAir(bt: *BigTomb, result: MCValue) void {
225 const is_used = !bt.function.liveness.isUnused(bt.inst);
226 if (is_used) {
227 log.debug("%{d} => {}", .{ bt.inst, result });
228 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
229 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
230 }
231 bt.function.finishAirBookkeeping();
232 }
233};
234
235pub fn generate(
236 bin_file: *link.File,
237 src_loc: Module.SrcLoc,
238 module_fn: *Module.Fn,
239 air: Air,
240 liveness: Liveness,
241 code: *std.ArrayList(u8),
242 debug_output: DebugInfoOutput,
243) GenerateSymbolError!FnResult {
244 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
245 @panic("Attempted to compile for architecture that was disabled by build configuration");
246 }
247
248 const mod = bin_file.options.module.?;
249 const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
250 assert(fn_owner_decl.has_tv);
251 const fn_type = fn_owner_decl.ty;
252
253 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
254 defer {
255 assert(branch_stack.items.len == 1);
256 branch_stack.items[0].deinit(bin_file.allocator);
257 branch_stack.deinit();
258 }
259 try branch_stack.append(.{});
260
261 var function = Self{
262 .gpa = bin_file.allocator,
263 .air = air,
264 .liveness = liveness,
265 .target = &bin_file.options.target,
266 .bin_file = bin_file,
267 .mod_fn = module_fn,
268 .code = code,
269 .debug_output = debug_output,
270 .err_msg = null,
271 .args = undefined, // populated after `resolveCallingConventionValues`
272 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
273 .fn_type = fn_type,
274 .arg_index = 0,
275 .branch_stack = &branch_stack,
276 .src_loc = src_loc,
277 .stack_align = undefined,
278 .end_di_line = module_fn.rbrace_line,
279 .end_di_column = module_fn.rbrace_column,
280 };
281 defer function.stack.deinit(bin_file.allocator);
282 defer function.blocks.deinit(bin_file.allocator);
283 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
284
285 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
286 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
287 error.OutOfRegisters => return FnResult{
288 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
289 },
290 else => |e| return e,
291 };
292 defer call_info.deinit(&function);
293
294 function.args = call_info.args;
295 function.ret_mcv = call_info.return_value;
296 function.stack_align = call_info.stack_align;
297 function.max_end_stack = call_info.stack_byte_count;
298
299 function.gen() catch |err| switch (err) {
300 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
301 error.OutOfRegisters => return FnResult{
302 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
303 },
304 else => |e| return e,
305 };
306
307 var mir = Mir{
308 .instructions = function.mir_instructions.toOwnedSlice(),
309 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
310 };
311 defer mir.deinit(bin_file.allocator);
312
313 var emit = Emit{
314 .mir = mir,
315 .bin_file = bin_file,
316 .debug_output = debug_output,
317 .target = &bin_file.options.target,
318 .src_loc = src_loc,
319 .code = code,
320 .prev_di_pc = 0,
321 .prev_di_line = module_fn.lbrace_line,
322 .prev_di_column = module_fn.lbrace_column,
323 };
324 defer emit.deinit();
325
326 emit.emitMir() catch |err| switch (err) {
327 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
328 else => |e| return e,
329 };
330
331 if (function.err_msg) |em| {
332 return FnResult{ .fail = em };
333 } else {
334 return FnResult{ .appended = {} };
335 }
336}
337
338fn gen(self: *Self) !void {
339 const cc = self.fn_type.fnCallingConvention();
340 if (cc != .Naked) {
341 // TODO Finish function prologue and epilogue for sparc64.
342
343 // save %sp, stack_save_area, %sp
344 const save_inst = try self.addInst(.{
345 .tag = .save,
346 .data = .{
347 .arithmetic_3op = .{
348 .is_imm = true,
349 .rd = .sp,
350 .rs1 = .sp,
351 .rs2_or_imm = .{ .imm = -abi.stack_save_area },
352 },
353 },
354 });
355
356 _ = try self.addInst(.{
357 .tag = .dbg_prologue_end,
358 .data = .{ .nop = {} },
359 });
360
361 try self.genBody(self.air.getMainBody());
362
363 _ = try self.addInst(.{
364 .tag = .dbg_epilogue_begin,
365 .data = .{ .nop = {} },
366 });
367
368 // exitlude jumps
369 if (self.exitlude_jump_relocs.items.len > 0 and
370 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
371 {
372 // If the last Mir instruction (apart from the
373 // dbg_epilogue_begin) is the last exitlude jump
374 // relocation (which would just jump one instruction
375 // further), it can be safely removed
376 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());
377 }
378
379 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
380 _ = jmp_reloc;
381 return self.fail("TODO add branches in sparc64", .{});
382 }
383
384 // Backpatch stack offset
385 const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space;
386 const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
387 if (math.cast(i13, stack_size)) |size| {
388 self.mir_instructions.set(save_inst, .{
389 .tag = .save,
390 .data = .{
391 .arithmetic_3op = .{
392 .is_imm = true,
393 .rd = .sp,
394 .rs1 = .sp,
395 .rs2_or_imm = .{ .imm = -size },
396 },
397 },
398 });
399 } else |_| {
400 // TODO for large stacks, replace the prologue with:
401 // setx stack_size, %g1
402 // save %sp, %g1, %sp
403 return self.fail("TODO SPARCv9: allow larger stacks", .{});
404 }
405
406 // return %i7 + 8
407 _ = try self.addInst(.{
408 .tag = .@"return",
409 .data = .{
410 .arithmetic_2op = .{
411 .is_imm = true,
412 .rs1 = .@"i7",
413 .rs2_or_imm = .{ .imm = 8 },
414 },
415 },
416 });
417
418 // Branches in SPARC have a delay slot, that is, the instruction
419 // following it will unconditionally be executed.
420 // See: Section 3.2.3 Control Transfer in SPARCv9 manual.
421 // See also: https://arcb.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/delaybra.html
422 // TODO Find a way to fill this delay slot
423 // nop
424 _ = try self.addInst(.{
425 .tag = .nop,
426 .data = .{ .nop = {} },
427 });
428 } else {
429 _ = try self.addInst(.{
430 .tag = .dbg_prologue_end,
431 .data = .{ .nop = {} },
432 });
433
434 try self.genBody(self.air.getMainBody());
435
436 _ = try self.addInst(.{
437 .tag = .dbg_epilogue_begin,
438 .data = .{ .nop = {} },
439 });
440 }
441
442 // Drop them off at the rbrace.
443 _ = try self.addInst(.{
444 .tag = .dbg_line,
445 .data = .{ .dbg_line_column = .{
446 .line = self.end_di_line,
447 .column = self.end_di_column,
448 } },
449 });
450}
451
452fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
453 const air_tags = self.air.instructions.items(.tag);
454
455 for (body) |inst| {
456 const old_air_bookkeeping = self.air_bookkeeping;
457 try self.ensureProcessDeathCapacity(Liveness.bpi);
458
459 switch (air_tags[inst]) {
460 // zig fmt: off
461 .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"),
462 .addwrap => @panic("TODO try self.airAddWrap(inst)"),
463 .add_sat => @panic("TODO try self.airAddSat(inst)"),
464 .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"),
465 .subwrap => @panic("TODO try self.airSubWrap(inst)"),
466 .sub_sat => @panic("TODO try self.airSubSat(inst)"),
467 .mul => @panic("TODO try self.airMul(inst)"),
468 .mulwrap => @panic("TODO try self.airMulWrap(inst)"),
469 .mul_sat => @panic("TODO try self.airMulSat(inst)"),
470 .rem => @panic("TODO try self.airRem(inst)"),
471 .mod => @panic("TODO try self.airMod(inst)"),
472 .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
473 .shl_sat => @panic("TODO try self.airShlSat(inst)"),
474 .min => @panic("TODO try self.airMin(inst)"),
475 .max => @panic("TODO try self.airMax(inst)"),
476 .slice => @panic("TODO try self.airSlice(inst)"),
477
478 .sqrt,
479 .sin,
480 .cos,
481 .tan,
482 .exp,
483 .exp2,
484 .log,
485 .log2,
486 .log10,
487 .fabs,
488 .floor,
489 .ceil,
490 .round,
491 .trunc_float,
492 => @panic("TODO try self.airUnaryMath(inst)"),
493
494 .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"),
495 .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"),
496 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
497 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
498
499 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
500
501 .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"),
502 .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"),
503 .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"),
504 .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"),
505 .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"),
506 .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"),
507 .cmp_vector => @panic("TODO try self.airCmpVector(inst)"),
508 .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"),
509
510 .bool_and => @panic("TODO try self.airBoolOp(inst)"),
511 .bool_or => @panic("TODO try self.airBoolOp(inst)"),
512 .bit_and => @panic("TODO try self.airBitAnd(inst)"),
513 .bit_or => @panic("TODO try self.airBitOr(inst)"),
514 .xor => @panic("TODO try self.airXor(inst)"),
515 .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
516
517 .alloc => @panic("TODO try self.airAlloc(inst)"),
518 .ret_ptr => try self.airRetPtr(inst),
519 .arg => try self.airArg(inst),
520 .assembly => try self.airAsm(inst),
521 .bitcast => @panic("TODO try self.airBitCast(inst)"),
522 .block => try self.airBlock(inst),
523 .br => @panic("TODO try self.airBr(inst)"),
524 .breakpoint => try self.airBreakpoint(),
525 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
526 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
527 .fence => @panic("TODO try self.airFence()"),
528 .cond_br => @panic("TODO try self.airCondBr(inst)"),
529 .dbg_stmt => try self.airDbgStmt(inst),
530 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
531 .fpext => @panic("TODO try self.airFpext(inst)"),
532 .intcast => @panic("TODO try self.airIntCast(inst)"),
533 .trunc => @panic("TODO try self.airTrunc(inst)"),
534 .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"),
535 .is_non_null => @panic("TODO try self.airIsNonNull(inst)"),
536 .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"),
537 .is_null => @panic("TODO try self.airIsNull(inst)"),
538 .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"),
539 .is_non_err => @panic("TODO try self.airIsNonErr(inst)"),
540 .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"),
541 .is_err => @panic("TODO try self.airIsErr(inst)"),
542 .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"),
543 .load => @panic("TODO try self.airLoad(inst)"),
544 .loop => @panic("TODO try self.airLoop(inst)"),
545 .not => @panic("TODO try self.airNot(inst)"),
546 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
547 .ret => try self.airRet(inst),
548 .ret_load => try self.airRetLoad(inst),
549 .store => try self.airStore(inst),
550 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
551 .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"),
552 .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"),
553 .int_to_float => @panic("TODO try self.airIntToFloat(inst)"),
554 .float_to_int => @panic("TODO try self.airFloatToInt(inst)"),
555 .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"),
556 .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"),
557 .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"),
558 .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"),
559 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
560 .memset => @panic("TODO try self.airMemset(inst)"),
561 .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"),
562 .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"),
563 .clz => @panic("TODO try self.airClz(inst)"),
564 .ctz => @panic("TODO try self.airCtz(inst)"),
565 .popcount => @panic("TODO try self.airPopcount(inst)"),
566 .byte_swap => @panic("TODO try self.airByteSwap(inst)"),
567 .bit_reverse => @panic("TODO try self.airBitReverse(inst)"),
568 .tag_name => @panic("TODO try self.airTagName(inst)"),
569 .error_name => @panic("TODO try self.airErrorName(inst)"),
570 .splat => @panic("TODO try self.airSplat(inst)"),
571 .select => @panic("TODO try self.airSelect(inst)"),
572 .shuffle => @panic("TODO try self.airShuffle(inst)"),
573 .reduce => @panic("TODO try self.airReduce(inst)"),
574 .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"),
575 .union_init => @panic("TODO try self.airUnionInit(inst)"),
576 .prefetch => @panic("TODO try self.airPrefetch(inst)"),
577 .mul_add => @panic("TODO try self.airMulAdd(inst)"),
578
579 .dbg_var_ptr,
580 .dbg_var_val,
581 => try self.airDbgVar(inst),
582
583 .dbg_inline_begin,
584 .dbg_inline_end,
585 => try self.airDbgInline(inst),
586
587 .dbg_block_begin,
588 .dbg_block_end,
589 => try self.airDbgBlock(inst),
590
591 .call => try self.airCall(inst, .auto),
592 .call_always_tail => try self.airCall(inst, .always_tail),
593 .call_never_tail => try self.airCall(inst, .never_tail),
594 .call_never_inline => try self.airCall(inst, .never_inline),
595
596 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
597 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
598 .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"),
599 .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"),
600
601 .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"),
602 .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"),
603 .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"),
604 .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"),
605
606 .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"),
607
608 .switch_br => try self.airSwitch(inst),
609 .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"),
610 .slice_len => @panic("TODO try self.airSliceLen(inst)"),
611
612 .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"),
613 .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"),
614
615 .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"),
616 .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"),
617 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
618 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
619 .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"),
620
621 .constant => unreachable, // excluded from function bodies
622 .const_ty => unreachable, // excluded from function bodies
623 .unreach => self.finishAirBookkeeping(),
624
625 .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"),
626 .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"),
627 .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"),
628 .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"),
629 .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"),
630 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
631 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
632 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
633
634 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
635 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
636 .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"),
637
638 .wasm_memory_size => unreachable,
639 .wasm_memory_grow => unreachable,
640 // zig fmt: on
641 }
642
643 if (std.debug.runtime_safety) {
644 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
645 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
646 }
647 }
648 }
649}
650
651fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
652 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
653 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
654 const is_volatile = (extra.data.flags & 0x80000000) != 0;
655 const clobbers_len = @truncate(u31, extra.data.flags);
656 var extra_i: usize = extra.end;
657 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]);
658 extra_i += outputs.len;
659 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]);
660 extra_i += inputs.len;
661
662 const dead = !is_volatile and self.liveness.isUnused(inst);
663 const result: MCValue = if (dead) .dead else result: {
664 if (outputs.len > 1) {
665 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
666 }
667
668 const output_constraint: ?[]const u8 = for (outputs) |output| {
669 if (output != .none) {
670 return self.fail("TODO implement codegen for non-expr asm", .{});
671 }
672 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
673 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
674 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
675 // This equation accounts for the fact that even if we have exactly 4 bytes
676 // for the string, we still use the next u32 for the null terminator.
677 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
678
679 break constraint;
680 } else null;
681
682 for (inputs) |input| {
683 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
684 const constraint = std.mem.sliceTo(input_bytes, 0);
685 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
686 // This equation accounts for the fact that even if we have exactly 4 bytes
687 // for the string, we still use the next u32 for the null terminator.
688 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
689
690 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
691 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
692 }
693 const reg_name = constraint[1 .. constraint.len - 1];
694 const reg = parseRegName(reg_name) orelse
695 return self.fail("unrecognized register: '{s}'", .{reg_name});
696
697 const arg_mcv = try self.resolveInst(input);
698 try self.register_manager.getReg(reg, null);
699 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
700 }
701
702 {
703 var clobber_i: u32 = 0;
704 while (clobber_i < clobbers_len) : (clobber_i += 1) {
705 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
706 // This equation accounts for the fact that even if we have exactly 4 bytes
707 // for the string, we still use the next u32 for the null terminator.
708 extra_i += clobber.len / 4 + 1;
709
710 // TODO honor these
711 }
712 }
713
714 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
715
716 if (mem.eql(u8, asm_source, "ta 0x6d")) {
717 _ = try self.addInst(.{
718 .tag = .tcc,
719 .data = .{
720 .trap = .{
721 .is_imm = true,
722 .cond = 0b1000, // TODO need to look into changing this into an enum
723 .rs2_or_imm = .{ .imm = 0x6d },
724 },
725 },
726 });
727 } else {
728 return self.fail("TODO implement a full SPARCv9 assembly parsing", .{});
729 }
730
731 if (output_constraint) |output| {
732 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
733 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
734 }
735 const reg_name = output[2 .. output.len - 1];
736 const reg = parseRegName(reg_name) orelse
737 return self.fail("unrecognized register: '{s}'", .{reg_name});
738 break :result MCValue{ .register = reg };
739 } else {
740 break :result MCValue{ .none = {} };
741 }
742 };
743
744 simple: {
745 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
746 var buf_index: usize = 0;
747 for (outputs) |output| {
748 if (output == .none) continue;
749
750 if (buf_index >= buf.len) break :simple;
751 buf[buf_index] = output;
752 buf_index += 1;
753 }
754 if (buf_index + inputs.len > buf.len) break :simple;
755 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
756 return self.finishAir(inst, result, buf);
757 }
758
759 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
760 for (outputs) |output| {
761 if (output == .none) continue;
762
763 bt.feed(output);
764 }
765 for (inputs) |input| {
766 bt.feed(input);
767 }
768 return bt.finishAir(result);
769}
770
771fn airArg(self: *Self, inst: Air.Inst.Index) !void {
772 const arg_index = self.arg_index;
773 self.arg_index += 1;
774
775 const ty = self.air.typeOfIndex(inst);
776 _ = ty;
777
778 const result = self.args[arg_index];
779 // TODO support stack-only arguments
780 // TODO Copy registers to the stack
781 const mcv = result;
782
783 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
784
785 if (self.liveness.isUnused(inst))
786 return self.finishAirBookkeeping();
787
788 switch (mcv) {
789 .register => |reg| {
790 self.register_manager.getRegAssumeFree(reg, inst);
791 },
792 else => {},
793 }
794
795 return self.finishAir(inst, mcv, .{ .none, .none, .none });
796}
797
798fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
799 try self.blocks.putNoClobber(self.gpa, inst, .{
800 // A block is a setup to be able to jump to the end.
801 .relocs = .{},
802 // It also acts as a receptacle for break operands.
803 // Here we use `MCValue.none` to represent a null value so that the first
804 // break instruction will choose a MCValue for the block result and overwrite
805 // this field. Following break instructions will use that MCValue to put their
806 // block results.
807 .mcv = MCValue{ .none = {} },
808 });
809 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
810
811 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
812 const extra = self.air.extraData(Air.Block, ty_pl.payload);
813 const body = self.air.extra[extra.end..][0..extra.data.body_len];
814 try self.genBody(body);
815
816 // relocations for `bpcc` instructions
817 const relocs = &self.blocks.getPtr(inst).?.relocs;
818 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
819 // If the last Mir instruction is the last relocation (which
820 // would just jump one instruction further), it can be safely
821 // removed
822 self.mir_instructions.orderedRemove(relocs.pop());
823 }
824 for (relocs.items) |reloc| {
825 try self.performReloc(reloc);
826 }
827
828 const result = self.blocks.getPtr(inst).?.mcv;
829 return self.finishAir(inst, result, .{ .none, .none, .none });
830}
831
832fn airBreakpoint(self: *Self) !void {
833 // ta 0x01
834 _ = try self.addInst(.{
835 .tag = .tcc,
836 .data = .{
837 .trap = .{
838 .is_imm = true,
839 .cond = 0b1000, // TODO need to look into changing this into an enum
840 .rs2_or_imm = .{ .imm = 0x01 },
841 },
842 },
843 });
844 return self.finishAirBookkeeping();
845}
846
847fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
848 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
849
850 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
851 const callee = pl_op.operand;
852 const extra = self.air.extraData(Air.Call, pl_op.payload);
853 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]);
854 const ty = self.air.typeOf(callee);
855 const fn_ty = switch (ty.zigTypeTag()) {
856 .Fn => ty,
857 .Pointer => ty.childType(),
858 else => unreachable,
859 };
860
861 var info = try self.resolveCallingConventionValues(fn_ty, .caller);
862 defer info.deinit(self);
863 for (info.args) |mc_arg, arg_i| {
864 const arg = args[arg_i];
865 const arg_ty = self.air.typeOf(arg);
866 const arg_mcv = try self.resolveInst(arg);
867
868 switch (mc_arg) {
869 .none => continue,
870 .undef => unreachable,
871 .immediate => unreachable,
872 .unreach => unreachable,
873 .dead => unreachable,
874 .memory => unreachable,
875 .register => |reg| {
876 try self.register_manager.getReg(reg, null);
877 try self.genSetReg(arg_ty, reg, arg_mcv);
878 },
879 .stack_offset => {
880 return self.fail("TODO implement calling with parameters in memory", .{});
881 },
882 .ptr_stack_offset => {
883 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
884 },
885 }
886 }
887
888 // Due to incremental compilation, how function calls are generated depends
889 // on linking.
890 if (self.air.value(callee)) |func_value| {
891 if (self.bin_file.tag == link.File.Elf.base_tag) {
892 if (func_value.castTag(.function)) |func_payload| {
893 const func = func_payload.data;
894 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
895 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
896 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
897 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
898 const mod = self.bin_file.options.module.?;
899 break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes);
900 } else unreachable;
901
902 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
903
904 _ = try self.addInst(.{
905 .tag = .jmpl,
906 .data = .{
907 .arithmetic_3op = .{
908 .is_imm = false,
909 .rd = .o7,
910 .rs1 = .o7,
911 .rs2_or_imm = .{ .rs2 = .g0 },
912 },
913 },
914 });
915
916 // TODO Find a way to fill this delay slot
917 _ = try self.addInst(.{
918 .tag = .nop,
919 .data = .{ .nop = {} },
920 });
921 } else if (func_value.castTag(.extern_fn)) |_| {
922 return self.fail("TODO implement calling extern functions", .{});
923 } else {
924 return self.fail("TODO implement calling bitcasted functions", .{});
925 }
926 } else @panic("TODO SPARCv9 currently does not support non-ELF binaries");
927 } else {
928 assert(ty.zigTypeTag() == .Pointer);
929 const mcv = try self.resolveInst(callee);
930 try self.genSetReg(ty, .o7, mcv);
931
932 _ = try self.addInst(.{
933 .tag = .jmpl,
934 .data = .{
935 .arithmetic_3op = .{
936 .is_imm = false,
937 .rd = .o7,
938 .rs1 = .o7,
939 .rs2_or_imm = .{ .rs2 = .g0 },
940 },
941 },
942 });
943
944 // TODO Find a way to fill this delay slot
945 _ = try self.addInst(.{
946 .tag = .nop,
947 .data = .{ .nop = {} },
948 });
949 }
950
951 const result = info.return_value;
952
953 if (args.len + 1 <= Liveness.bpi - 1) {
954 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
955 buf[0] = callee;
956 std.mem.copy(Air.Inst.Ref, buf[1..], args);
957 return self.finishAir(inst, result, buf);
958 }
959
960 @panic("TODO handle return value with BigTomb");
961}
962
963fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
964 // TODO emit debug info lexical block
965 return self.finishAir(inst, .dead, .{ .none, .none, .none });
966}
967
968fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
969 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
970 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
971 // TODO emit debug info for function change
972 _ = function;
973 return self.finishAir(inst, .dead, .{ .none, .none, .none });
974}
975
976fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
977 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
978
979 _ = try self.addInst(.{
980 .tag = .dbg_line,
981 .data = .{
982 .dbg_line_column = .{
983 .line = dbg_stmt.line,
984 .column = dbg_stmt.column,
985 },
986 },
987 });
988
989 return self.finishAirBookkeeping();
990}
991
992fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
993 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
994 const name = self.air.nullTerminatedString(pl_op.payload);
995 const operand = pl_op.operand;
996 // TODO emit debug info for this variable
997 _ = name;
998 return self.finishAir(inst, .dead, .{ operand, .none, .none });
999}
1000
1001fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1002 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1003 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
1004 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1005}
1006
1007fn airRet(self: *Self, inst: Air.Inst.Index) !void {
1008 const un_op = self.air.instructions.items(.data)[inst].un_op;
1009 const operand = try self.resolveInst(un_op);
1010 try self.ret(operand);
1011 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1012}
1013
1014fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
1015 const un_op = self.air.instructions.items(.data)[inst].un_op;
1016 const ptr = try self.resolveInst(un_op);
1017 _ = ptr;
1018 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
1019 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1020}
1021
1022fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1023 const stack_offset = try self.allocMemPtr(inst);
1024 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1025}
1026
1027fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1028 _ = self;
1029 _ = inst;
1030
1031 return self.fail("TODO implement store for {}", .{self.target.cpu.arch});
1032}
1033
1034fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
1035 _ = self;
1036 _ = inst;
1037
1038 return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
1039}
1040
1041// Common helper functions
1042
1043/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
1044/// after codegen for this symbol is done.
1045fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
1046 switch (self.debug_output) {
1047 .dwarf => |dw| {
1048 assert(ty.hasRuntimeBits());
1049 const dbg_info = &dw.dbg_info;
1050 const index = dbg_info.items.len;
1051 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
1052 const mod = self.bin_file.options.module.?;
1053 const atom = switch (self.bin_file.tag) {
1054 .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom,
1055 else => unreachable,
1056 };
1057 try dw.addTypeReloc(atom, ty, @intCast(u32, index), null);
1058 },
1059 else => {},
1060 }
1061}
1062
1063fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
1064 const gpa = self.gpa;
1065 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
1066 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
1067 self.mir_instructions.appendAssumeCapacity(inst);
1068 return result_index;
1069}
1070
1071fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
1072 if (abi_align > self.stack_align)
1073 self.stack_align = abi_align;
1074 // TODO find a free slot instead of always appending
1075 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
1076 self.next_stack_offset = offset + abi_size;
1077 if (self.next_stack_offset > self.max_end_stack)
1078 self.max_end_stack = self.next_stack_offset;
1079 try self.stack.putNoClobber(self.gpa, offset, .{
1080 .inst = inst,
1081 .size = abi_size,
1082 });
1083 return offset;
1084}
1085
1086/// Use a pointer instruction as the basis for allocating stack memory.
1087fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
1088 const elem_ty = self.air.typeOfIndex(inst).elemType();
1089
1090 if (!elem_ty.hasRuntimeBits()) {
1091 // As this stack item will never be dereferenced at runtime,
1092 // return the stack offset 0. Stack offset 0 will be where all
1093 // zero-sized stack allocations live as non-zero-sized
1094 // allocations will always have an offset > 0.
1095 return @as(u32, 0);
1096 }
1097
1098 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1099 const mod = self.bin_file.options.module.?;
1100 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
1101 };
1102 // TODO swap this for inst.ty.ptrAlign
1103 const abi_align = elem_ty.abiAlignment(self.target.*);
1104 return self.allocMem(inst, abi_size, abi_align);
1105}
1106
1107fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
1108 const elem_ty = self.air.typeOfIndex(inst);
1109 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1110 const mod = self.bin_file.options.module.?;
1111 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
1112 };
1113 const abi_align = elem_ty.abiAlignment(self.target.*);
1114 if (abi_align > self.stack_align)
1115 self.stack_align = abi_align;
1116
1117 if (reg_ok) {
1118 // Make sure the type can fit in a register before we try to allocate one.
1119 if (abi_size <= 8) {
1120 if (self.register_manager.tryAllocReg(inst)) |reg| {
1121 return MCValue{ .register = reg };
1122 }
1123 }
1124 }
1125 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
1126 return MCValue{ .stack_offset = stack_offset };
1127}
1128
1129/// Copies a value to a register without tracking the register. The register is not considered
1130/// allocated. A second call to `copyToTmpRegister` may return the same register.
1131/// This can have a side effect of spilling instructions to the stack to free up a register.
1132fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
1133 const reg = try self.register_manager.allocReg(null);
1134 try self.genSetReg(ty, reg, mcv);
1135 return reg;
1136}
1137
1138fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1139 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1140 try table.ensureUnusedCapacity(self.gpa, additional_count);
1141}
1142
1143fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
1144 @setCold(true);
1145 assert(self.err_msg == null);
1146 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
1147 return error.CodegenFail;
1148}
1149
1150/// Called when there are no operands, and the instruction is always unreferenced.
1151fn finishAirBookkeeping(self: *Self) void {
1152 if (std.debug.runtime_safety) {
1153 self.air_bookkeeping += 1;
1154 }
1155}
1156
1157fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
1158 var tomb_bits = self.liveness.getTombBits(inst);
1159 for (operands) |op| {
1160 const dies = @truncate(u1, tomb_bits) != 0;
1161 tomb_bits >>= 1;
1162 if (!dies) continue;
1163 const op_int = @enumToInt(op);
1164 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
1165 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
1166 self.processDeath(op_index);
1167 }
1168 const is_used = @truncate(u1, tomb_bits) == 0;
1169 if (is_used) {
1170 log.debug("%{d} => {}", .{ inst, result });
1171 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1172 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
1173
1174 switch (result) {
1175 .register => |reg| {
1176 // In some cases (such as bitcast), an operand
1177 // may be the same MCValue as the result. If
1178 // that operand died and was a register, it
1179 // was freed by processDeath. We have to
1180 // "re-allocate" the register.
1181 if (self.register_manager.isRegFree(reg)) {
1182 self.register_manager.getRegAssumeFree(reg, inst);
1183 }
1184 },
1185 else => {},
1186 }
1187 }
1188 self.finishAirBookkeeping();
1189}
1190
1191fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
1192 const ty = self.air.instructions.items(.data)[inst].ty;
1193 const name = self.mod_fn.getParamName(arg_index);
1194 const name_with_null = name.ptr[0 .. name.len + 1];
1195
1196 switch (mcv) {
1197 .register => |reg| {
1198 switch (self.debug_output) {
1199 .dwarf => |dw| {
1200 const dbg_info = &dw.dbg_info;
1201 try dbg_info.ensureUnusedCapacity(3);
1202 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
1203 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1204 1, // ULEB128 dwarf expression length
1205 reg.dwarfLocOp(),
1206 });
1207 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
1208 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
1209 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
1210 },
1211 else => {},
1212 }
1213 },
1214 .stack_offset => |offset| {
1215 _ = offset;
1216 switch (self.debug_output) {
1217 .dwarf => {},
1218 else => {},
1219 }
1220 },
1221 else => {},
1222 }
1223}
1224
1225fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1226 assert(off_type == Register or off_type == i13);
1227
1228 const is_imm = (off_type == i13);
1229 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1230
1231 switch (abi_size) {
1232 1, 2, 4, 8 => {
1233 const tag: Mir.Inst.Tag = switch (abi_size) {
1234 1 => .ldub,
1235 2 => .lduh,
1236 4 => .lduw,
1237 8 => .ldx,
1238 else => unreachable, // unexpected abi size
1239 };
1240
1241 _ = try self.addInst(.{
1242 .tag = tag,
1243 .data = .{
1244 .arithmetic_3op = .{
1245 .is_imm = is_imm,
1246 .rd = value_reg,
1247 .rs1 = addr_reg,
1248 .rs2_or_imm = rs2_or_imm,
1249 },
1250 },
1251 });
1252 },
1253 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1254 else => unreachable,
1255 }
1256}
1257
1258fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
1259 switch (mcv) {
1260 .dead => unreachable,
1261 .unreach, .none => return, // Nothing to do.
1262 .undef => {
1263 if (!self.wantSafety())
1264 return; // The already existing value will do just fine.
1265 // Write the debug undefined value.
1266 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1267 },
1268 .ptr_stack_offset => |off| {
1269 const simm13 = math.cast(u12, off) catch
1270 return self.fail("TODO larger stack offsets", .{});
1271
1272 _ = try self.addInst(.{
1273 .tag = .add,
1274 .data = .{
1275 .arithmetic_3op = .{
1276 .is_imm = true,
1277 .rd = reg,
1278 .rs1 = .sp,
1279 .rs2_or_imm = .{ .imm = simm13 },
1280 },
1281 },
1282 });
1283 },
1284 .immediate => |x| {
1285 if (x <= math.maxInt(u12)) {
1286 _ = try self.addInst(.{
1287 .tag = .@"or",
1288 .data = .{
1289 .arithmetic_3op = .{
1290 .is_imm = true,
1291 .rd = reg,
1292 .rs1 = .g0,
1293 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1294 },
1295 },
1296 });
1297 } else if (x <= math.maxInt(u32)) {
1298 _ = try self.addInst(.{
1299 .tag = .sethi,
1300 .data = .{
1301 .sethi = .{
1302 .rd = reg,
1303 .imm = @truncate(u22, x >> 10),
1304 },
1305 },
1306 });
1307
1308 _ = try self.addInst(.{
1309 .tag = .@"or",
1310 .data = .{
1311 .arithmetic_3op = .{
1312 .is_imm = true,
1313 .rd = reg,
1314 .rs1 = reg,
1315 .rs2_or_imm = .{ .imm = @truncate(u10, x) },
1316 },
1317 },
1318 });
1319 } else if (x <= math.maxInt(u44)) {
1320 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) });
1321
1322 _ = try self.addInst(.{
1323 .tag = .sllx,
1324 .data = .{
1325 .shift = .{
1326 .is_imm = true,
1327 .width = .shift64,
1328 .rd = reg,
1329 .rs1 = reg,
1330 .rs2_or_imm = .{ .imm = 12 },
1331 },
1332 },
1333 });
1334
1335 _ = try self.addInst(.{
1336 .tag = .@"or",
1337 .data = .{
1338 .arithmetic_3op = .{
1339 .is_imm = true,
1340 .rd = reg,
1341 .rs1 = reg,
1342 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1343 },
1344 },
1345 });
1346 } else {
1347 // Need to allocate a temporary register to load 64-bit immediates.
1348 const tmp_reg = try self.register_manager.allocReg(null);
1349
1350 try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) });
1351 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) });
1352
1353 _ = try self.addInst(.{
1354 .tag = .sllx,
1355 .data = .{
1356 .shift = .{
1357 .is_imm = true,
1358 .width = .shift64,
1359 .rd = reg,
1360 .rs1 = reg,
1361 .rs2_or_imm = .{ .imm = 32 },
1362 },
1363 },
1364 });
1365
1366 _ = try self.addInst(.{
1367 .tag = .@"or",
1368 .data = .{
1369 .arithmetic_3op = .{
1370 .is_imm = false,
1371 .rd = reg,
1372 .rs1 = reg,
1373 .rs2_or_imm = .{ .rs2 = tmp_reg },
1374 },
1375 },
1376 });
1377 }
1378 },
1379 .register => |src_reg| {
1380 // If the registers are the same, nothing to do.
1381 if (src_reg.id() == reg.id())
1382 return;
1383
1384 // or %g0, src, dst (aka mov src, dst)
1385 _ = try self.addInst(.{
1386 .tag = .@"or",
1387 .data = .{
1388 .arithmetic_3op = .{
1389 .is_imm = false,
1390 .rd = reg,
1391 .rs1 = .g0,
1392 .rs2_or_imm = .{ .rs2 = src_reg },
1393 },
1394 },
1395 });
1396 },
1397 .memory => |addr| {
1398 // The value is in memory at a hard-coded address.
1399 // If the type is a pointer, it means the pointer address is at this memory location.
1400 try self.genSetReg(ty, reg, .{ .immediate = addr });
1401 try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));
1402 },
1403 .stack_offset => |off| {
1404 const real_offset = off + abi.stack_bias + abi.stack_save_area;
1405 const simm13 = math.cast(i13, real_offset) catch
1406 return self.fail("TODO larger stack offsets", .{});
1407 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
1408 },
1409 }
1410}
1411
1412fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1413 const abi_size = ty.abiSize(self.target.*);
1414 switch (mcv) {
1415 .dead => unreachable,
1416 .unreach, .none => return, // Nothing to do.
1417 .undef => {
1418 if (!self.wantSafety())
1419 return; // The already existing value will do just fine.
1420 // TODO Upgrade this to a memset call when we have that available.
1421 switch (ty.abiSize(self.target.*)) {
1422 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
1423 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
1424 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1425 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1426 else => return self.fail("TODO implement memset", .{}),
1427 }
1428 },
1429 .immediate,
1430 .ptr_stack_offset,
1431 => {
1432 const reg = try self.copyToTmpRegister(ty, mcv);
1433 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1434 },
1435 .register => |reg| {
1436 const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area;
1437 const simm13 = math.cast(i13, real_offset) catch
1438 return self.fail("TODO larger stack offsets", .{});
1439 return self.genStore(reg, .sp, i13, simm13, abi_size);
1440 },
1441 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),
1442 }
1443}
1444
1445fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1446 assert(off_type == Register or off_type == i13);
1447
1448 const is_imm = (off_type == i13);
1449 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1450
1451 switch (abi_size) {
1452 1, 2, 4, 8 => {
1453 const tag: Mir.Inst.Tag = switch (abi_size) {
1454 1 => .stb,
1455 2 => .sth,
1456 4 => .stw,
1457 8 => .stx,
1458 else => unreachable, // unexpected abi size
1459 };
1460
1461 _ = try self.addInst(.{
1462 .tag = tag,
1463 .data = .{
1464 .arithmetic_3op = .{
1465 .is_imm = is_imm,
1466 .rd = value_reg,
1467 .rs1 = addr_reg,
1468 .rs2_or_imm = rs2_or_imm,
1469 },
1470 },
1471 });
1472 },
1473 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1474 else => unreachable,
1475 }
1476}
1477
1478fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
1479 if (typed_value.val.isUndef())
1480 return MCValue{ .undef = {} };
1481
1482 if (typed_value.val.castTag(.decl_ref)) |payload| {
1483 return self.lowerDeclRef(typed_value, payload.data);
1484 }
1485 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
1486 return self.lowerDeclRef(typed_value, payload.data.decl_index);
1487 }
1488 const target = self.target.*;
1489
1490 switch (typed_value.ty.zigTypeTag()) {
1491 .Int => {
1492 const info = typed_value.ty.intInfo(self.target.*);
1493 if (info.bits <= 64) {
1494 const unsigned = switch (info.signedness) {
1495 .signed => blk: {
1496 const signed = typed_value.val.toSignedInt();
1497 break :blk @bitCast(u64, signed);
1498 },
1499 .unsigned => typed_value.val.toUnsignedInt(target),
1500 };
1501
1502 return MCValue{ .immediate = unsigned };
1503 } else {
1504 return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
1505 }
1506 },
1507 .ComptimeInt => unreachable, // semantic analysis prevents this
1508 .ComptimeFloat => unreachable, // semantic analysis prevents this
1509 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
1510 }
1511}
1512
1513fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
1514 // Treat each stack item as a "layer" on top of the previous one.
1515 var i: usize = self.branch_stack.items.len;
1516 while (true) {
1517 i -= 1;
1518 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1519 assert(mcv != .dead);
1520 return mcv;
1521 }
1522 }
1523}
1524
1525fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
1526 try self.ensureProcessDeathCapacity(operand_count + 1);
1527 return BigTomb{
1528 .function = self,
1529 .inst = inst,
1530 .tomb_bits = self.liveness.getTombBits(inst),
1531 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
1532 .bit_index = 0,
1533 };
1534}
1535
1536fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
1537 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1538 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1539
1540 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
1541 if (tv.ty.zigTypeTag() == .Pointer) blk: {
1542 if (tv.ty.castPtrToFn()) |_| break :blk;
1543 if (!tv.ty.elemType2().hasRuntimeBits()) {
1544 return MCValue.none;
1545 }
1546 }
1547
1548 const mod = self.bin_file.options.module.?;
1549 const decl = mod.declPtr(decl_index);
1550
1551 mod.markDeclAlive(decl);
1552 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1553 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1554 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
1555 return MCValue{ .memory = got_addr };
1556 } else {
1557 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
1558 }
1559}
1560
1561fn parseRegName(name: []const u8) ?Register {
1562 if (@hasDecl(Register, "parseRegName")) {
1563 return Register.parseRegName(name);
1564 }
1565 return std.meta.stringToEnum(Register, name);
1566}
1567
1568fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
1569 const tag = self.mir_instructions.items(.tag)[inst];
1570 switch (tag) {
1571 .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
1572 else => unreachable,
1573 }
1574}
1575
1576/// Asserts there is already capacity to insert into top branch inst_table.
1577fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1578 const air_tags = self.air.instructions.items(.tag);
1579 if (air_tags[inst] == .constant) return; // Constants are immortal.
1580 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1581 const prev_value = self.getResolvedInstValue(inst);
1582 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1583 branch.inst_table.putAssumeCapacity(inst, .dead);
1584 switch (prev_value) {
1585 .register => |reg| {
1586 self.register_manager.freeReg(reg);
1587 },
1588 else => {}, // TODO process stack allocation death
1589 }
1590}
1591
1592/// Caller must call `CallMCValues.deinit`.
1593fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues {
1594 const cc = fn_ty.fnCallingConvention();
1595 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1596 defer self.gpa.free(param_types);
1597 fn_ty.fnParamTypes(param_types);
1598 var result: CallMCValues = .{
1599 .args = try self.gpa.alloc(MCValue, param_types.len),
1600 // These undefined values must be populated before returning from this function.
1601 .return_value = undefined,
1602 .stack_byte_count = undefined,
1603 .stack_align = undefined,
1604 };
1605 errdefer self.gpa.free(result.args);
1606
1607 const ret_ty = fn_ty.fnReturnType();
1608
1609 switch (cc) {
1610 .Naked => {
1611 assert(result.args.len == 0);
1612 result.return_value = .{ .unreach = {} };
1613 result.stack_byte_count = 0;
1614 result.stack_align = 1;
1615 return result;
1616 },
1617 .Unspecified, .C => {
1618 // SPARC Compliance Definition 2.4.1, Chapter 3
1619 // Low-Level System Information (64-bit psABI) - Function Calling Sequence
1620
1621 var next_register: usize = 0;
1622 var next_stack_offset: u32 = 0;
1623
1624 // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee.
1625 const argument_registers = switch (role) {
1626 .caller => abi.c_abi_int_param_regs_caller_view,
1627 .callee => abi.c_abi_int_param_regs_callee_view,
1628 };
1629
1630 for (param_types) |ty, i| {
1631 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1632 if (param_size <= 8) {
1633 if (next_register < argument_registers.len) {
1634 result.args[i] = .{ .register = argument_registers[next_register] };
1635 next_register += 1;
1636 } else {
1637 result.args[i] = .{ .stack_offset = next_stack_offset };
1638 next_register += next_stack_offset;
1639 }
1640 } else if (param_size <= 16) {
1641 if (next_register < argument_registers.len - 1) {
1642 return self.fail("TODO MCValues with 2 registers", .{});
1643 } else if (next_register < argument_registers.len) {
1644 return self.fail("TODO MCValues split register + stack", .{});
1645 } else {
1646 result.args[i] = .{ .stack_offset = next_stack_offset };
1647 next_register += next_stack_offset;
1648 }
1649 } else {
1650 result.args[i] = .{ .stack_offset = next_stack_offset };
1651 next_register += next_stack_offset;
1652 }
1653 }
1654
1655 result.stack_byte_count = next_stack_offset;
1656 result.stack_align = 16;
1657
1658 if (ret_ty.zigTypeTag() == .NoReturn) {
1659 result.return_value = .{ .unreach = {} };
1660 } else if (!ret_ty.hasRuntimeBits()) {
1661 result.return_value = .{ .none = {} };
1662 } else {
1663 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
1664 // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller.
1665 if (ret_ty_size <= 8) {
1666 result.return_value = switch (role) {
1667 .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] },
1668 .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] },
1669 };
1670 } else {
1671 return self.fail("TODO support more return values for sparc64", .{});
1672 }
1673 }
1674 },
1675 else => return self.fail("TODO implement function parameters for {} on sparc64", .{cc}),
1676 }
1677
1678 return result;
1679}
1680
1681fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
1682 // First section of indexes correspond to a set number of constant values.
1683 const ref_int = @enumToInt(inst);
1684 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
1685 const tv = Air.Inst.Ref.typed_value_map[ref_int];
1686 if (!tv.ty.hasRuntimeBits()) {
1687 return MCValue{ .none = {} };
1688 }
1689 return self.genTypedValue(tv);
1690 }
1691
1692 // If the type has no codegen bits, no need to store it.
1693 const inst_ty = self.air.typeOf(inst);
1694 if (!inst_ty.hasRuntimeBits())
1695 return MCValue{ .none = {} };
1696
1697 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
1698 switch (self.air.instructions.items(.tag)[inst_index]) {
1699 .constant => {
1700 // Constants have static lifetimes, so they are always memoized in the outer most table.
1701 const branch = &self.branch_stack.items[0];
1702 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
1703 if (!gop.found_existing) {
1704 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
1705 gop.value_ptr.* = try self.genTypedValue(.{
1706 .ty = inst_ty,
1707 .val = self.air.values[ty_pl.payload],
1708 });
1709 }
1710 return gop.value_ptr.*;
1711 },
1712 .const_ty => unreachable,
1713 else => return self.getResolvedInstValue(inst_index),
1714 }
1715}
1716
1717fn ret(self: *Self, mcv: MCValue) !void {
1718 const ret_ty = self.fn_type.fnReturnType();
1719 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1720
1721 // Just add space for an instruction, patch this later
1722 const index = try self.addInst(.{
1723 .tag = .nop,
1724 .data = .{ .nop = {} },
1725 });
1726 try self.exitlude_jump_relocs.append(self.gpa, index);
1727}
1728
1729fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1730 if (!self.liveness.operandDies(inst, op_index))
1731 return false;
1732
1733 switch (mcv) {
1734 .register => |reg| {
1735 // If it's in the registers table, need to associate the register with the
1736 // new instruction.
1737 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
1738 if (!self.register_manager.isRegFree(reg)) {
1739 self.register_manager.registers[index] = inst;
1740 }
1741 }
1742 log.debug("%{d} => {} (reused)", .{ inst, reg });
1743 },
1744 .stack_offset => |off| {
1745 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1746 },
1747 else => return false,
1748 }
1749
1750 // Prevent the operand deaths processing code from deallocating it.
1751 self.liveness.clearOperandDeath(inst, op_index);
1752
1753 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1754 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1755 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1756
1757 return true;
1758}
1759
1760/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1761fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
1762 switch (loc) {
1763 .none => return,
1764 .register => |reg| return self.genSetReg(ty, reg, val),
1765 .stack_offset => |off| return self.genSetStack(ty, off, val),
1766 .memory => {
1767 return self.fail("TODO implement setRegOrMem for memory", .{});
1768 },
1769 else => unreachable,
1770 }
1771}
1772
1773pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1774 const stack_mcv = try self.allocRegOrMem(inst, false);
1775 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
1776 const reg_mcv = self.getResolvedInstValue(inst);
1777 assert(reg == reg_mcv.register);
1778 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1779 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1780 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1781}
1782
1783/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
1784fn wantSafety(self: *Self) bool {
1785 return switch (self.bin_file.options.optimize_mode) {
1786 .Debug => true,
1787 .ReleaseSafe => true,
1788 .ReleaseFast => false,
1789 .ReleaseSmall => false,
1790 };
1791}
src/arch/sparc64/Emit.zig created+274
...@@ -0,0 +1,274 @@
1//! This file contains the functionality for lowering SPARCv9 MIR into
2//! machine code
3
4const std = @import("std");
5const Endian = std.builtin.Endian;
6const assert = std.debug.assert;
7const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");
9const ErrorMsg = Module.ErrorMsg;
10const Liveness = @import("../../Liveness.zig");
11const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
12const DW = std.dwarf;
13const leb128 = std.leb;
14
15const Emit = @This();
16const Mir = @import("Mir.zig");
17const bits = @import("bits.zig");
18const Instruction = bits.Instruction;
19const Register = bits.Register;
20
21mir: Mir,
22bin_file: *link.File,
23debug_output: DebugInfoOutput,
24target: *const std.Target,
25err_msg: ?*ErrorMsg = null,
26src_loc: Module.SrcLoc,
27code: *std.ArrayList(u8),
28
29prev_di_line: u32,
30prev_di_column: u32,
31/// Relative to the beginning of `code`.
32prev_di_pc: usize,
33
34const InnerError = error{
35 OutOfMemory,
36 EmitFail,
37};
38
39pub fn emitMir(
40 emit: *Emit,
41) InnerError!void {
42 const mir_tags = emit.mir.instructions.items(.tag);
43
44 // Emit machine code
45 for (mir_tags) |tag, index| {
46 const inst = @intCast(u32, index);
47 switch (tag) {
48 .dbg_line => try emit.mirDbgLine(inst),
49 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
50 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
51
52 .add => try emit.mirArithmetic3Op(inst),
53
54 .bpcc => @panic("TODO implement sparc64 bpcc"),
55
56 .call => @panic("TODO implement sparc64 call"),
57
58 .jmpl => try emit.mirArithmetic3Op(inst),
59
60 .ldub => try emit.mirArithmetic3Op(inst),
61 .lduh => try emit.mirArithmetic3Op(inst),
62 .lduw => try emit.mirArithmetic3Op(inst),
63 .ldx => try emit.mirArithmetic3Op(inst),
64
65 .@"or" => try emit.mirArithmetic3Op(inst),
66
67 .nop => try emit.mirNop(),
68
69 .@"return" => try emit.mirArithmetic2Op(inst),
70
71 .save => try emit.mirArithmetic3Op(inst),
72 .restore => try emit.mirArithmetic3Op(inst),
73
74 .sethi => try emit.mirSethi(inst),
75
76 .sllx => @panic("TODO implement sparc64 sllx"),
77
78 .stb => try emit.mirArithmetic3Op(inst),
79 .sth => try emit.mirArithmetic3Op(inst),
80 .stw => try emit.mirArithmetic3Op(inst),
81 .stx => try emit.mirArithmetic3Op(inst),
82
83 .sub => try emit.mirArithmetic3Op(inst),
84
85 .tcc => try emit.mirTrap(inst),
86 }
87 }
88}
89
90pub fn deinit(emit: *Emit) void {
91 emit.* = undefined;
92}
93
94fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
95 const tag = emit.mir.instructions.items(.tag)[inst];
96 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
97
98 switch (tag) {
99 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
100 else => unreachable,
101 }
102}
103
104fn mirDebugPrologueEnd(emit: *Emit) !void {
105 switch (emit.debug_output) {
106 .dwarf => |dbg_out| {
107 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
108 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
109 },
110 .plan9 => {},
111 .none => {},
112 }
113}
114
115fn mirDebugEpilogueBegin(emit: *Emit) !void {
116 switch (emit.debug_output) {
117 .dwarf => |dbg_out| {
118 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
119 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
120 },
121 .plan9 => {},
122 .none => {},
123 }
124}
125
126fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
127 const tag = emit.mir.instructions.items(.tag)[inst];
128 const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op;
129
130 const rs1 = data.rs1;
131
132 if (data.is_imm) {
133 const imm = data.rs2_or_imm.imm;
134 switch (tag) {
135 .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
136 else => unreachable,
137 }
138 } else {
139 const rs2 = data.rs2_or_imm.rs2;
140 switch (tag) {
141 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
142 else => unreachable,
143 }
144 }
145}
146
147fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
148 const tag = emit.mir.instructions.items(.tag)[inst];
149 const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op;
150
151 const rd = data.rd;
152 const rs1 = data.rs1;
153
154 if (data.is_imm) {
155 const imm = data.rs2_or_imm.imm;
156 switch (tag) {
157 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
158 .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)),
159 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
160 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
161 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
162 .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
163 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
164 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
165 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
166 .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)),
167 .sth => try emit.writeInstruction(Instruction.sth(i13, rs1, imm, rd)),
168 .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)),
169 .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)),
170 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
171 else => unreachable,
172 }
173 } else {
174 const rs2 = data.rs2_or_imm.rs2;
175 switch (tag) {
176 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
177 .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)),
178 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
179 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
180 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
181 .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
182 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
183 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
184 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
185 .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)),
186 .sth => try emit.writeInstruction(Instruction.sth(Register, rs1, rs2, rd)),
187 .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)),
188 .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)),
189 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
190 else => unreachable,
191 }
192 }
193}
194
195fn mirNop(emit: *Emit) !void {
196 try emit.writeInstruction(Instruction.nop());
197}
198
199fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
200 const tag = emit.mir.instructions.items(.tag)[inst];
201 const data = emit.mir.instructions.items(.data)[inst].sethi;
202
203 const imm = data.imm;
204 const rd = data.rd;
205
206 assert(tag == .sethi);
207 try emit.writeInstruction(Instruction.sethi(imm, rd));
208}
209
210fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
211 const tag = emit.mir.instructions.items(.tag)[inst];
212 const data = emit.mir.instructions.items(.data)[inst].trap;
213
214 const cond = data.cond;
215 const ccr = data.ccr;
216 const rs1 = data.rs1;
217
218 if (data.is_imm) {
219 const imm = data.rs2_or_imm.imm;
220 switch (tag) {
221 .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)),
222 else => unreachable,
223 }
224 } else {
225 const rs2 = data.rs2_or_imm.rs2;
226 switch (tag) {
227 .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)),
228 else => unreachable,
229 }
230 }
231}
232
233// Common helper functions
234
235fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
236 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
237 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
238 switch (emit.debug_output) {
239 .dwarf => |dbg_out| {
240 // TODO Look into using the DWARF special opcodes to compress this data.
241 // It lets you emit single-byte opcodes that add different numbers to
242 // both the PC and the line number at the same time.
243 try dbg_out.dbg_line.ensureUnusedCapacity(11);
244 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
245 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
246 if (delta_line != 0) {
247 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
248 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
249 }
250 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
251 emit.prev_di_pc = emit.code.items.len;
252 emit.prev_di_line = line;
253 emit.prev_di_column = column;
254 emit.prev_di_pc = emit.code.items.len;
255 },
256 else => {},
257 }
258}
259
260fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
261 @setCold(true);
262 assert(emit.err_msg == null);
263 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
264 return error.EmitFail;
265}
266
267fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
268 // SPARCv9 instructions are always arranged in BE regardless of the
269 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
270 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
271 const endian = Endian.Big;
272
273 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
274}
src/arch/sparc64/Mir.zig created+256
...@@ -0,0 +1,256 @@
1//! Machine Intermediate Representation.
2//! This data is produced by SPARCv9 Codegen or SPARCv9 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const std = @import("std");
10const builtin = @import("builtin");
11const assert = std.debug.assert;
12
13const Mir = @This();
14const bits = @import("bits.zig");
15const Air = @import("../../Air.zig");
16
17const Instruction = bits.Instruction;
18const Register = bits.Register;
19
20instructions: std.MultiArrayList(Inst).Slice,
21
22/// The meaning of this data is determined by `Inst.Tag` value.
23extra: []const u32,
24
25pub const Inst = struct {
26 tag: Tag,
27 /// The meaning of this depends on `tag`.
28 data: Data,
29
30 pub const Tag = enum(u16) {
31 /// Pseudo-instruction: End of prologue
32 dbg_prologue_end,
33 /// Pseudo-instruction: Beginning of epilogue
34 dbg_epilogue_begin,
35 /// Pseudo-instruction: Update debug line
36 dbg_line,
37
38 // All the real instructions are ordered by their section number
39 // in The SPARC Architecture Manual, Version 9.
40
41 /// A.2 Add
42 /// This uses the arithmetic_3op field.
43 // TODO add other operations.
44 add,
45
46 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
47 /// This uses the branch_predict field.
48 bpcc,
49
50 /// A.8 Call and Link
51 /// This uses the branch_link field.
52 call,
53
54 /// A.24 Jump and Link
55 /// This uses the arithmetic_3op field.
56 jmpl,
57
58 /// A.27 Load Integer
59 /// This uses the arithmetic_3op field.
60 /// Note that the ldd variant of this instruction is deprecated, so do not emit
61 /// it unless specifically requested (e.g. by inline assembly).
62 // TODO add other operations.
63 ldub,
64 lduh,
65 lduw,
66 ldx,
67
68 /// A.31 Logical Operations
69 /// This uses the arithmetic_3op field.
70 // TODO add other operations.
71 @"or",
72
73 /// A.40 No Operation
74 /// This uses the nop field.
75 nop,
76
77 /// A.45 RETURN
78 /// This uses the arithmetic_2op field.
79 @"return",
80
81 /// A.46 SAVE and RESTORE
82 /// This uses the arithmetic_3op field.
83 save,
84 restore,
85
86 /// A.48 SETHI
87 /// This uses the sethi field.
88 sethi,
89
90 /// A.49 Shift
91 /// This uses the shift field.
92 // TODO add other operations.
93 sllx,
94
95 /// A.54 Store Integer
96 /// This uses the arithmetic_3op field.
97 /// Note that the std variant of this instruction is deprecated, so do not emit
98 /// it unless specifically requested (e.g. by inline assembly).
99 // TODO add other operations.
100 stb,
101 sth,
102 stw,
103 stx,
104
105 /// A.56 Subtract
106 /// This uses the arithmetic_3op field.
107 // TODO add other operations.
108 sub,
109
110 /// A.61 Trap on Integer Condition Codes (Tcc)
111 /// This uses the trap field.
112 tcc,
113 };
114
115 /// The position of an MIR instruction within the `Mir` instructions array.
116 pub const Index = u32;
117
118 /// All instructions have a 8-byte payload, which is contained within
119 /// this union. `Tag` determines which union field is active, as well as
120 /// how to interpret the data within.
121 // TODO this is a quick-n-dirty solution that needs to be cleaned up.
122 pub const Data = union {
123 /// Debug info: line and column
124 ///
125 /// Used by e.g. dbg_line
126 dbg_line_column: struct {
127 line: u32,
128 column: u32,
129 },
130
131 /// Two operand arithmetic.
132 /// if is_imm true then it uses the imm field of rs2_or_imm,
133 /// otherwise it uses rs2 field.
134 ///
135 /// Used by e.g. return
136 arithmetic_2op: struct {
137 is_imm: bool,
138 rs1: Register,
139 rs2_or_imm: union {
140 rs2: Register,
141 imm: i13,
142 },
143 },
144
145 /// Three operand arithmetic.
146 /// if is_imm true then it uses the imm field of rs2_or_imm,
147 /// otherwise it uses rs2 field.
148 ///
149 /// Used by e.g. add, sub
150 arithmetic_3op: struct {
151 is_imm: bool,
152 rd: Register,
153 rs1: Register,
154 rs2_or_imm: union {
155 rs2: Register,
156 imm: i13,
157 },
158 },
159
160 /// Branch and link (always unconditional).
161 /// Used by e.g. call
162 branch_link: struct {
163 inst: Index,
164 link: Register = .o7,
165 },
166
167 /// Branch with prediction.
168 /// Used by e.g. bpcc
169 branch_predict: struct {
170 annul: bool = false,
171 pt: bool = true,
172 ccr: Instruction.CCR,
173 cond: Instruction.Condition,
174 inst: Index,
175 },
176
177 /// No additional data
178 ///
179 /// Used by e.g. flushw
180 nop: void,
181
182 /// SETHI operands.
183 ///
184 /// Used by sethi
185 sethi: struct {
186 rd: Register,
187 imm: u22,
188 },
189
190 /// Shift operands.
191 /// if is_imm true then it uses the imm field of rs2_or_imm,
192 /// otherwise it uses rs2 field.
193 ///
194 /// Used by e.g. add, sub
195 shift: struct {
196 is_imm: bool,
197 width: Instruction.ShiftWidth,
198 rd: Register,
199 rs1: Register,
200 rs2_or_imm: union {
201 rs2: Register,
202 imm: u6,
203 },
204 },
205
206 /// Trap.
207 /// if is_imm true then it uses the imm field of rs2_or_imm,
208 /// otherwise it uses rs2 field.
209 ///
210 /// Used by e.g. tcc
211 trap: struct {
212 is_imm: bool = true,
213 cond: Instruction.Condition,
214 ccr: Instruction.CCR = .icc,
215 rs1: Register = .g0,
216 rs2_or_imm: union {
217 rs2: Register,
218 imm: u7,
219 },
220 },
221 };
222
223 // Make sure we don't accidentally make instructions bigger than expected.
224 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
225 comptime {
226 if (builtin.mode != .Debug) {
227 assert(@sizeOf(Data) == 8);
228 }
229 }
230};
231
232pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
233 mir.instructions.deinit(gpa);
234 gpa.free(mir.extra);
235 mir.* = undefined;
236}
237
238/// Returns the requested data, as well as the new index which is at the start of the
239/// trailers for the object.
240pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
241 const fields = std.meta.fields(T);
242 var i: usize = index;
243 var result: T = undefined;
244 inline for (fields) |field| {
245 @field(result, field.name) = switch (field.field_type) {
246 u32 => mir.extra[i],
247 i32 => @bitCast(i32, mir.extra[i]),
248 else => @compileError("bad field type"),
249 };
250 i += 1;
251 }
252 return .{
253 .data = result,
254 .end = i,
255 };
256}
src/arch/sparc64/abi.zig created+37
...@@ -0,0 +1,37 @@
1const bits = @import("bits.zig");
2const Register = bits.Register;
3
4// SPARCv9 stack constants.
5// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1.
6
7// On SPARCv9, %sp points to top of stack + stack bias,
8// and %fp points to top of previous frame + stack bias.
9pub const stack_bias = 2047;
10
11// The first 176 bytes of the stack is reserved for register saving purposes.
12// SPARCv9 requires to reserve space in the stack for the first six arguments,
13// even though they are usually passed in registers.
14pub const stack_save_area = 176;
15
16// There are no callee-preserved registers since the windowing
17// mechanism already takes care of them.
18// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling
19// something, though, as those are shared with the callee and might be
20// thrashed by it.
21pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 };
22
23// Try to allocate i, l, o, then g sets of registers, in order of priority.
24pub const allocatable_regs = [_]Register{
25 // zig fmt: off
26 .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5",
27 .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7,
28 .o0, .o1, .o2, .o3, .o4, .o5,
29 .g1, .g4, .g5,
30 // zig fmt: on
31};
32
33pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
34pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
35
36pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
37pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
src/arch/sparc64/bits.zig created+1256
...@@ -0,0 +1,1256 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6/// General purpose registers in the SPARCv9 instruction set
7pub const Register = enum(u6) {
8 // zig fmt: off
9 g0, g1, g2, g3, g4, g5, g6, g7,
10 o0, o1, o2, o3, o4, o5, o6, o7,
11 l0, l1, l2, l3, l4, l5, l6, l7,
12 @"i0", @"i1", @"i2", @"i3", @"i4", @"i5", @"i6", @"i7",
13
14 sp = 46, // stack pointer (o6)
15 fp = 62, // frame pointer (i6)
16 // zig fmt: on
17
18 pub fn id(self: Register) u5 {
19 return @truncate(u5, @enumToInt(self));
20 }
21
22 pub fn enc(self: Register) u5 {
23 // For integer registers, enc() == id().
24 return self.id();
25 }
26
27 pub fn dwarfLocOp(reg: Register) u8 {
28 return @as(u8, reg.id()) + DW.OP.reg0;
29 }
30};
31
32test "Register.id" {
33 // SP
34 try testing.expectEqual(@as(u5, 14), Register.o6.id());
35 try testing.expectEqual(Register.o6.id(), Register.sp.id());
36
37 // FP
38 try testing.expectEqual(@as(u5, 30), Register.@"i6".id());
39 try testing.expectEqual(Register.@"i6".id(), Register.fp.id());
40
41 // x0
42 try testing.expectEqual(@as(u5, 0), Register.g0.id());
43 try testing.expectEqual(@as(u5, 8), Register.o0.id());
44 try testing.expectEqual(@as(u5, 16), Register.l0.id());
45 try testing.expectEqual(@as(u5, 24), Register.@"i0".id());
46}
47
48test "Register.enc" {
49 // x0
50 try testing.expectEqual(@as(u5, 0), Register.g0.enc());
51 try testing.expectEqual(@as(u5, 8), Register.o0.enc());
52 try testing.expectEqual(@as(u5, 16), Register.l0.enc());
53 try testing.expectEqual(@as(u5, 24), Register.@"i0".enc());
54
55 // For integer registers, enc() == id().
56 try testing.expectEqual(Register.g0.enc(), Register.g0.id());
57 try testing.expectEqual(Register.o0.enc(), Register.o0.id());
58 try testing.expectEqual(Register.l0.enc(), Register.l0.id());
59 try testing.expectEqual(Register.@"i0".enc(), Register.@"i0".id());
60}
61
62/// Scalar floating point registers in the SPARCv9 instruction set
63pub const FloatingPointRegister = enum(u7) {
64 // SPARCv9 has 64 f32 registers, 32 f64 registers, and 16 f128 registers,
65 // which are aliased in this way:
66 //
67 // | %d0 | %d2 |
68 // %q0 | %f0 | %f1 | %f2 | %f3 |
69 // | %d4 | %d6 |
70 // %q4 | %f4 | %f5 | %f6 | %f7 |
71 // ...
72 // | %d60 | %d62 |
73 // %q60 | %f60 | %f61 | %f62 | %f63 |
74 //
75 // Though, since the instructions uses five-bit addressing, only %f0-%f31
76 // is usable with f32 instructions.
77
78 // zig fmt: off
79
80 // 32-bit registers
81 @"f0", @"f1", @"f2", @"f3", @"f4", @"f5", @"f6", @"f7",
82 @"f8", @"f9", @"f10", @"f11", @"f12", @"f13", @"f14", @"f15",
83 @"f16", @"f17", @"f18", @"f19", @"f20", @"f21", @"f22", @"f23",
84 @"f24", @"f25", @"f26", @"f27", @"f28", @"f29", @"f30", @"f31",
85
86 // 64-bit registers
87 d0, d2, d4, d6, d8, d10, d12, d14,
88 d16, d18, d20, d22, d24, d26, d28, d30,
89 d32, d34, d36, d38, d40, d42, d44, d46,
90 d48, d50, d52, d54, d56, d58, d60, d62,
91
92 // 128-bit registers
93 q0, q4, q8, q12, q16, q20, q24, q28,
94 q32, q36, q40, q44, q48, q52, q56, q60,
95 // zig fmt: on
96
97 pub fn id(self: FloatingPointRegister) u6 {
98 return switch (self.size()) {
99 32 => @truncate(u6, @enumToInt(self)),
100 64 => @truncate(u6, (@enumToInt(self) - 32) * 2),
101 128 => @truncate(u6, (@enumToInt(self) - 64) * 4),
102 else => unreachable,
103 };
104 }
105
106 pub fn enc(self: FloatingPointRegister) u5 {
107 // Floating point registers use an encoding scheme to map from the 6-bit
108 // ID to 5-bit encoded value.
109 // (See section 5.1.4.1 of SPARCv9 ISA specification)
110
111 const reg_id = self.id();
112 return @truncate(u5, reg_id | (reg_id >> 5));
113 }
114
115 /// Returns the bit-width of the register.
116 pub fn size(self: FloatingPointRegister) u8 {
117 return switch (@enumToInt(self)) {
118 0...31 => 32,
119 32...63 => 64,
120 64...79 => 128,
121 else => unreachable,
122 };
123 }
124};
125
126test "FloatingPointRegister.id" {
127 // Low region
128 try testing.expectEqual(@as(u6, 0), FloatingPointRegister.q0.id());
129 try testing.expectEqual(FloatingPointRegister.q0.id(), FloatingPointRegister.d0.id());
130 try testing.expectEqual(FloatingPointRegister.d0.id(), FloatingPointRegister.@"f0".id());
131
132 try testing.expectEqual(@as(u6, 28), FloatingPointRegister.q28.id());
133 try testing.expectEqual(FloatingPointRegister.q28.id(), FloatingPointRegister.d28.id());
134 try testing.expectEqual(FloatingPointRegister.d28.id(), FloatingPointRegister.@"f28".id());
135
136 // High region
137 try testing.expectEqual(@as(u6, 32), FloatingPointRegister.q32.id());
138 try testing.expectEqual(FloatingPointRegister.q32.id(), FloatingPointRegister.d32.id());
139
140 try testing.expectEqual(@as(u6, 60), FloatingPointRegister.q60.id());
141 try testing.expectEqual(FloatingPointRegister.q60.id(), FloatingPointRegister.d60.id());
142}
143
144test "FloatingPointRegister.enc" {
145 // f registers
146 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.@"f0".enc());
147 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.@"f1".enc());
148 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.@"f31".enc());
149
150 // d registers
151 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.enc());
152 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.d32.enc());
153 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d62.enc());
154
155 // q registers
156 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.enc());
157 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.q32.enc());
158 try testing.expectEqual(@as(u5, 29), FloatingPointRegister.q60.enc());
159}
160
161/// Represents an instruction in the SPARCv9 instruction set
162pub const Instruction = union(enum) {
163 // Some of the instruction formats have several minor formats, here I
164 // name them with letters since there's no official naming scheme.
165 // TODO: need to rename the minor formats to a more descriptive name.
166
167 // I am using regular structs instead of packed ones to avoid
168 // endianness-dependent behavior when constructing the actual
169 // assembly instructions.
170 // See also: https://github.com/ziglang/zig/issues/10113
171 // TODO: change it back to packed structs once the issue is resolved.
172
173 // Format 1 (op = 1): CALL
174 format_1: struct {
175 op: u2 = 0b01,
176 disp30: u30,
177 },
178
179 // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)
180 format_2a: struct {
181 op: u2 = 0b00,
182 rd: u5,
183 op2: u3,
184 imm22: u22,
185 },
186 format_2b: struct {
187 op: u2 = 0b00,
188 a: u1,
189 cond: u4,
190 op2: u3,
191 disp22: u22,
192 },
193 format_2c: struct {
194 op: u2 = 0b00,
195 a: u1,
196 cond: u4,
197 op2: u3,
198 cc1: u1,
199 cc0: u1,
200 p: u1,
201 disp19: u19,
202 },
203 format_2d: struct {
204 op: u2 = 0b00,
205 a: u1,
206 fixed: u1 = 0b0,
207 rcond: u3,
208 op2: u3,
209 d16hi: u2,
210 p: u1,
211 rs1: u5,
212 d16lo: u14,
213 },
214
215 // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store
216 format_3a: struct {
217 op: u2,
218 rd: u5,
219 op3: u6,
220 rs1: u5,
221 i: u1 = 0b0,
222 reserved: u8 = 0b00000000,
223 rs2: u5,
224 },
225 format_3b: struct {
226 op: u2,
227 rd: u5,
228 op3: u6,
229 rs1: u5,
230 i: u1 = 0b1,
231 simm13: u13,
232 },
233 format_3c: struct {
234 op: u2,
235 reserved1: u5 = 0b00000,
236 op3: u6,
237 rs1: u5,
238 i: u1 = 0b0,
239 reserved2: u8 = 0b00000000,
240 rs2: u5,
241 },
242 format_3d: struct {
243 op: u2,
244 reserved: u5 = 0b00000,
245 op3: u6,
246 rs1: u5,
247 i: u1 = 0b1,
248 simm13: u13,
249 },
250 format_3e: struct {
251 op: u2,
252 rd: u5,
253 op3: u6,
254 rs1: u5,
255 i: u1 = 0b0,
256 rcond: u3,
257 reserved: u5 = 0b00000,
258 rs2: u5,
259 },
260 format_3f: struct {
261 op: u2,
262 rd: u5,
263 op3: u6,
264 rs1: u5,
265 i: u1 = 0b1,
266 rcond: u3,
267 simm10: u10,
268 },
269 format_3g: struct {
270 op: u2,
271 rd: u5,
272 op3: u6,
273 rs1: u5,
274 // See Errata 58 of SPARCv9 specification
275 // https://sparc.org/errata-for-v9/#58
276 i: u1 = 0b1,
277 reserved: u8 = 0b00000000,
278 rs2: u5,
279 },
280 format_3h: struct {
281 op: u2 = 0b10,
282 fixed1: u5 = 0b00000,
283 op3: u6 = 0b101000,
284 fixed2: u5 = 0b01111,
285 i: u1 = 0b1,
286 reserved: u6 = 0b000000,
287 cmask: u3,
288 mmask: u4,
289 },
290 format_3i: struct {
291 op: u2,
292 rd: u5,
293 op3: u6,
294 rs1: u5,
295 i: u1 = 0b0,
296 imm_asi: u8,
297 rs2: u5,
298 },
299 format_3j: struct {
300 op: u2,
301 impl_dep1: u5,
302 op3: u6,
303 impl_dep2: u19,
304 },
305 format_3k: struct {
306 op: u2,
307 rd: u5,
308 op3: u6,
309 rs1: u5,
310 i: u1 = 0b0,
311 x: u1,
312 reserved: u7 = 0b0000000,
313 rs2: u5,
314 },
315 format_3l: struct {
316 op: u2,
317 rd: u5,
318 op3: u6,
319 rs1: u5,
320 i: u1 = 0b1,
321 x: u1 = 0b0,
322 reserved: u7 = 0b0000000,
323 shcnt32: u5,
324 },
325 format_3m: struct {
326 op: u2,
327 rd: u5,
328 op3: u6,
329 rs1: u5,
330 i: u1 = 0b1,
331 x: u1 = 0b1,
332 reserved: u6 = 0b000000,
333 shcnt64: u6,
334 },
335 format_3n: struct {
336 op: u2,
337 rd: u5,
338 op3: u6,
339 reserved: u5 = 0b00000,
340 opf: u9,
341 rs2: u5,
342 },
343 format_3o: struct {
344 op: u2,
345 fixed: u3 = 0b000,
346 cc1: u1,
347 cc0: u1,
348 op3: u6,
349 rs1: u5,
350 opf: u9,
351 rs2: u5,
352 },
353 format_3p: struct {
354 op: u2,
355 rd: u5,
356 op3: u6,
357 rs1: u5,
358 opf: u9,
359 rs2: u5,
360 },
361 format_3q: struct {
362 op: u2,
363 rd: u5,
364 op3: u6,
365 rs1: u5,
366 reserved: u14 = 0b00000000000000,
367 },
368 format_3r: struct {
369 op: u2,
370 fcn: u5,
371 op3: u6,
372 reserved: u19 = 0b0000000000000000000,
373 },
374 format_3s: struct {
375 op: u2,
376 rd: u5,
377 op3: u6,
378 reserved: u19 = 0b0000000000000000000,
379 },
380
381 //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc
382 format_4a: struct {
383 op: u2 = 0b10,
384 rd: u5,
385 op3: u6,
386 rs1: u5,
387 i: u1 = 0b0,
388 cc1: u1,
389 cc0: u1,
390 reserved: u6 = 0b000000,
391 rs2: u5,
392 },
393 format_4b: struct {
394 op: u2 = 0b10,
395 rd: u5,
396 op3: u6,
397 rs1: u5,
398 i: u1 = 0b1,
399 cc1: u1,
400 cc0: u1,
401 simm11: u11,
402 },
403 format_4c: struct {
404 op: u2 = 0b10,
405 rd: u5,
406 op3: u6,
407 cc2: u1,
408 cond: u4,
409 i: u1 = 0b0,
410 cc1: u1,
411 cc0: u1,
412 reserved: u6 = 0b000000,
413 rs2: u5,
414 },
415 format_4d: struct {
416 op: u2 = 0b10,
417 rd: u5,
418 op3: u6,
419 cc2: u1,
420 cond: u4,
421 i: u1 = 0b1,
422 cc1: u1,
423 cc0: u1,
424 simm11: u11,
425 },
426 format_4e: struct {
427 op: u2 = 0b10,
428 rd: u5,
429 op3: u6,
430 rs1: u5,
431 i: u1 = 0b1,
432 cc1: u1,
433 cc0: u1,
434 reserved: u4 = 0b0000,
435 sw_trap: u7,
436 },
437 format_4f: struct {
438 op: u2 = 0b10,
439 rd: u5,
440 op3: u6,
441 rs1: u5,
442 fixed: u1 = 0b0,
443 rcond: u3,
444 opf_low: u5,
445 rs2: u5,
446 },
447 format_4g: struct {
448 op: u2 = 0b10,
449 rd: u5,
450 op3: u6,
451 fixed: u1 = 0b0,
452 cond: u4,
453 opf_cc: u3,
454 opf_low: u6,
455 rs2: u5,
456 },
457
458 pub const CCR = enum(u3) {
459 fcc0,
460 fcc1,
461 fcc2,
462 fcc3,
463 icc,
464 reserved1,
465 xcc,
466 reserved2,
467 };
468
469 pub const RCondition = enum(u3) {
470 reserved1,
471 eq_zero,
472 le_zero,
473 lt_zero,
474 reserved2,
475 ne_zero,
476 gt_zero,
477 ge_zero,
478 };
479
480 pub const ASI = enum(u8) {
481 asi_nucleus = 0x04,
482 asi_nucleus_little = 0x0c,
483 asi_as_if_user_primary = 0x10,
484 asi_as_if_user_secondary = 0x11,
485 asi_as_if_user_primary_little = 0x18,
486 asi_as_if_user_secondary_little = 0x19,
487 asi_primary = 0x80,
488 asi_secondary = 0x81,
489 asi_primary_nofault = 0x82,
490 asi_secondary_nofault = 0x83,
491 asi_primary_little = 0x88,
492 asi_secondary_little = 0x89,
493 asi_primary_nofault_little = 0x8a,
494 asi_secondary_nofault_little = 0x8b,
495 };
496
497 pub const ShiftWidth = enum(u1) {
498 shift32,
499 shift64,
500 };
501
502 pub const MemOrderingConstraint = packed struct {
503 store_store: bool = false,
504 load_store: bool = false,
505 store_load: bool = false,
506 load_load: bool = false,
507 };
508
509 pub const MemCompletionConstraint = packed struct {
510 sync: bool = false,
511 mem_issue: bool = false,
512 lookaside: bool = false,
513 };
514
515 // TODO: Need to define an enum for `cond` values
516 // This is kinda challenging since the cond values have different meanings
517 // depending on whether it's operating on integer or FP CCR.
518 pub const Condition = u4;
519
520 pub fn toU32(self: Instruction) u32 {
521 // TODO: Remove this once packed structs work.
522 return switch (self) {
523 .format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30),
524 .format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22),
525 .format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22),
526 .format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19),
527 .format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo),
528 .format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
529 .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
530 .format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2),
531 .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
532 .format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
533 .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10),
534 .format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
535 .format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask),
536 .format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2),
537 .format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2),
538 .format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
539 .format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32),
540 .format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64),
541 .format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
542 .format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
543 .format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
544 .format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved),
545 .format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
546 .format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
547 .format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
548 .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
549 .format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
550 .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
551 .format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap),
552 .format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
553 .format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
554 };
555 }
556
557 // SPARCv9 Instruction formats.
558 // See section 6.2 of the SPARCv9 ISA manual.
559
560 fn format1(disp: i32) Instruction {
561 const udisp = @bitCast(u32, disp);
562
563 // In SPARC, branch target needs to be aligned to 4 bytes.
564 assert(udisp % 4 == 0);
565
566 // Discard the last two bits since those are implicitly zero.
567 const udisp_truncated = @truncate(u30, udisp >> 2);
568 return Instruction{
569 .format_1 = .{
570 .disp30 = udisp_truncated,
571 },
572 };
573 }
574
575 fn format2a(op2: u3, imm: u22, rd: Register) Instruction {
576 return Instruction{
577 .format_2a = .{
578 .rd = rd.enc(),
579 .op2 = op2,
580 .imm22 = imm,
581 },
582 };
583 }
584
585 fn format2b(op2: u3, cond: Condition, annul: bool, disp: i24) Instruction {
586 const udisp = @bitCast(u24, disp);
587
588 // In SPARC, branch target needs to be aligned to 4 bytes.
589 assert(udisp % 4 == 0);
590
591 // Discard the last two bits since those are implicitly zero.
592 const udisp_truncated = @truncate(u22, udisp >> 2);
593 return Instruction{
594 .format_2b = .{
595 .a = @boolToInt(annul),
596 .cond = cond,
597 .op2 = op2,
598 .disp22 = udisp_truncated,
599 },
600 };
601 }
602
603 fn format2c(op2: u3, cond: Condition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction {
604 const udisp = @bitCast(u21, disp);
605
606 // In SPARC, branch target needs to be aligned to 4 bytes.
607 assert(udisp % 4 == 0);
608
609 // Discard the last two bits since those are implicitly zero.
610 const udisp_truncated = @truncate(u19, udisp >> 2);
611
612 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
613 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
614 return Instruction{
615 .format_2c = .{
616 .a = @boolToInt(annul),
617 .cond = cond,
618 .op2 = op2,
619 .cc1 = ccr_cc1,
620 .cc0 = ccr_cc0,
621 .p = @boolToInt(pt),
622 .disp19 = udisp_truncated,
623 },
624 };
625 }
626
627 fn format2d(op2: u3, rcond: RCondition, annul: bool, pt: bool, rs1: Register, disp: i18) Instruction {
628 const udisp = @bitCast(u18, disp);
629
630 // In SPARC, branch target needs to be aligned to 4 bytes.
631 assert(udisp % 4 == 0);
632
633 // Discard the last two bits since those are implicitly zero,
634 // and split it into low and high parts.
635 const udisp_truncated = @truncate(u16, udisp >> 2);
636 const udisp_hi = @truncate(u2, (udisp_truncated & 0b1100_0000_0000_0000) >> 14);
637 const udisp_lo = @truncate(u14, udisp_truncated & 0b0011_1111_1111_1111);
638 return Instruction{
639 .format_2d = .{
640 .a = @boolToInt(annul),
641 .rcond = @enumToInt(rcond),
642 .op2 = op2,
643 .p = @boolToInt(pt),
644 .rs1 = rs1.enc(),
645 .d16hi = udisp_hi,
646 .d16lo = udisp_lo,
647 },
648 };
649 }
650
651 fn format3a(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction {
652 return Instruction{
653 .format_3a = .{
654 .op = op,
655 .rd = rd.enc(),
656 .op3 = op3,
657 .rs1 = rs1.enc(),
658 .rs2 = rs2.enc(),
659 },
660 };
661 }
662 fn format3b(op: u2, op3: u6, rs1: Register, imm: i13, rd: Register) Instruction {
663 return Instruction{
664 .format_3b = .{
665 .op = op,
666 .rd = rd.enc(),
667 .op3 = op3,
668 .rs1 = rs1.enc(),
669 .simm13 = @bitCast(u13, imm),
670 },
671 };
672 }
673 fn format3c(op: u2, op3: u6, rs1: Register, rs2: Register) Instruction {
674 return Instruction{
675 .format_3c = .{
676 .op = op,
677 .op3 = op3,
678 .rs1 = rs1.enc(),
679 .rs2 = rs2.enc(),
680 },
681 };
682 }
683 fn format3d(op: u2, op3: u6, rs1: Register, imm: i13) Instruction {
684 return Instruction{
685 .format_3d = .{
686 .op = op,
687 .op3 = op3,
688 .rs1 = rs1.enc(),
689 .simm13 = @bitCast(u13, imm),
690 },
691 };
692 }
693 fn format3e(op: u2, op3: u6, rcond: RCondition, rs1: Register, rs2: Register, rd: Register) Instruction {
694 return Instruction{
695 .format_3e = .{
696 .op = op,
697 .rd = rd.enc(),
698 .op3 = op3,
699 .rs1 = rs1.enc(),
700 .rcond = @enumToInt(rcond),
701 .rs2 = rs2.enc(),
702 },
703 };
704 }
705 fn format3f(op: u2, op3: u6, rcond: RCondition, rs1: Register, imm: i10, rd: Register) Instruction {
706 return Instruction{
707 .format_3f = .{
708 .op = op,
709 .rd = rd.enc(),
710 .op3 = op3,
711 .rs1 = rs1.enc(),
712 .rcond = @enumToInt(rcond),
713 .simm10 = @bitCast(u10, imm),
714 },
715 };
716 }
717 fn format3g(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction {
718 return Instruction{
719 .format_3g = .{
720 .op = op,
721 .rd = rd.enc(),
722 .op3 = op3,
723 .rs1 = rs1.enc(),
724 .rs2 = rs2.enc(),
725 },
726 };
727 }
728 fn format3h(cmask: MemCompletionConstraint, mmask: MemOrderingConstraint) Instruction {
729 return Instruction{
730 .format_3h = .{
731 .cmask = @bitCast(u3, cmask),
732 .mmask = @bitCast(u4, mmask),
733 },
734 };
735 }
736 fn format3i(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register, asi: ASI) Instruction {
737 return Instruction{
738 .format_3i = .{
739 .op = op,
740 .rd = rd.enc(),
741 .op3 = op3,
742 .rs1 = rs1.enc(),
743 .imm_asi = @enumToInt(asi),
744 .rs2 = rs2.enc(),
745 },
746 };
747 }
748 fn format3j(op: u2, op3: u6, impl_dep1: u5, impl_dep2: u19) Instruction {
749 return Instruction{
750 .format_3j = .{
751 .op = op,
752 .impl_dep1 = impl_dep1,
753 .op3 = op3,
754 .impl_dep2 = impl_dep2,
755 },
756 };
757 }
758 fn format3k(op: u2, op3: u6, sw: ShiftWidth, rs1: Register, rs2: Register, rd: Register) Instruction {
759 return Instruction{
760 .format_3k = .{
761 .op = op,
762 .rd = rd.enc(),
763 .op3 = op3,
764 .rs1 = rs1.enc(),
765 .x = @enumToInt(sw),
766 .rs2 = rs2.enc(),
767 },
768 };
769 }
770 fn format3l(op: u2, op3: u6, rs1: Register, shift_count: u5, rd: Register) Instruction {
771 return Instruction{
772 .format_3l = .{
773 .op = op,
774 .rd = rd.enc(),
775 .op3 = op3,
776 .rs1 = rs1.enc(),
777 .shcnt32 = shift_count,
778 },
779 };
780 }
781 fn format3m(op: u2, op3: u6, rs1: Register, shift_count: u6, rd: Register) Instruction {
782 return Instruction{
783 .format_3m = .{
784 .op = op,
785 .rd = rd.enc(),
786 .op3 = op3,
787 .rs1 = rs1.enc(),
788 .shcnt64 = shift_count,
789 },
790 };
791 }
792 fn format3n(op: u2, op3: u6, opf: u9, rs2: Register, rd: Register) Instruction {
793 return Instruction{
794 .format_3n = .{
795 .op = op,
796 .rd = rd.enc(),
797 .op3 = op3,
798 .opf = opf,
799 .rs2 = rs2.enc(),
800 },
801 };
802 }
803 fn format3o(op: u2, op3: u6, opf: u9, ccr: CCR, rs1: Register, rs2: Register) Instruction {
804 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
805 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
806 return Instruction{
807 .format_3o = .{
808 .op = op,
809 .cc1 = ccr_cc1,
810 .cc0 = ccr_cc0,
811 .op3 = op3,
812 .rs1 = rs1.enc(),
813 .opf = opf,
814 .rs2 = rs2.enc(),
815 },
816 };
817 }
818 fn format3p(op: u2, op3: u6, opf: u9, rs1: Register, rs2: Register, rd: Register) Instruction {
819 return Instruction{
820 .format_3p = .{
821 .op = op,
822 .rd = rd.enc(),
823 .op3 = op3,
824 .rs1 = rs1.enc(),
825 .opf = opf,
826 .rs2 = rs2.enc(),
827 },
828 };
829 }
830 fn format3q(op: u2, op3: u6, rs1: Register, rd: Register) Instruction {
831 return Instruction{
832 .format_3q = .{
833 .op = op,
834 .rd = rd.enc(),
835 .op3 = op3,
836 .rs1 = rs1.enc(),
837 },
838 };
839 }
840 fn format3r(op: u2, op3: u6, fcn: u5) Instruction {
841 return Instruction{
842 .format_3r = .{
843 .op = op,
844 .fcn = fcn,
845 .op3 = op3,
846 },
847 };
848 }
849 fn format3s(op: u2, op3: u6, rd: Register) Instruction {
850 return Instruction{
851 .format_3s = .{
852 .op = op,
853 .rd = rd.enc(),
854 .op3 = op3,
855 },
856 };
857 }
858
859 fn format4a(op3: u6, ccr: CCR, rs1: Register, rs2: Register, rd: Register) Instruction {
860 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
861 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
862 return Instruction{
863 .format_4a = .{
864 .rd = rd.enc(),
865 .op3 = op3,
866 .rs1 = rs1.enc(),
867 .cc1 = ccr_cc1,
868 .cc0 = ccr_cc0,
869 .rs2 = rs2.enc(),
870 },
871 };
872 }
873
874 fn format4b(op3: u6, ccr: CCR, rs1: Register, imm: i11, rd: Register) Instruction {
875 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
876 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
877 return Instruction{
878 .format_4b = .{
879 .rd = rd.enc(),
880 .op3 = op3,
881 .rs1 = rs1.enc(),
882 .cc1 = ccr_cc1,
883 .cc0 = ccr_cc0,
884 .simm11 = @bitCast(u11, imm),
885 },
886 };
887 }
888
889 fn format4c(op3: u6, cond: Condition, ccr: CCR, rs2: Register, rd: Register) Instruction {
890 const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2);
891 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
892 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
893 return Instruction{
894 .format_4c = .{
895 .rd = rd.enc(),
896 .op3 = op3,
897 .cc2 = ccr_cc2,
898 .cond = cond,
899 .cc1 = ccr_cc1,
900 .cc0 = ccr_cc0,
901 .rs2 = rs2.enc(),
902 },
903 };
904 }
905
906 fn format4d(op3: u6, cond: Condition, ccr: CCR, imm: i11, rd: Register) Instruction {
907 const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2);
908 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
909 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
910 return Instruction{
911 .format_4d = .{
912 .rd = rd.enc(),
913 .op3 = op3,
914 .cc2 = ccr_cc2,
915 .cond = cond,
916 .cc1 = ccr_cc1,
917 .cc0 = ccr_cc0,
918 .simm11 = @bitCast(u11, imm),
919 },
920 };
921 }
922
923 fn format4e(op3: u6, ccr: CCR, rs1: Register, rd: Register, sw_trap: u7) Instruction {
924 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
925 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
926 return Instruction{
927 .format_4e = .{
928 .rd = rd.enc(),
929 .op3 = op3,
930 .rs1 = rs1.enc(),
931 .cc1 = ccr_cc1,
932 .cc0 = ccr_cc0,
933 .sw_trap = sw_trap,
934 },
935 };
936 }
937
938 fn format4f(
939 op3: u6,
940 opf_low: u5,
941 rcond: RCondition,
942 rs1: Register,
943 rs2: Register,
944 rd: Register,
945 ) Instruction {
946 return Instruction{
947 .format_4f = .{
948 .rd = rd.enc(),
949 .op3 = op3,
950 .rs1 = rs1.enc(),
951 .rcond = @enumToInt(rcond),
952 .opf_low = opf_low,
953 .rs2 = rs2.enc(),
954 },
955 };
956 }
957
958 fn format4g(op3: u6, opf_low: u6, opf_cc: u3, cond: Condition, rs2: Register, rd: Register) Instruction {
959 return Instruction{
960 .format_4g = .{
961 .rd = rd.enc(),
962 .op3 = op3,
963 .cond = cond,
964 .opf_cc = opf_cc,
965 .opf_low = opf_low,
966 .rs2 = rs2.enc(),
967 },
968 };
969 }
970
971 // SPARCv9 Instruction definition.
972 // See appendix A of the SPARCv9 ISA manual.
973
974 pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
975 return switch (s2) {
976 Register => format3a(0b10, 0b00_0000, rs1, rs2, rd),
977 i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd),
978 else => unreachable,
979 };
980 }
981
982 pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
983 return switch (s2) {
984 Register => format3a(0b10, 0b11_1000, rs1, rs2, rd),
985 i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd),
986 else => unreachable,
987 };
988 }
989
990 pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
991 return switch (s2) {
992 Register => format3a(0b11, 0b00_0001, rs1, rs2, rd),
993 i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd),
994 else => unreachable,
995 };
996 }
997
998 pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
999 return switch (s2) {
1000 Register => format3a(0b11, 0b00_0010, rs1, rs2, rd),
1001 i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd),
1002 else => unreachable,
1003 };
1004 }
1005
1006 pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1007 return switch (s2) {
1008 Register => format3a(0b11, 0b00_0000, rs1, rs2, rd),
1009 i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd),
1010 else => unreachable,
1011 };
1012 }
1013
1014 pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1015 return switch (s2) {
1016 Register => format3a(0b11, 0b00_1011, rs1, rs2, rd),
1017 i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd),
1018 else => unreachable,
1019 };
1020 }
1021
1022 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1023 return switch (s2) {
1024 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
1025 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
1026 else => unreachable,
1027 };
1028 }
1029
1030 pub fn nop() Instruction {
1031 return sethi(0, .g0);
1032 }
1033
1034 pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction {
1035 return switch (s2) {
1036 Register => format3c(0b10, 0b11_1001, rs1, rs2),
1037 i13 => format3d(0b10, 0b11_1001, rs1, rs2),
1038 else => unreachable,
1039 };
1040 }
1041
1042 pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1043 return switch (s2) {
1044 Register => format3a(0b10, 0b11_1100, rs1, rs2, rd),
1045 i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd),
1046 else => unreachable,
1047 };
1048 }
1049
1050 pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1051 return switch (s2) {
1052 Register => format3a(0b10, 0b11_1101, rs1, rs2, rd),
1053 i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd),
1054 else => unreachable,
1055 };
1056 }
1057
1058 pub fn sethi(imm: u22, rd: Register) Instruction {
1059 return format2a(0b100, imm, rd);
1060 }
1061
1062 pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1063 return switch (s2) {
1064 Register => format3a(0b11, 0b00_0101, rs1, rs2, rd),
1065 i13 => format3b(0b11, 0b00_0101, rs1, rs2, rd),
1066 else => unreachable,
1067 };
1068 }
1069
1070 pub fn sth(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1071 return switch (s2) {
1072 Register => format3a(0b11, 0b00_0110, rs1, rs2, rd),
1073 i13 => format3b(0b11, 0b00_0110, rs1, rs2, rd),
1074 else => unreachable,
1075 };
1076 }
1077
1078 pub fn stw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1079 return switch (s2) {
1080 Register => format3a(0b11, 0b00_0100, rs1, rs2, rd),
1081 i13 => format3b(0b11, 0b00_0100, rs1, rs2, rd),
1082 else => unreachable,
1083 };
1084 }
1085
1086 pub fn stx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1087 return switch (s2) {
1088 Register => format3a(0b11, 0b00_1110, rs1, rs2, rd),
1089 i13 => format3b(0b11, 0b00_1110, rs1, rs2, rd),
1090 else => unreachable,
1091 };
1092 }
1093
1094 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1095 return switch (s2) {
1096 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
1097 i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd),
1098 else => unreachable,
1099 };
1100 }
1101
1102 pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
1103 // Tcc instructions abuse the rd field to store the conditionals.
1104 return switch (s2) {
1105 Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
1106 u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2),
1107 else => unreachable,
1108 };
1109 }
1110};
1111
1112test "Serialize formats" {
1113 const Testcase = struct {
1114 inst: Instruction,
1115 expected: u32,
1116 };
1117
1118 // Note that the testcases might or might not be a valid instruction
1119 // This is mostly just to check the behavior of the format packed structs
1120 // since currently stage1 doesn't properly implement it in all cases
1121 const testcases = [_]Testcase{
1122 .{
1123 .inst = Instruction.format1(4),
1124 .expected = 0b01_000000000000000000000000000001,
1125 },
1126 .{
1127 .inst = Instruction.format2a(4, 0, .g0),
1128 .expected = 0b00_00000_100_0000000000000000000000,
1129 },
1130 .{
1131 .inst = Instruction.format2b(6, 3, true, -4),
1132 .expected = 0b00_1_0011_110_1111111111111111111111,
1133 },
1134 .{
1135 .inst = Instruction.format2c(3, 0, false, true, .xcc, 8),
1136 .expected = 0b00_0_0000_011_1_0_1_0000000000000000010,
1137 },
1138 .{
1139 .inst = Instruction.format2d(7, .eq_zero, false, true, .o0, 20),
1140 .expected = 0b00_0_0_001_111_00_1_01000_00000000000101,
1141 },
1142 .{
1143 .inst = Instruction.format3a(3, 5, .g0, .o1, .l2),
1144 .expected = 0b11_10010_000101_00000_0_00000000_01001,
1145 },
1146 .{
1147 .inst = Instruction.format3b(3, 5, .g0, -1, .l2),
1148 .expected = 0b11_10010_000101_00000_1_1111111111111,
1149 },
1150 .{
1151 .inst = Instruction.format3c(3, 5, .g0, .o1),
1152 .expected = 0b11_00000_000101_00000_0_00000000_01001,
1153 },
1154 .{
1155 .inst = Instruction.format3d(3, 5, .g0, 0),
1156 .expected = 0b11_00000_000101_00000_1_0000000000000,
1157 },
1158 .{
1159 .inst = Instruction.format3e(3, 5, .ne_zero, .g0, .o1, .l2),
1160 .expected = 0b11_10010_000101_00000_0_101_00000_01001,
1161 },
1162 .{
1163 .inst = Instruction.format3f(3, 5, .ne_zero, .g0, -1, .l2),
1164 .expected = 0b11_10010_000101_00000_1_101_1111111111,
1165 },
1166 .{
1167 .inst = Instruction.format3g(3, 5, .g0, .o1, .l2),
1168 .expected = 0b11_10010_000101_00000_1_00000000_01001,
1169 },
1170 .{
1171 .inst = Instruction.format3h(.{}, .{}),
1172 .expected = 0b10_00000_101000_01111_1_000000_000_0000,
1173 },
1174 .{
1175 .inst = Instruction.format3i(3, 5, .g0, .o1, .l2, .asi_primary_little),
1176 .expected = 0b11_10010_000101_00000_0_10001000_01001,
1177 },
1178 .{
1179 .inst = Instruction.format3j(3, 5, 31, 0),
1180 .expected = 0b11_11111_000101_0000000000000000000,
1181 },
1182 .{
1183 .inst = Instruction.format3k(3, 5, .shift32, .g0, .o1, .l2),
1184 .expected = 0b11_10010_000101_00000_0_0_0000000_01001,
1185 },
1186 .{
1187 .inst = Instruction.format3l(3, 5, .g0, 31, .l2),
1188 .expected = 0b11_10010_000101_00000_1_0_0000000_11111,
1189 },
1190 .{
1191 .inst = Instruction.format3m(3, 5, .g0, 63, .l2),
1192 .expected = 0b11_10010_000101_00000_1_1_000000_111111,
1193 },
1194 .{
1195 .inst = Instruction.format3n(3, 5, 0, .o1, .l2),
1196 .expected = 0b11_10010_000101_00000_000000000_01001,
1197 },
1198 .{
1199 .inst = Instruction.format3o(3, 5, 0, .xcc, .o1, .l2),
1200 .expected = 0b11_000_1_0_000101_01001_000000000_10010,
1201 },
1202 .{
1203 .inst = Instruction.format3p(3, 5, 0, .g0, .o1, .l2),
1204 .expected = 0b11_10010_000101_00000_000000000_01001,
1205 },
1206 .{
1207 .inst = Instruction.format3q(3, 5, .g0, .o1),
1208 .expected = 0b11_01001_000101_00000_00000000000000,
1209 },
1210 .{
1211 .inst = Instruction.format3r(3, 5, 4),
1212 .expected = 0b11_00100_000101_0000000000000000000,
1213 },
1214 .{
1215 .inst = Instruction.format3s(3, 5, .g0),
1216 .expected = 0b11_00000_000101_0000000000000000000,
1217 },
1218 .{
1219 .inst = Instruction.format4a(8, .xcc, .g0, .o1, .l2),
1220 .expected = 0b10_10010_001000_00000_0_1_0_000000_01001,
1221 },
1222 .{
1223 .inst = Instruction.format4b(8, .xcc, .g0, -1, .l2),
1224 .expected = 0b10_10010_001000_00000_1_1_0_11111111111,
1225 },
1226 .{
1227 .inst = Instruction.format4c(8, 0, .xcc, .g0, .o1),
1228 .expected = 0b10_01001_001000_1_0000_0_1_0_000000_00000,
1229 },
1230 .{
1231 .inst = Instruction.format4d(8, 0, .xcc, 0, .l2),
1232 .expected = 0b10_10010_001000_1_0000_1_1_0_00000000000,
1233 },
1234 .{
1235 .inst = Instruction.format4e(8, .xcc, .g0, .o1, 0),
1236 .expected = 0b10_01001_001000_00000_1_1_0_0000_0000000,
1237 },
1238 .{
1239 .inst = Instruction.format4f(8, 4, .eq_zero, .g0, .o1, .l2),
1240 .expected = 0b10_10010_001000_00000_0_001_00100_01001,
1241 },
1242 .{
1243 .inst = Instruction.format4g(8, 4, 2, 0, .o1, .l2),
1244 .expected = 0b10_10010_001000_0_0000_010_000100_01001,
1245 },
1246 };
1247
1248 for (testcases) |case| {
1249 const actual = case.inst.toU32();
1250 testing.expectEqual(case.expected, actual) catch |err| {
1251 std.debug.print("error: {x}\n", .{err});
1252 std.debug.print("case: {x}\n", .{case});
1253 return err;
1254 };
1255 }
1256}
src/arch/sparcv9/CodeGen.zig deleted-1791
...@@ -1,1791 +0,0 @@
1//! SPARCv9 codegen.
2//! This lowers AIR into MIR.
3//! For now this only implements medium/low code model with absolute addressing.
4//! TODO add support for other code models.
5const std = @import("std");
6const assert = std.debug.assert;
7const log = std.log.scoped(.codegen);
8const math = std.math;
9const mem = std.mem;
10const Allocator = mem.Allocator;
11const builtin = @import("builtin");
12const link = @import("../../link.zig");
13const Module = @import("../../Module.zig");
14const TypedValue = @import("../../TypedValue.zig");
15const ErrorMsg = Module.ErrorMsg;
16const Air = @import("../../Air.zig");
17const Mir = @import("Mir.zig");
18const Emit = @import("Emit.zig");
19const Liveness = @import("../../Liveness.zig");
20const Type = @import("../../type.zig").Type;
21const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
22const FnResult = @import("../../codegen.zig").FnResult;
23const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
24const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
25const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs);
26
27const build_options = @import("build_options");
28
29const bits = @import("bits.zig");
30const abi = @import("abi.zig");
31const Register = bits.Register;
32
33const Self = @This();
34
35const InnerError = error{
36 OutOfMemory,
37 CodegenFail,
38 OutOfRegisters,
39};
40
41const RegisterView = enum(u1) {
42 caller,
43 callee,
44};
45
46gpa: Allocator,
47air: Air,
48liveness: Liveness,
49bin_file: *link.File,
50target: *const std.Target,
51mod_fn: *const Module.Fn,
52code: *std.ArrayList(u8),
53debug_output: DebugInfoOutput,
54err_msg: ?*ErrorMsg,
55args: []MCValue,
56ret_mcv: MCValue,
57fn_type: Type,
58arg_index: usize,
59src_loc: Module.SrcLoc,
60stack_align: u32,
61
62/// MIR Instructions
63mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
64/// MIR extra data
65mir_extra: std.ArrayListUnmanaged(u32) = .{},
66
67/// Byte offset within the source file of the ending curly.
68end_di_line: u32,
69end_di_column: u32,
70
71/// The value is an offset into the `Function` `code` from the beginning.
72/// To perform the reloc, write 32-bit signed little-endian integer
73/// which is a relative jump, based on the address following the reloc.
74exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
75
76/// Whenever there is a runtime branch, we push a Branch onto this stack,
77/// and pop it off when the runtime branch joins. This provides an "overlay"
78/// of the table of mappings from instructions to `MCValue` from within the branch.
79/// This way we can modify the `MCValue` for an instruction in different ways
80/// within different branches. Special consideration is needed when a branch
81/// joins with its parent, to make sure all instructions have the same MCValue
82/// across each runtime branch upon joining.
83branch_stack: *std.ArrayList(Branch),
84
85// Key is the block instruction
86blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
87
88register_manager: RegisterManager = .{},
89
90/// Maps offset to what is stored there.
91stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
92
93/// Offset from the stack base, representing the end of the stack frame.
94max_end_stack: u32 = 0,
95/// Represents the current end stack offset. If there is no existing slot
96/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
97next_stack_offset: u32 = 0,
98
99/// Debug field, used to find bugs in the compiler.
100air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
101
102const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
103
104const MCValue = union(enum) {
105 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
106 /// TODO Look into deleting this tag and using `dead` instead, since every use
107 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
108 none,
109 /// Control flow will not allow this value to be observed.
110 unreach,
111 /// No more references to this value remain.
112 dead,
113 /// The value is undefined.
114 undef,
115 /// A pointer-sized integer that fits in a register.
116 /// If the type is a pointer, this is the pointer address in virtual address space.
117 immediate: u64,
118 /// The value is in a target-specific register.
119 register: Register,
120 /// The value is in memory at a hard-coded address.
121 /// If the type is a pointer, it means the pointer address is at this memory location.
122 memory: u64,
123 /// The value is one of the stack variables.
124 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
125 stack_offset: u32,
126 /// The value is a pointer to one of the stack variables (payload is stack offset).
127 ptr_stack_offset: u32,
128
129 fn isMemory(mcv: MCValue) bool {
130 return switch (mcv) {
131 .memory, .stack_offset => true,
132 else => false,
133 };
134 }
135
136 fn isImmediate(mcv: MCValue) bool {
137 return switch (mcv) {
138 .immediate => true,
139 else => false,
140 };
141 }
142
143 fn isMutable(mcv: MCValue) bool {
144 return switch (mcv) {
145 .none => unreachable,
146 .unreach => unreachable,
147 .dead => unreachable,
148
149 .immediate,
150 .memory,
151 .ptr_stack_offset,
152 .undef,
153 => false,
154
155 .register,
156 .stack_offset,
157 => true,
158 };
159 }
160};
161
162const Branch = struct {
163 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
164
165 fn deinit(self: *Branch, gpa: Allocator) void {
166 self.inst_table.deinit(gpa);
167 self.* = undefined;
168 }
169};
170
171const StackAllocation = struct {
172 inst: Air.Inst.Index,
173 /// TODO do we need size? should be determined by inst.ty.abiSize()
174 size: u32,
175};
176
177const BlockData = struct {
178 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
179 /// The first break instruction encounters `null` here and chooses a
180 /// machine code value for the block result, populating this field.
181 /// Following break instructions encounter that value and use it for
182 /// the location to store their block results.
183 mcv: MCValue,
184};
185
186const CallMCValues = struct {
187 args: []MCValue,
188 return_value: MCValue,
189 stack_byte_count: u32,
190 stack_align: u32,
191
192 fn deinit(self: *CallMCValues, func: *Self) void {
193 func.gpa.free(self.args);
194 self.* = undefined;
195 }
196};
197
198const BigTomb = struct {
199 function: *Self,
200 inst: Air.Inst.Index,
201 tomb_bits: Liveness.Bpi,
202 big_tomb_bits: u32,
203 bit_index: usize,
204
205 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
206 const this_bit_index = bt.bit_index;
207 bt.bit_index += 1;
208
209 const op_int = @enumToInt(op_ref);
210 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
211 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
212
213 if (this_bit_index < Liveness.bpi - 1) {
214 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
215 if (!dies) return;
216 } else {
217 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
218 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
219 if (!dies) return;
220 }
221 bt.function.processDeath(op_index);
222 }
223
224 fn finishAir(bt: *BigTomb, result: MCValue) void {
225 const is_used = !bt.function.liveness.isUnused(bt.inst);
226 if (is_used) {
227 log.debug("%{d} => {}", .{ bt.inst, result });
228 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
229 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
230 }
231 bt.function.finishAirBookkeeping();
232 }
233};
234
235pub fn generate(
236 bin_file: *link.File,
237 src_loc: Module.SrcLoc,
238 module_fn: *Module.Fn,
239 air: Air,
240 liveness: Liveness,
241 code: *std.ArrayList(u8),
242 debug_output: DebugInfoOutput,
243) GenerateSymbolError!FnResult {
244 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
245 @panic("Attempted to compile for architecture that was disabled by build configuration");
246 }
247
248 const mod = bin_file.options.module.?;
249 const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
250 assert(fn_owner_decl.has_tv);
251 const fn_type = fn_owner_decl.ty;
252
253 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
254 defer {
255 assert(branch_stack.items.len == 1);
256 branch_stack.items[0].deinit(bin_file.allocator);
257 branch_stack.deinit();
258 }
259 try branch_stack.append(.{});
260
261 var function = Self{
262 .gpa = bin_file.allocator,
263 .air = air,
264 .liveness = liveness,
265 .target = &bin_file.options.target,
266 .bin_file = bin_file,
267 .mod_fn = module_fn,
268 .code = code,
269 .debug_output = debug_output,
270 .err_msg = null,
271 .args = undefined, // populated after `resolveCallingConventionValues`
272 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
273 .fn_type = fn_type,
274 .arg_index = 0,
275 .branch_stack = &branch_stack,
276 .src_loc = src_loc,
277 .stack_align = undefined,
278 .end_di_line = module_fn.rbrace_line,
279 .end_di_column = module_fn.rbrace_column,
280 };
281 defer function.stack.deinit(bin_file.allocator);
282 defer function.blocks.deinit(bin_file.allocator);
283 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
284
285 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
286 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
287 error.OutOfRegisters => return FnResult{
288 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
289 },
290 else => |e| return e,
291 };
292 defer call_info.deinit(&function);
293
294 function.args = call_info.args;
295 function.ret_mcv = call_info.return_value;
296 function.stack_align = call_info.stack_align;
297 function.max_end_stack = call_info.stack_byte_count;
298
299 function.gen() catch |err| switch (err) {
300 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
301 error.OutOfRegisters => return FnResult{
302 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
303 },
304 else => |e| return e,
305 };
306
307 var mir = Mir{
308 .instructions = function.mir_instructions.toOwnedSlice(),
309 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
310 };
311 defer mir.deinit(bin_file.allocator);
312
313 var emit = Emit{
314 .mir = mir,
315 .bin_file = bin_file,
316 .debug_output = debug_output,
317 .target = &bin_file.options.target,
318 .src_loc = src_loc,
319 .code = code,
320 .prev_di_pc = 0,
321 .prev_di_line = module_fn.lbrace_line,
322 .prev_di_column = module_fn.lbrace_column,
323 };
324 defer emit.deinit();
325
326 emit.emitMir() catch |err| switch (err) {
327 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
328 else => |e| return e,
329 };
330
331 if (function.err_msg) |em| {
332 return FnResult{ .fail = em };
333 } else {
334 return FnResult{ .appended = {} };
335 }
336}
337
338fn gen(self: *Self) !void {
339 const cc = self.fn_type.fnCallingConvention();
340 if (cc != .Naked) {
341 // TODO Finish function prologue and epilogue for sparcv9.
342
343 // save %sp, stack_save_area, %sp
344 const save_inst = try self.addInst(.{
345 .tag = .save,
346 .data = .{
347 .arithmetic_3op = .{
348 .is_imm = true,
349 .rd = .sp,
350 .rs1 = .sp,
351 .rs2_or_imm = .{ .imm = -abi.stack_save_area },
352 },
353 },
354 });
355
356 _ = try self.addInst(.{
357 .tag = .dbg_prologue_end,
358 .data = .{ .nop = {} },
359 });
360
361 try self.genBody(self.air.getMainBody());
362
363 _ = try self.addInst(.{
364 .tag = .dbg_epilogue_begin,
365 .data = .{ .nop = {} },
366 });
367
368 // exitlude jumps
369 if (self.exitlude_jump_relocs.items.len > 0 and
370 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
371 {
372 // If the last Mir instruction (apart from the
373 // dbg_epilogue_begin) is the last exitlude jump
374 // relocation (which would just jump one instruction
375 // further), it can be safely removed
376 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());
377 }
378
379 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
380 _ = jmp_reloc;
381 return self.fail("TODO add branches in sparcv9", .{});
382 }
383
384 // Backpatch stack offset
385 const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space;
386 const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
387 if (math.cast(i13, stack_size)) |size| {
388 self.mir_instructions.set(save_inst, .{
389 .tag = .save,
390 .data = .{
391 .arithmetic_3op = .{
392 .is_imm = true,
393 .rd = .sp,
394 .rs1 = .sp,
395 .rs2_or_imm = .{ .imm = -size },
396 },
397 },
398 });
399 } else |_| {
400 // TODO for large stacks, replace the prologue with:
401 // setx stack_size, %g1
402 // save %sp, %g1, %sp
403 return self.fail("TODO SPARCv9: allow larger stacks", .{});
404 }
405
406 // return %i7 + 8
407 _ = try self.addInst(.{
408 .tag = .@"return",
409 .data = .{
410 .arithmetic_2op = .{
411 .is_imm = true,
412 .rs1 = .@"i7",
413 .rs2_or_imm = .{ .imm = 8 },
414 },
415 },
416 });
417
418 // Branches in SPARC have a delay slot, that is, the instruction
419 // following it will unconditionally be executed.
420 // See: Section 3.2.3 Control Transfer in SPARCv9 manual.
421 // See also: https://arcb.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/delaybra.html
422 // TODO Find a way to fill this delay slot
423 // nop
424 _ = try self.addInst(.{
425 .tag = .nop,
426 .data = .{ .nop = {} },
427 });
428 } else {
429 _ = try self.addInst(.{
430 .tag = .dbg_prologue_end,
431 .data = .{ .nop = {} },
432 });
433
434 try self.genBody(self.air.getMainBody());
435
436 _ = try self.addInst(.{
437 .tag = .dbg_epilogue_begin,
438 .data = .{ .nop = {} },
439 });
440 }
441
442 // Drop them off at the rbrace.
443 _ = try self.addInst(.{
444 .tag = .dbg_line,
445 .data = .{ .dbg_line_column = .{
446 .line = self.end_di_line,
447 .column = self.end_di_column,
448 } },
449 });
450}
451
452fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
453 const air_tags = self.air.instructions.items(.tag);
454
455 for (body) |inst| {
456 const old_air_bookkeeping = self.air_bookkeeping;
457 try self.ensureProcessDeathCapacity(Liveness.bpi);
458
459 switch (air_tags[inst]) {
460 // zig fmt: off
461 .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"),
462 .addwrap => @panic("TODO try self.airAddWrap(inst)"),
463 .add_sat => @panic("TODO try self.airAddSat(inst)"),
464 .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"),
465 .subwrap => @panic("TODO try self.airSubWrap(inst)"),
466 .sub_sat => @panic("TODO try self.airSubSat(inst)"),
467 .mul => @panic("TODO try self.airMul(inst)"),
468 .mulwrap => @panic("TODO try self.airMulWrap(inst)"),
469 .mul_sat => @panic("TODO try self.airMulSat(inst)"),
470 .rem => @panic("TODO try self.airRem(inst)"),
471 .mod => @panic("TODO try self.airMod(inst)"),
472 .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
473 .shl_sat => @panic("TODO try self.airShlSat(inst)"),
474 .min => @panic("TODO try self.airMin(inst)"),
475 .max => @panic("TODO try self.airMax(inst)"),
476 .slice => @panic("TODO try self.airSlice(inst)"),
477
478 .sqrt,
479 .sin,
480 .cos,
481 .tan,
482 .exp,
483 .exp2,
484 .log,
485 .log2,
486 .log10,
487 .fabs,
488 .floor,
489 .ceil,
490 .round,
491 .trunc_float,
492 => @panic("TODO try self.airUnaryMath(inst)"),
493
494 .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"),
495 .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"),
496 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
497 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
498
499 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
500
501 .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"),
502 .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"),
503 .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"),
504 .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"),
505 .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"),
506 .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"),
507 .cmp_vector => @panic("TODO try self.airCmpVector(inst)"),
508 .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"),
509
510 .bool_and => @panic("TODO try self.airBoolOp(inst)"),
511 .bool_or => @panic("TODO try self.airBoolOp(inst)"),
512 .bit_and => @panic("TODO try self.airBitAnd(inst)"),
513 .bit_or => @panic("TODO try self.airBitOr(inst)"),
514 .xor => @panic("TODO try self.airXor(inst)"),
515 .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
516
517 .alloc => @panic("TODO try self.airAlloc(inst)"),
518 .ret_ptr => try self.airRetPtr(inst),
519 .arg => try self.airArg(inst),
520 .assembly => try self.airAsm(inst),
521 .bitcast => @panic("TODO try self.airBitCast(inst)"),
522 .block => try self.airBlock(inst),
523 .br => @panic("TODO try self.airBr(inst)"),
524 .breakpoint => try self.airBreakpoint(),
525 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
526 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
527 .fence => @panic("TODO try self.airFence()"),
528 .cond_br => @panic("TODO try self.airCondBr(inst)"),
529 .dbg_stmt => try self.airDbgStmt(inst),
530 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
531 .fpext => @panic("TODO try self.airFpext(inst)"),
532 .intcast => @panic("TODO try self.airIntCast(inst)"),
533 .trunc => @panic("TODO try self.airTrunc(inst)"),
534 .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"),
535 .is_non_null => @panic("TODO try self.airIsNonNull(inst)"),
536 .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"),
537 .is_null => @panic("TODO try self.airIsNull(inst)"),
538 .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"),
539 .is_non_err => @panic("TODO try self.airIsNonErr(inst)"),
540 .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"),
541 .is_err => @panic("TODO try self.airIsErr(inst)"),
542 .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"),
543 .load => @panic("TODO try self.airLoad(inst)"),
544 .loop => @panic("TODO try self.airLoop(inst)"),
545 .not => @panic("TODO try self.airNot(inst)"),
546 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
547 .ret => try self.airRet(inst),
548 .ret_load => try self.airRetLoad(inst),
549 .store => try self.airStore(inst),
550 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
551 .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"),
552 .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"),
553 .int_to_float => @panic("TODO try self.airIntToFloat(inst)"),
554 .float_to_int => @panic("TODO try self.airFloatToInt(inst)"),
555 .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"),
556 .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"),
557 .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"),
558 .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"),
559 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
560 .memset => @panic("TODO try self.airMemset(inst)"),
561 .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"),
562 .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"),
563 .clz => @panic("TODO try self.airClz(inst)"),
564 .ctz => @panic("TODO try self.airCtz(inst)"),
565 .popcount => @panic("TODO try self.airPopcount(inst)"),
566 .byte_swap => @panic("TODO try self.airByteSwap(inst)"),
567 .bit_reverse => @panic("TODO try self.airBitReverse(inst)"),
568 .tag_name => @panic("TODO try self.airTagName(inst)"),
569 .error_name => @panic("TODO try self.airErrorName(inst)"),
570 .splat => @panic("TODO try self.airSplat(inst)"),
571 .select => @panic("TODO try self.airSelect(inst)"),
572 .shuffle => @panic("TODO try self.airShuffle(inst)"),
573 .reduce => @panic("TODO try self.airReduce(inst)"),
574 .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"),
575 .union_init => @panic("TODO try self.airUnionInit(inst)"),
576 .prefetch => @panic("TODO try self.airPrefetch(inst)"),
577 .mul_add => @panic("TODO try self.airMulAdd(inst)"),
578
579 .dbg_var_ptr,
580 .dbg_var_val,
581 => try self.airDbgVar(inst),
582
583 .dbg_inline_begin,
584 .dbg_inline_end,
585 => try self.airDbgInline(inst),
586
587 .dbg_block_begin,
588 .dbg_block_end,
589 => try self.airDbgBlock(inst),
590
591 .call => try self.airCall(inst, .auto),
592 .call_always_tail => try self.airCall(inst, .always_tail),
593 .call_never_tail => try self.airCall(inst, .never_tail),
594 .call_never_inline => try self.airCall(inst, .never_inline),
595
596 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
597 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
598 .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"),
599 .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"),
600
601 .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"),
602 .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"),
603 .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"),
604 .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"),
605
606 .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"),
607
608 .switch_br => try self.airSwitch(inst),
609 .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"),
610 .slice_len => @panic("TODO try self.airSliceLen(inst)"),
611
612 .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"),
613 .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"),
614
615 .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"),
616 .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"),
617 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
618 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
619 .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"),
620
621 .constant => unreachable, // excluded from function bodies
622 .const_ty => unreachable, // excluded from function bodies
623 .unreach => self.finishAirBookkeeping(),
624
625 .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"),
626 .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"),
627 .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"),
628 .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"),
629 .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"),
630 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
631 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
632 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
633
634 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
635 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
636 .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"),
637
638 .wasm_memory_size => unreachable,
639 .wasm_memory_grow => unreachable,
640 // zig fmt: on
641 }
642
643 if (std.debug.runtime_safety) {
644 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
645 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
646 }
647 }
648 }
649}
650
651fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
652 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
653 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
654 const is_volatile = (extra.data.flags & 0x80000000) != 0;
655 const clobbers_len = @truncate(u31, extra.data.flags);
656 var extra_i: usize = extra.end;
657 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]);
658 extra_i += outputs.len;
659 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]);
660 extra_i += inputs.len;
661
662 const dead = !is_volatile and self.liveness.isUnused(inst);
663 const result: MCValue = if (dead) .dead else result: {
664 if (outputs.len > 1) {
665 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
666 }
667
668 const output_constraint: ?[]const u8 = for (outputs) |output| {
669 if (output != .none) {
670 return self.fail("TODO implement codegen for non-expr asm", .{});
671 }
672 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
673 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
674 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
675 // This equation accounts for the fact that even if we have exactly 4 bytes
676 // for the string, we still use the next u32 for the null terminator.
677 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
678
679 break constraint;
680 } else null;
681
682 for (inputs) |input| {
683 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
684 const constraint = std.mem.sliceTo(input_bytes, 0);
685 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
686 // This equation accounts for the fact that even if we have exactly 4 bytes
687 // for the string, we still use the next u32 for the null terminator.
688 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
689
690 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
691 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
692 }
693 const reg_name = constraint[1 .. constraint.len - 1];
694 const reg = parseRegName(reg_name) orelse
695 return self.fail("unrecognized register: '{s}'", .{reg_name});
696
697 const arg_mcv = try self.resolveInst(input);
698 try self.register_manager.getReg(reg, null);
699 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
700 }
701
702 {
703 var clobber_i: u32 = 0;
704 while (clobber_i < clobbers_len) : (clobber_i += 1) {
705 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
706 // This equation accounts for the fact that even if we have exactly 4 bytes
707 // for the string, we still use the next u32 for the null terminator.
708 extra_i += clobber.len / 4 + 1;
709
710 // TODO honor these
711 }
712 }
713
714 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
715
716 if (mem.eql(u8, asm_source, "ta 0x6d")) {
717 _ = try self.addInst(.{
718 .tag = .tcc,
719 .data = .{
720 .trap = .{
721 .is_imm = true,
722 .cond = 0b1000, // TODO need to look into changing this into an enum
723 .rs2_or_imm = .{ .imm = 0x6d },
724 },
725 },
726 });
727 } else {
728 return self.fail("TODO implement a full SPARCv9 assembly parsing", .{});
729 }
730
731 if (output_constraint) |output| {
732 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
733 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
734 }
735 const reg_name = output[2 .. output.len - 1];
736 const reg = parseRegName(reg_name) orelse
737 return self.fail("unrecognized register: '{s}'", .{reg_name});
738 break :result MCValue{ .register = reg };
739 } else {
740 break :result MCValue{ .none = {} };
741 }
742 };
743
744 simple: {
745 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
746 var buf_index: usize = 0;
747 for (outputs) |output| {
748 if (output == .none) continue;
749
750 if (buf_index >= buf.len) break :simple;
751 buf[buf_index] = output;
752 buf_index += 1;
753 }
754 if (buf_index + inputs.len > buf.len) break :simple;
755 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
756 return self.finishAir(inst, result, buf);
757 }
758
759 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
760 for (outputs) |output| {
761 if (output == .none) continue;
762
763 bt.feed(output);
764 }
765 for (inputs) |input| {
766 bt.feed(input);
767 }
768 return bt.finishAir(result);
769}
770
771fn airArg(self: *Self, inst: Air.Inst.Index) !void {
772 const arg_index = self.arg_index;
773 self.arg_index += 1;
774
775 const ty = self.air.typeOfIndex(inst);
776 _ = ty;
777
778 const result = self.args[arg_index];
779 // TODO support stack-only arguments
780 // TODO Copy registers to the stack
781 const mcv = result;
782
783 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
784
785 if (self.liveness.isUnused(inst))
786 return self.finishAirBookkeeping();
787
788 switch (mcv) {
789 .register => |reg| {
790 self.register_manager.getRegAssumeFree(reg, inst);
791 },
792 else => {},
793 }
794
795 return self.finishAir(inst, mcv, .{ .none, .none, .none });
796}
797
798fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
799 try self.blocks.putNoClobber(self.gpa, inst, .{
800 // A block is a setup to be able to jump to the end.
801 .relocs = .{},
802 // It also acts as a receptacle for break operands.
803 // Here we use `MCValue.none` to represent a null value so that the first
804 // break instruction will choose a MCValue for the block result and overwrite
805 // this field. Following break instructions will use that MCValue to put their
806 // block results.
807 .mcv = MCValue{ .none = {} },
808 });
809 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
810
811 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
812 const extra = self.air.extraData(Air.Block, ty_pl.payload);
813 const body = self.air.extra[extra.end..][0..extra.data.body_len];
814 try self.genBody(body);
815
816 // relocations for `bpcc` instructions
817 const relocs = &self.blocks.getPtr(inst).?.relocs;
818 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
819 // If the last Mir instruction is the last relocation (which
820 // would just jump one instruction further), it can be safely
821 // removed
822 self.mir_instructions.orderedRemove(relocs.pop());
823 }
824 for (relocs.items) |reloc| {
825 try self.performReloc(reloc);
826 }
827
828 const result = self.blocks.getPtr(inst).?.mcv;
829 return self.finishAir(inst, result, .{ .none, .none, .none });
830}
831
832fn airBreakpoint(self: *Self) !void {
833 // ta 0x01
834 _ = try self.addInst(.{
835 .tag = .tcc,
836 .data = .{
837 .trap = .{
838 .is_imm = true,
839 .cond = 0b1000, // TODO need to look into changing this into an enum
840 .rs2_or_imm = .{ .imm = 0x01 },
841 },
842 },
843 });
844 return self.finishAirBookkeeping();
845}
846
847fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
848 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
849
850 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
851 const callee = pl_op.operand;
852 const extra = self.air.extraData(Air.Call, pl_op.payload);
853 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]);
854 const ty = self.air.typeOf(callee);
855 const fn_ty = switch (ty.zigTypeTag()) {
856 .Fn => ty,
857 .Pointer => ty.childType(),
858 else => unreachable,
859 };
860
861 var info = try self.resolveCallingConventionValues(fn_ty, .caller);
862 defer info.deinit(self);
863 for (info.args) |mc_arg, arg_i| {
864 const arg = args[arg_i];
865 const arg_ty = self.air.typeOf(arg);
866 const arg_mcv = try self.resolveInst(arg);
867
868 switch (mc_arg) {
869 .none => continue,
870 .undef => unreachable,
871 .immediate => unreachable,
872 .unreach => unreachable,
873 .dead => unreachable,
874 .memory => unreachable,
875 .register => |reg| {
876 try self.register_manager.getReg(reg, null);
877 try self.genSetReg(arg_ty, reg, arg_mcv);
878 },
879 .stack_offset => {
880 return self.fail("TODO implement calling with parameters in memory", .{});
881 },
882 .ptr_stack_offset => {
883 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
884 },
885 }
886 }
887
888 // Due to incremental compilation, how function calls are generated depends
889 // on linking.
890 if (self.air.value(callee)) |func_value| {
891 if (self.bin_file.tag == link.File.Elf.base_tag) {
892 if (func_value.castTag(.function)) |func_payload| {
893 const func = func_payload.data;
894 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
895 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
896 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
897 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
898 const mod = self.bin_file.options.module.?;
899 break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes);
900 } else unreachable;
901
902 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
903
904 _ = try self.addInst(.{
905 .tag = .jmpl,
906 .data = .{
907 .arithmetic_3op = .{
908 .is_imm = false,
909 .rd = .o7,
910 .rs1 = .o7,
911 .rs2_or_imm = .{ .rs2 = .g0 },
912 },
913 },
914 });
915
916 // TODO Find a way to fill this delay slot
917 _ = try self.addInst(.{
918 .tag = .nop,
919 .data = .{ .nop = {} },
920 });
921 } else if (func_value.castTag(.extern_fn)) |_| {
922 return self.fail("TODO implement calling extern functions", .{});
923 } else {
924 return self.fail("TODO implement calling bitcasted functions", .{});
925 }
926 } else @panic("TODO SPARCv9 currently does not support non-ELF binaries");
927 } else {
928 assert(ty.zigTypeTag() == .Pointer);
929 const mcv = try self.resolveInst(callee);
930 try self.genSetReg(ty, .o7, mcv);
931
932 _ = try self.addInst(.{
933 .tag = .jmpl,
934 .data = .{
935 .arithmetic_3op = .{
936 .is_imm = false,
937 .rd = .o7,
938 .rs1 = .o7,
939 .rs2_or_imm = .{ .rs2 = .g0 },
940 },
941 },
942 });
943
944 // TODO Find a way to fill this delay slot
945 _ = try self.addInst(.{
946 .tag = .nop,
947 .data = .{ .nop = {} },
948 });
949 }
950
951 const result = info.return_value;
952
953 if (args.len + 1 <= Liveness.bpi - 1) {
954 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
955 buf[0] = callee;
956 std.mem.copy(Air.Inst.Ref, buf[1..], args);
957 return self.finishAir(inst, result, buf);
958 }
959
960 @panic("TODO handle return value with BigTomb");
961}
962
963fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
964 // TODO emit debug info lexical block
965 return self.finishAir(inst, .dead, .{ .none, .none, .none });
966}
967
968fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
969 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
970 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
971 // TODO emit debug info for function change
972 _ = function;
973 return self.finishAir(inst, .dead, .{ .none, .none, .none });
974}
975
976fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
977 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
978
979 _ = try self.addInst(.{
980 .tag = .dbg_line,
981 .data = .{
982 .dbg_line_column = .{
983 .line = dbg_stmt.line,
984 .column = dbg_stmt.column,
985 },
986 },
987 });
988
989 return self.finishAirBookkeeping();
990}
991
992fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
993 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
994 const name = self.air.nullTerminatedString(pl_op.payload);
995 const operand = pl_op.operand;
996 // TODO emit debug info for this variable
997 _ = name;
998 return self.finishAir(inst, .dead, .{ operand, .none, .none });
999}
1000
1001fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1002 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1003 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
1004 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1005}
1006
1007fn airRet(self: *Self, inst: Air.Inst.Index) !void {
1008 const un_op = self.air.instructions.items(.data)[inst].un_op;
1009 const operand = try self.resolveInst(un_op);
1010 try self.ret(operand);
1011 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1012}
1013
1014fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
1015 const un_op = self.air.instructions.items(.data)[inst].un_op;
1016 const ptr = try self.resolveInst(un_op);
1017 _ = ptr;
1018 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
1019 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1020}
1021
1022fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
1023 const stack_offset = try self.allocMemPtr(inst);
1024 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1025}
1026
1027fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1028 _ = self;
1029 _ = inst;
1030
1031 return self.fail("TODO implement store for {}", .{self.target.cpu.arch});
1032}
1033
1034fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
1035 _ = self;
1036 _ = inst;
1037
1038 return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
1039}
1040
1041// Common helper functions
1042
1043/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
1044/// after codegen for this symbol is done.
1045fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
1046 switch (self.debug_output) {
1047 .dwarf => |dw| {
1048 assert(ty.hasRuntimeBits());
1049 const dbg_info = &dw.dbg_info;
1050 const index = dbg_info.items.len;
1051 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
1052 const mod = self.bin_file.options.module.?;
1053 const atom = switch (self.bin_file.tag) {
1054 .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom,
1055 else => unreachable,
1056 };
1057 try dw.addTypeReloc(atom, ty, @intCast(u32, index), null);
1058 },
1059 else => {},
1060 }
1061}
1062
1063fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
1064 const gpa = self.gpa;
1065 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
1066 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
1067 self.mir_instructions.appendAssumeCapacity(inst);
1068 return result_index;
1069}
1070
1071fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
1072 if (abi_align > self.stack_align)
1073 self.stack_align = abi_align;
1074 // TODO find a free slot instead of always appending
1075 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
1076 self.next_stack_offset = offset + abi_size;
1077 if (self.next_stack_offset > self.max_end_stack)
1078 self.max_end_stack = self.next_stack_offset;
1079 try self.stack.putNoClobber(self.gpa, offset, .{
1080 .inst = inst,
1081 .size = abi_size,
1082 });
1083 return offset;
1084}
1085
1086/// Use a pointer instruction as the basis for allocating stack memory.
1087fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
1088 const elem_ty = self.air.typeOfIndex(inst).elemType();
1089
1090 if (!elem_ty.hasRuntimeBits()) {
1091 // As this stack item will never be dereferenced at runtime,
1092 // return the stack offset 0. Stack offset 0 will be where all
1093 // zero-sized stack allocations live as non-zero-sized
1094 // allocations will always have an offset > 0.
1095 return @as(u32, 0);
1096 }
1097
1098 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1099 const mod = self.bin_file.options.module.?;
1100 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
1101 };
1102 // TODO swap this for inst.ty.ptrAlign
1103 const abi_align = elem_ty.abiAlignment(self.target.*);
1104 return self.allocMem(inst, abi_size, abi_align);
1105}
1106
1107fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
1108 const elem_ty = self.air.typeOfIndex(inst);
1109 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1110 const mod = self.bin_file.options.module.?;
1111 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
1112 };
1113 const abi_align = elem_ty.abiAlignment(self.target.*);
1114 if (abi_align > self.stack_align)
1115 self.stack_align = abi_align;
1116
1117 if (reg_ok) {
1118 // Make sure the type can fit in a register before we try to allocate one.
1119 if (abi_size <= 8) {
1120 if (self.register_manager.tryAllocReg(inst)) |reg| {
1121 return MCValue{ .register = reg };
1122 }
1123 }
1124 }
1125 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
1126 return MCValue{ .stack_offset = stack_offset };
1127}
1128
1129/// Copies a value to a register without tracking the register. The register is not considered
1130/// allocated. A second call to `copyToTmpRegister` may return the same register.
1131/// This can have a side effect of spilling instructions to the stack to free up a register.
1132fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
1133 const reg = try self.register_manager.allocReg(null);
1134 try self.genSetReg(ty, reg, mcv);
1135 return reg;
1136}
1137
1138fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1139 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1140 try table.ensureUnusedCapacity(self.gpa, additional_count);
1141}
1142
1143fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
1144 @setCold(true);
1145 assert(self.err_msg == null);
1146 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
1147 return error.CodegenFail;
1148}
1149
1150/// Called when there are no operands, and the instruction is always unreferenced.
1151fn finishAirBookkeeping(self: *Self) void {
1152 if (std.debug.runtime_safety) {
1153 self.air_bookkeeping += 1;
1154 }
1155}
1156
1157fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
1158 var tomb_bits = self.liveness.getTombBits(inst);
1159 for (operands) |op| {
1160 const dies = @truncate(u1, tomb_bits) != 0;
1161 tomb_bits >>= 1;
1162 if (!dies) continue;
1163 const op_int = @enumToInt(op);
1164 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
1165 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
1166 self.processDeath(op_index);
1167 }
1168 const is_used = @truncate(u1, tomb_bits) == 0;
1169 if (is_used) {
1170 log.debug("%{d} => {}", .{ inst, result });
1171 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1172 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
1173
1174 switch (result) {
1175 .register => |reg| {
1176 // In some cases (such as bitcast), an operand
1177 // may be the same MCValue as the result. If
1178 // that operand died and was a register, it
1179 // was freed by processDeath. We have to
1180 // "re-allocate" the register.
1181 if (self.register_manager.isRegFree(reg)) {
1182 self.register_manager.getRegAssumeFree(reg, inst);
1183 }
1184 },
1185 else => {},
1186 }
1187 }
1188 self.finishAirBookkeeping();
1189}
1190
1191fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
1192 const ty = self.air.instructions.items(.data)[inst].ty;
1193 const name = self.mod_fn.getParamName(arg_index);
1194 const name_with_null = name.ptr[0 .. name.len + 1];
1195
1196 switch (mcv) {
1197 .register => |reg| {
1198 switch (self.debug_output) {
1199 .dwarf => |dw| {
1200 const dbg_info = &dw.dbg_info;
1201 try dbg_info.ensureUnusedCapacity(3);
1202 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
1203 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1204 1, // ULEB128 dwarf expression length
1205 reg.dwarfLocOp(),
1206 });
1207 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
1208 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
1209 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
1210 },
1211 else => {},
1212 }
1213 },
1214 .stack_offset => |offset| {
1215 _ = offset;
1216 switch (self.debug_output) {
1217 .dwarf => {},
1218 else => {},
1219 }
1220 },
1221 else => {},
1222 }
1223}
1224
1225fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1226 assert(off_type == Register or off_type == i13);
1227
1228 const is_imm = (off_type == i13);
1229 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1230
1231 switch (abi_size) {
1232 1, 2, 4, 8 => {
1233 const tag: Mir.Inst.Tag = switch (abi_size) {
1234 1 => .ldub,
1235 2 => .lduh,
1236 4 => .lduw,
1237 8 => .ldx,
1238 else => unreachable, // unexpected abi size
1239 };
1240
1241 _ = try self.addInst(.{
1242 .tag = tag,
1243 .data = .{
1244 .arithmetic_3op = .{
1245 .is_imm = is_imm,
1246 .rd = value_reg,
1247 .rs1 = addr_reg,
1248 .rs2_or_imm = rs2_or_imm,
1249 },
1250 },
1251 });
1252 },
1253 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1254 else => unreachable,
1255 }
1256}
1257
1258fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
1259 switch (mcv) {
1260 .dead => unreachable,
1261 .unreach, .none => return, // Nothing to do.
1262 .undef => {
1263 if (!self.wantSafety())
1264 return; // The already existing value will do just fine.
1265 // Write the debug undefined value.
1266 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1267 },
1268 .ptr_stack_offset => |off| {
1269 const simm13 = math.cast(u12, off) catch
1270 return self.fail("TODO larger stack offsets", .{});
1271
1272 _ = try self.addInst(.{
1273 .tag = .add,
1274 .data = .{
1275 .arithmetic_3op = .{
1276 .is_imm = true,
1277 .rd = reg,
1278 .rs1 = .sp,
1279 .rs2_or_imm = .{ .imm = simm13 },
1280 },
1281 },
1282 });
1283 },
1284 .immediate => |x| {
1285 if (x <= math.maxInt(u12)) {
1286 _ = try self.addInst(.{
1287 .tag = .@"or",
1288 .data = .{
1289 .arithmetic_3op = .{
1290 .is_imm = true,
1291 .rd = reg,
1292 .rs1 = .g0,
1293 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1294 },
1295 },
1296 });
1297 } else if (x <= math.maxInt(u32)) {
1298 _ = try self.addInst(.{
1299 .tag = .sethi,
1300 .data = .{
1301 .sethi = .{
1302 .rd = reg,
1303 .imm = @truncate(u22, x >> 10),
1304 },
1305 },
1306 });
1307
1308 _ = try self.addInst(.{
1309 .tag = .@"or",
1310 .data = .{
1311 .arithmetic_3op = .{
1312 .is_imm = true,
1313 .rd = reg,
1314 .rs1 = reg,
1315 .rs2_or_imm = .{ .imm = @truncate(u10, x) },
1316 },
1317 },
1318 });
1319 } else if (x <= math.maxInt(u44)) {
1320 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) });
1321
1322 _ = try self.addInst(.{
1323 .tag = .sllx,
1324 .data = .{
1325 .shift = .{
1326 .is_imm = true,
1327 .width = .shift64,
1328 .rd = reg,
1329 .rs1 = reg,
1330 .rs2_or_imm = .{ .imm = 12 },
1331 },
1332 },
1333 });
1334
1335 _ = try self.addInst(.{
1336 .tag = .@"or",
1337 .data = .{
1338 .arithmetic_3op = .{
1339 .is_imm = true,
1340 .rd = reg,
1341 .rs1 = reg,
1342 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1343 },
1344 },
1345 });
1346 } else {
1347 // Need to allocate a temporary register to load 64-bit immediates.
1348 const tmp_reg = try self.register_manager.allocReg(null);
1349
1350 try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) });
1351 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) });
1352
1353 _ = try self.addInst(.{
1354 .tag = .sllx,
1355 .data = .{
1356 .shift = .{
1357 .is_imm = true,
1358 .width = .shift64,
1359 .rd = reg,
1360 .rs1 = reg,
1361 .rs2_or_imm = .{ .imm = 32 },
1362 },
1363 },
1364 });
1365
1366 _ = try self.addInst(.{
1367 .tag = .@"or",
1368 .data = .{
1369 .arithmetic_3op = .{
1370 .is_imm = false,
1371 .rd = reg,
1372 .rs1 = reg,
1373 .rs2_or_imm = .{ .rs2 = tmp_reg },
1374 },
1375 },
1376 });
1377 }
1378 },
1379 .register => |src_reg| {
1380 // If the registers are the same, nothing to do.
1381 if (src_reg.id() == reg.id())
1382 return;
1383
1384 // or %g0, src, dst (aka mov src, dst)
1385 _ = try self.addInst(.{
1386 .tag = .@"or",
1387 .data = .{
1388 .arithmetic_3op = .{
1389 .is_imm = false,
1390 .rd = reg,
1391 .rs1 = .g0,
1392 .rs2_or_imm = .{ .rs2 = src_reg },
1393 },
1394 },
1395 });
1396 },
1397 .memory => |addr| {
1398 // The value is in memory at a hard-coded address.
1399 // If the type is a pointer, it means the pointer address is at this memory location.
1400 try self.genSetReg(ty, reg, .{ .immediate = addr });
1401 try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));
1402 },
1403 .stack_offset => |off| {
1404 const real_offset = off + abi.stack_bias + abi.stack_save_area;
1405 const simm13 = math.cast(i13, real_offset) catch
1406 return self.fail("TODO larger stack offsets", .{});
1407 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
1408 },
1409 }
1410}
1411
1412fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1413 const abi_size = ty.abiSize(self.target.*);
1414 switch (mcv) {
1415 .dead => unreachable,
1416 .unreach, .none => return, // Nothing to do.
1417 .undef => {
1418 if (!self.wantSafety())
1419 return; // The already existing value will do just fine.
1420 // TODO Upgrade this to a memset call when we have that available.
1421 switch (ty.abiSize(self.target.*)) {
1422 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
1423 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
1424 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1425 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1426 else => return self.fail("TODO implement memset", .{}),
1427 }
1428 },
1429 .immediate,
1430 .ptr_stack_offset,
1431 => {
1432 const reg = try self.copyToTmpRegister(ty, mcv);
1433 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1434 },
1435 .register => |reg| {
1436 const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area;
1437 const simm13 = math.cast(i13, real_offset) catch
1438 return self.fail("TODO larger stack offsets", .{});
1439 return self.genStore(reg, .sp, i13, simm13, abi_size);
1440 },
1441 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),
1442 }
1443}
1444
1445fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1446 assert(off_type == Register or off_type == i13);
1447
1448 const is_imm = (off_type == i13);
1449 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1450
1451 switch (abi_size) {
1452 1, 2, 4, 8 => {
1453 const tag: Mir.Inst.Tag = switch (abi_size) {
1454 1 => .stb,
1455 2 => .sth,
1456 4 => .stw,
1457 8 => .stx,
1458 else => unreachable, // unexpected abi size
1459 };
1460
1461 _ = try self.addInst(.{
1462 .tag = tag,
1463 .data = .{
1464 .arithmetic_3op = .{
1465 .is_imm = is_imm,
1466 .rd = value_reg,
1467 .rs1 = addr_reg,
1468 .rs2_or_imm = rs2_or_imm,
1469 },
1470 },
1471 });
1472 },
1473 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1474 else => unreachable,
1475 }
1476}
1477
1478fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
1479 if (typed_value.val.isUndef())
1480 return MCValue{ .undef = {} };
1481
1482 if (typed_value.val.castTag(.decl_ref)) |payload| {
1483 return self.lowerDeclRef(typed_value, payload.data);
1484 }
1485 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
1486 return self.lowerDeclRef(typed_value, payload.data.decl_index);
1487 }
1488 const target = self.target.*;
1489
1490 switch (typed_value.ty.zigTypeTag()) {
1491 .Int => {
1492 const info = typed_value.ty.intInfo(self.target.*);
1493 if (info.bits <= 64) {
1494 const unsigned = switch (info.signedness) {
1495 .signed => blk: {
1496 const signed = typed_value.val.toSignedInt();
1497 break :blk @bitCast(u64, signed);
1498 },
1499 .unsigned => typed_value.val.toUnsignedInt(target),
1500 };
1501
1502 return MCValue{ .immediate = unsigned };
1503 } else {
1504 return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
1505 }
1506 },
1507 .ComptimeInt => unreachable, // semantic analysis prevents this
1508 .ComptimeFloat => unreachable, // semantic analysis prevents this
1509 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
1510 }
1511}
1512
1513fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
1514 // Treat each stack item as a "layer" on top of the previous one.
1515 var i: usize = self.branch_stack.items.len;
1516 while (true) {
1517 i -= 1;
1518 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1519 assert(mcv != .dead);
1520 return mcv;
1521 }
1522 }
1523}
1524
1525fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
1526 try self.ensureProcessDeathCapacity(operand_count + 1);
1527 return BigTomb{
1528 .function = self,
1529 .inst = inst,
1530 .tomb_bits = self.liveness.getTombBits(inst),
1531 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
1532 .bit_index = 0,
1533 };
1534}
1535
1536fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
1537 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1538 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1539
1540 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
1541 if (tv.ty.zigTypeTag() == .Pointer) blk: {
1542 if (tv.ty.castPtrToFn()) |_| break :blk;
1543 if (!tv.ty.elemType2().hasRuntimeBits()) {
1544 return MCValue.none;
1545 }
1546 }
1547
1548 const mod = self.bin_file.options.module.?;
1549 const decl = mod.declPtr(decl_index);
1550
1551 mod.markDeclAlive(decl);
1552 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1553 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1554 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
1555 return MCValue{ .memory = got_addr };
1556 } else {
1557 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
1558 }
1559}
1560
1561fn parseRegName(name: []const u8) ?Register {
1562 if (@hasDecl(Register, "parseRegName")) {
1563 return Register.parseRegName(name);
1564 }
1565 return std.meta.stringToEnum(Register, name);
1566}
1567
1568fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
1569 const tag = self.mir_instructions.items(.tag)[inst];
1570 switch (tag) {
1571 .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
1572 else => unreachable,
1573 }
1574}
1575
1576/// Asserts there is already capacity to insert into top branch inst_table.
1577fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1578 const air_tags = self.air.instructions.items(.tag);
1579 if (air_tags[inst] == .constant) return; // Constants are immortal.
1580 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1581 const prev_value = self.getResolvedInstValue(inst);
1582 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1583 branch.inst_table.putAssumeCapacity(inst, .dead);
1584 switch (prev_value) {
1585 .register => |reg| {
1586 self.register_manager.freeReg(reg);
1587 },
1588 else => {}, // TODO process stack allocation death
1589 }
1590}
1591
1592/// Caller must call `CallMCValues.deinit`.
1593fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues {
1594 const cc = fn_ty.fnCallingConvention();
1595 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1596 defer self.gpa.free(param_types);
1597 fn_ty.fnParamTypes(param_types);
1598 var result: CallMCValues = .{
1599 .args = try self.gpa.alloc(MCValue, param_types.len),
1600 // These undefined values must be populated before returning from this function.
1601 .return_value = undefined,
1602 .stack_byte_count = undefined,
1603 .stack_align = undefined,
1604 };
1605 errdefer self.gpa.free(result.args);
1606
1607 const ret_ty = fn_ty.fnReturnType();
1608
1609 switch (cc) {
1610 .Naked => {
1611 assert(result.args.len == 0);
1612 result.return_value = .{ .unreach = {} };
1613 result.stack_byte_count = 0;
1614 result.stack_align = 1;
1615 return result;
1616 },
1617 .Unspecified, .C => {
1618 // SPARC Compliance Definition 2.4.1, Chapter 3
1619 // Low-Level System Information (64-bit psABI) - Function Calling Sequence
1620
1621 var next_register: usize = 0;
1622 var next_stack_offset: u32 = 0;
1623
1624 // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee.
1625 const argument_registers = switch (role) {
1626 .caller => abi.c_abi_int_param_regs_caller_view,
1627 .callee => abi.c_abi_int_param_regs_callee_view,
1628 };
1629
1630 for (param_types) |ty, i| {
1631 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1632 if (param_size <= 8) {
1633 if (next_register < argument_registers.len) {
1634 result.args[i] = .{ .register = argument_registers[next_register] };
1635 next_register += 1;
1636 } else {
1637 result.args[i] = .{ .stack_offset = next_stack_offset };
1638 next_register += next_stack_offset;
1639 }
1640 } else if (param_size <= 16) {
1641 if (next_register < argument_registers.len - 1) {
1642 return self.fail("TODO MCValues with 2 registers", .{});
1643 } else if (next_register < argument_registers.len) {
1644 return self.fail("TODO MCValues split register + stack", .{});
1645 } else {
1646 result.args[i] = .{ .stack_offset = next_stack_offset };
1647 next_register += next_stack_offset;
1648 }
1649 } else {
1650 result.args[i] = .{ .stack_offset = next_stack_offset };
1651 next_register += next_stack_offset;
1652 }
1653 }
1654
1655 result.stack_byte_count = next_stack_offset;
1656 result.stack_align = 16;
1657
1658 if (ret_ty.zigTypeTag() == .NoReturn) {
1659 result.return_value = .{ .unreach = {} };
1660 } else if (!ret_ty.hasRuntimeBits()) {
1661 result.return_value = .{ .none = {} };
1662 } else {
1663 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
1664 // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller.
1665 if (ret_ty_size <= 8) {
1666 result.return_value = switch (role) {
1667 .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] },
1668 .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] },
1669 };
1670 } else {
1671 return self.fail("TODO support more return values for sparcv9", .{});
1672 }
1673 }
1674 },
1675 else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}),
1676 }
1677
1678 return result;
1679}
1680
1681fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
1682 // First section of indexes correspond to a set number of constant values.
1683 const ref_int = @enumToInt(inst);
1684 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
1685 const tv = Air.Inst.Ref.typed_value_map[ref_int];
1686 if (!tv.ty.hasRuntimeBits()) {
1687 return MCValue{ .none = {} };
1688 }
1689 return self.genTypedValue(tv);
1690 }
1691
1692 // If the type has no codegen bits, no need to store it.
1693 const inst_ty = self.air.typeOf(inst);
1694 if (!inst_ty.hasRuntimeBits())
1695 return MCValue{ .none = {} };
1696
1697 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
1698 switch (self.air.instructions.items(.tag)[inst_index]) {
1699 .constant => {
1700 // Constants have static lifetimes, so they are always memoized in the outer most table.
1701 const branch = &self.branch_stack.items[0];
1702 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
1703 if (!gop.found_existing) {
1704 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
1705 gop.value_ptr.* = try self.genTypedValue(.{
1706 .ty = inst_ty,
1707 .val = self.air.values[ty_pl.payload],
1708 });
1709 }
1710 return gop.value_ptr.*;
1711 },
1712 .const_ty => unreachable,
1713 else => return self.getResolvedInstValue(inst_index),
1714 }
1715}
1716
1717fn ret(self: *Self, mcv: MCValue) !void {
1718 const ret_ty = self.fn_type.fnReturnType();
1719 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1720
1721 // Just add space for an instruction, patch this later
1722 const index = try self.addInst(.{
1723 .tag = .nop,
1724 .data = .{ .nop = {} },
1725 });
1726 try self.exitlude_jump_relocs.append(self.gpa, index);
1727}
1728
1729fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1730 if (!self.liveness.operandDies(inst, op_index))
1731 return false;
1732
1733 switch (mcv) {
1734 .register => |reg| {
1735 // If it's in the registers table, need to associate the register with the
1736 // new instruction.
1737 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
1738 if (!self.register_manager.isRegFree(reg)) {
1739 self.register_manager.registers[index] = inst;
1740 }
1741 }
1742 log.debug("%{d} => {} (reused)", .{ inst, reg });
1743 },
1744 .stack_offset => |off| {
1745 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1746 },
1747 else => return false,
1748 }
1749
1750 // Prevent the operand deaths processing code from deallocating it.
1751 self.liveness.clearOperandDeath(inst, op_index);
1752
1753 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1754 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1755 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1756
1757 return true;
1758}
1759
1760/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1761fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
1762 switch (loc) {
1763 .none => return,
1764 .register => |reg| return self.genSetReg(ty, reg, val),
1765 .stack_offset => |off| return self.genSetStack(ty, off, val),
1766 .memory => {
1767 return self.fail("TODO implement setRegOrMem for memory", .{});
1768 },
1769 else => unreachable,
1770 }
1771}
1772
1773pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1774 const stack_mcv = try self.allocRegOrMem(inst, false);
1775 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
1776 const reg_mcv = self.getResolvedInstValue(inst);
1777 assert(reg == reg_mcv.register);
1778 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1779 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1780 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1781}
1782
1783/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
1784fn wantSafety(self: *Self) bool {
1785 return switch (self.bin_file.options.optimize_mode) {
1786 .Debug => true,
1787 .ReleaseSafe => true,
1788 .ReleaseFast => false,
1789 .ReleaseSmall => false,
1790 };
1791}
src/arch/sparcv9/Emit.zig deleted-274
...@@ -1,274 +0,0 @@
1//! This file contains the functionality for lowering SPARCv9 MIR into
2//! machine code
3
4const std = @import("std");
5const Endian = std.builtin.Endian;
6const assert = std.debug.assert;
7const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");
9const ErrorMsg = Module.ErrorMsg;
10const Liveness = @import("../../Liveness.zig");
11const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
12const DW = std.dwarf;
13const leb128 = std.leb;
14
15const Emit = @This();
16const Mir = @import("Mir.zig");
17const bits = @import("bits.zig");
18const Instruction = bits.Instruction;
19const Register = bits.Register;
20
21mir: Mir,
22bin_file: *link.File,
23debug_output: DebugInfoOutput,
24target: *const std.Target,
25err_msg: ?*ErrorMsg = null,
26src_loc: Module.SrcLoc,
27code: *std.ArrayList(u8),
28
29prev_di_line: u32,
30prev_di_column: u32,
31/// Relative to the beginning of `code`.
32prev_di_pc: usize,
33
34const InnerError = error{
35 OutOfMemory,
36 EmitFail,
37};
38
39pub fn emitMir(
40 emit: *Emit,
41) InnerError!void {
42 const mir_tags = emit.mir.instructions.items(.tag);
43
44 // Emit machine code
45 for (mir_tags) |tag, index| {
46 const inst = @intCast(u32, index);
47 switch (tag) {
48 .dbg_line => try emit.mirDbgLine(inst),
49 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
50 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
51
52 .add => try emit.mirArithmetic3Op(inst),
53
54 .bpcc => @panic("TODO implement sparcv9 bpcc"),
55
56 .call => @panic("TODO implement sparcv9 call"),
57
58 .jmpl => try emit.mirArithmetic3Op(inst),
59
60 .ldub => try emit.mirArithmetic3Op(inst),
61 .lduh => try emit.mirArithmetic3Op(inst),
62 .lduw => try emit.mirArithmetic3Op(inst),
63 .ldx => try emit.mirArithmetic3Op(inst),
64
65 .@"or" => try emit.mirArithmetic3Op(inst),
66
67 .nop => try emit.mirNop(),
68
69 .@"return" => try emit.mirArithmetic2Op(inst),
70
71 .save => try emit.mirArithmetic3Op(inst),
72 .restore => try emit.mirArithmetic3Op(inst),
73
74 .sethi => try emit.mirSethi(inst),
75
76 .sllx => @panic("TODO implement sparcv9 sllx"),
77
78 .stb => try emit.mirArithmetic3Op(inst),
79 .sth => try emit.mirArithmetic3Op(inst),
80 .stw => try emit.mirArithmetic3Op(inst),
81 .stx => try emit.mirArithmetic3Op(inst),
82
83 .sub => try emit.mirArithmetic3Op(inst),
84
85 .tcc => try emit.mirTrap(inst),
86 }
87 }
88}
89
90pub fn deinit(emit: *Emit) void {
91 emit.* = undefined;
92}
93
94fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
95 const tag = emit.mir.instructions.items(.tag)[inst];
96 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
97
98 switch (tag) {
99 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
100 else => unreachable,
101 }
102}
103
104fn mirDebugPrologueEnd(emit: *Emit) !void {
105 switch (emit.debug_output) {
106 .dwarf => |dbg_out| {
107 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
108 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
109 },
110 .plan9 => {},
111 .none => {},
112 }
113}
114
115fn mirDebugEpilogueBegin(emit: *Emit) !void {
116 switch (emit.debug_output) {
117 .dwarf => |dbg_out| {
118 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
119 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
120 },
121 .plan9 => {},
122 .none => {},
123 }
124}
125
126fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
127 const tag = emit.mir.instructions.items(.tag)[inst];
128 const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op;
129
130 const rs1 = data.rs1;
131
132 if (data.is_imm) {
133 const imm = data.rs2_or_imm.imm;
134 switch (tag) {
135 .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
136 else => unreachable,
137 }
138 } else {
139 const rs2 = data.rs2_or_imm.rs2;
140 switch (tag) {
141 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
142 else => unreachable,
143 }
144 }
145}
146
147fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
148 const tag = emit.mir.instructions.items(.tag)[inst];
149 const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op;
150
151 const rd = data.rd;
152 const rs1 = data.rs1;
153
154 if (data.is_imm) {
155 const imm = data.rs2_or_imm.imm;
156 switch (tag) {
157 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
158 .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)),
159 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
160 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
161 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
162 .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
163 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
164 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
165 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
166 .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)),
167 .sth => try emit.writeInstruction(Instruction.sth(i13, rs1, imm, rd)),
168 .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)),
169 .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)),
170 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
171 else => unreachable,
172 }
173 } else {
174 const rs2 = data.rs2_or_imm.rs2;
175 switch (tag) {
176 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
177 .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)),
178 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
179 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
180 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
181 .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
182 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
183 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
184 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
185 .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)),
186 .sth => try emit.writeInstruction(Instruction.sth(Register, rs1, rs2, rd)),
187 .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)),
188 .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)),
189 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
190 else => unreachable,
191 }
192 }
193}
194
195fn mirNop(emit: *Emit) !void {
196 try emit.writeInstruction(Instruction.nop());
197}
198
199fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
200 const tag = emit.mir.instructions.items(.tag)[inst];
201 const data = emit.mir.instructions.items(.data)[inst].sethi;
202
203 const imm = data.imm;
204 const rd = data.rd;
205
206 assert(tag == .sethi);
207 try emit.writeInstruction(Instruction.sethi(imm, rd));
208}
209
210fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
211 const tag = emit.mir.instructions.items(.tag)[inst];
212 const data = emit.mir.instructions.items(.data)[inst].trap;
213
214 const cond = data.cond;
215 const ccr = data.ccr;
216 const rs1 = data.rs1;
217
218 if (data.is_imm) {
219 const imm = data.rs2_or_imm.imm;
220 switch (tag) {
221 .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)),
222 else => unreachable,
223 }
224 } else {
225 const rs2 = data.rs2_or_imm.rs2;
226 switch (tag) {
227 .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)),
228 else => unreachable,
229 }
230 }
231}
232
233// Common helper functions
234
235fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
236 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
237 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
238 switch (emit.debug_output) {
239 .dwarf => |dbg_out| {
240 // TODO Look into using the DWARF special opcodes to compress this data.
241 // It lets you emit single-byte opcodes that add different numbers to
242 // both the PC and the line number at the same time.
243 try dbg_out.dbg_line.ensureUnusedCapacity(11);
244 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
245 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
246 if (delta_line != 0) {
247 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
248 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
249 }
250 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
251 emit.prev_di_pc = emit.code.items.len;
252 emit.prev_di_line = line;
253 emit.prev_di_column = column;
254 emit.prev_di_pc = emit.code.items.len;
255 },
256 else => {},
257 }
258}
259
260fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
261 @setCold(true);
262 assert(emit.err_msg == null);
263 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
264 return error.EmitFail;
265}
266
267fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
268 // SPARCv9 instructions are always arranged in BE regardless of the
269 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
270 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
271 const endian = Endian.Big;
272
273 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
274}
src/arch/sparcv9/Mir.zig deleted-256
...@@ -1,256 +0,0 @@
1//! Machine Intermediate Representation.
2//! This data is produced by SPARCv9 Codegen or SPARCv9 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const std = @import("std");
10const builtin = @import("builtin");
11const assert = std.debug.assert;
12
13const Mir = @This();
14const bits = @import("bits.zig");
15const Air = @import("../../Air.zig");
16
17const Instruction = bits.Instruction;
18const Register = bits.Register;
19
20instructions: std.MultiArrayList(Inst).Slice,
21
22/// The meaning of this data is determined by `Inst.Tag` value.
23extra: []const u32,
24
25pub const Inst = struct {
26 tag: Tag,
27 /// The meaning of this depends on `tag`.
28 data: Data,
29
30 pub const Tag = enum(u16) {
31 /// Pseudo-instruction: End of prologue
32 dbg_prologue_end,
33 /// Pseudo-instruction: Beginning of epilogue
34 dbg_epilogue_begin,
35 /// Pseudo-instruction: Update debug line
36 dbg_line,
37
38 // All the real instructions are ordered by their section number
39 // in The SPARC Architecture Manual, Version 9.
40
41 /// A.2 Add
42 /// This uses the arithmetic_3op field.
43 // TODO add other operations.
44 add,
45
46 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
47 /// This uses the branch_predict field.
48 bpcc,
49
50 /// A.8 Call and Link
51 /// This uses the branch_link field.
52 call,
53
54 /// A.24 Jump and Link
55 /// This uses the arithmetic_3op field.
56 jmpl,
57
58 /// A.27 Load Integer
59 /// This uses the arithmetic_3op field.
60 /// Note that the ldd variant of this instruction is deprecated, so do not emit
61 /// it unless specifically requested (e.g. by inline assembly).
62 // TODO add other operations.
63 ldub,
64 lduh,
65 lduw,
66 ldx,
67
68 /// A.31 Logical Operations
69 /// This uses the arithmetic_3op field.
70 // TODO add other operations.
71 @"or",
72
73 /// A.40 No Operation
74 /// This uses the nop field.
75 nop,
76
77 /// A.45 RETURN
78 /// This uses the arithmetic_2op field.
79 @"return",
80
81 /// A.46 SAVE and RESTORE
82 /// This uses the arithmetic_3op field.
83 save,
84 restore,
85
86 /// A.48 SETHI
87 /// This uses the sethi field.
88 sethi,
89
90 /// A.49 Shift
91 /// This uses the shift field.
92 // TODO add other operations.
93 sllx,
94
95 /// A.54 Store Integer
96 /// This uses the arithmetic_3op field.
97 /// Note that the std variant of this instruction is deprecated, so do not emit
98 /// it unless specifically requested (e.g. by inline assembly).
99 // TODO add other operations.
100 stb,
101 sth,
102 stw,
103 stx,
104
105 /// A.56 Subtract
106 /// This uses the arithmetic_3op field.
107 // TODO add other operations.
108 sub,
109
110 /// A.61 Trap on Integer Condition Codes (Tcc)
111 /// This uses the trap field.
112 tcc,
113 };
114
115 /// The position of an MIR instruction within the `Mir` instructions array.
116 pub const Index = u32;
117
118 /// All instructions have a 8-byte payload, which is contained within
119 /// this union. `Tag` determines which union field is active, as well as
120 /// how to interpret the data within.
121 // TODO this is a quick-n-dirty solution that needs to be cleaned up.
122 pub const Data = union {
123 /// Debug info: line and column
124 ///
125 /// Used by e.g. dbg_line
126 dbg_line_column: struct {
127 line: u32,
128 column: u32,
129 },
130
131 /// Two operand arithmetic.
132 /// if is_imm true then it uses the imm field of rs2_or_imm,
133 /// otherwise it uses rs2 field.
134 ///
135 /// Used by e.g. return
136 arithmetic_2op: struct {
137 is_imm: bool,
138 rs1: Register,
139 rs2_or_imm: union {
140 rs2: Register,
141 imm: i13,
142 },
143 },
144
145 /// Three operand arithmetic.
146 /// if is_imm true then it uses the imm field of rs2_or_imm,
147 /// otherwise it uses rs2 field.
148 ///
149 /// Used by e.g. add, sub
150 arithmetic_3op: struct {
151 is_imm: bool,
152 rd: Register,
153 rs1: Register,
154 rs2_or_imm: union {
155 rs2: Register,
156 imm: i13,
157 },
158 },
159
160 /// Branch and link (always unconditional).
161 /// Used by e.g. call
162 branch_link: struct {
163 inst: Index,
164 link: Register = .o7,
165 },
166
167 /// Branch with prediction.
168 /// Used by e.g. bpcc
169 branch_predict: struct {
170 annul: bool = false,
171 pt: bool = true,
172 ccr: Instruction.CCR,
173 cond: Instruction.Condition,
174 inst: Index,
175 },
176
177 /// No additional data
178 ///
179 /// Used by e.g. flushw
180 nop: void,
181
182 /// SETHI operands.
183 ///
184 /// Used by sethi
185 sethi: struct {
186 rd: Register,
187 imm: u22,
188 },
189
190 /// Shift operands.
191 /// if is_imm true then it uses the imm field of rs2_or_imm,
192 /// otherwise it uses rs2 field.
193 ///
194 /// Used by e.g. add, sub
195 shift: struct {
196 is_imm: bool,
197 width: Instruction.ShiftWidth,
198 rd: Register,
199 rs1: Register,
200 rs2_or_imm: union {
201 rs2: Register,
202 imm: u6,
203 },
204 },
205
206 /// Trap.
207 /// if is_imm true then it uses the imm field of rs2_or_imm,
208 /// otherwise it uses rs2 field.
209 ///
210 /// Used by e.g. tcc
211 trap: struct {
212 is_imm: bool = true,
213 cond: Instruction.Condition,
214 ccr: Instruction.CCR = .icc,
215 rs1: Register = .g0,
216 rs2_or_imm: union {
217 rs2: Register,
218 imm: u7,
219 },
220 },
221 };
222
223 // Make sure we don't accidentally make instructions bigger than expected.
224 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
225 comptime {
226 if (builtin.mode != .Debug) {
227 assert(@sizeOf(Data) == 8);
228 }
229 }
230};
231
232pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
233 mir.instructions.deinit(gpa);
234 gpa.free(mir.extra);
235 mir.* = undefined;
236}
237
238/// Returns the requested data, as well as the new index which is at the start of the
239/// trailers for the object.
240pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
241 const fields = std.meta.fields(T);
242 var i: usize = index;
243 var result: T = undefined;
244 inline for (fields) |field| {
245 @field(result, field.name) = switch (field.field_type) {
246 u32 => mir.extra[i],
247 i32 => @bitCast(i32, mir.extra[i]),
248 else => @compileError("bad field type"),
249 };
250 i += 1;
251 }
252 return .{
253 .data = result,
254 .end = i,
255 };
256}
src/arch/sparcv9/abi.zig deleted-37
...@@ -1,37 +0,0 @@
1const bits = @import("bits.zig");
2const Register = bits.Register;
3
4// SPARCv9 stack constants.
5// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1.
6
7// On SPARCv9, %sp points to top of stack + stack bias,
8// and %fp points to top of previous frame + stack bias.
9pub const stack_bias = 2047;
10
11// The first 176 bytes of the stack is reserved for register saving purposes.
12// SPARCv9 requires to reserve space in the stack for the first six arguments,
13// even though they are usually passed in registers.
14pub const stack_save_area = 176;
15
16// There are no callee-preserved registers since the windowing
17// mechanism already takes care of them.
18// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling
19// something, though, as those are shared with the callee and might be
20// thrashed by it.
21pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 };
22
23// Try to allocate i, l, o, then g sets of registers, in order of priority.
24pub const allocatable_regs = [_]Register{
25 // zig fmt: off
26 .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5",
27 .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7,
28 .o0, .o1, .o2, .o3, .o4, .o5,
29 .g1, .g4, .g5,
30 // zig fmt: on
31};
32
33pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
34pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
35
36pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
37pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
src/arch/sparcv9/bits.zig deleted-1256
...@@ -1,1256 +0,0 @@
1const std = @import("std");
2const DW = std.dwarf;
3const assert = std.debug.assert;
4const testing = std.testing;
5
6/// General purpose registers in the SPARCv9 instruction set
7pub const Register = enum(u6) {
8 // zig fmt: off
9 g0, g1, g2, g3, g4, g5, g6, g7,
10 o0, o1, o2, o3, o4, o5, o6, o7,
11 l0, l1, l2, l3, l4, l5, l6, l7,
12 @"i0", @"i1", @"i2", @"i3", @"i4", @"i5", @"i6", @"i7",
13
14 sp = 46, // stack pointer (o6)
15 fp = 62, // frame pointer (i6)
16 // zig fmt: on
17
18 pub fn id(self: Register) u5 {
19 return @truncate(u5, @enumToInt(self));
20 }
21
22 pub fn enc(self: Register) u5 {
23 // For integer registers, enc() == id().
24 return self.id();
25 }
26
27 pub fn dwarfLocOp(reg: Register) u8 {
28 return @as(u8, reg.id()) + DW.OP.reg0;
29 }
30};
31
32test "Register.id" {
33 // SP
34 try testing.expectEqual(@as(u5, 14), Register.o6.id());
35 try testing.expectEqual(Register.o6.id(), Register.sp.id());
36
37 // FP
38 try testing.expectEqual(@as(u5, 30), Register.@"i6".id());
39 try testing.expectEqual(Register.@"i6".id(), Register.fp.id());
40
41 // x0
42 try testing.expectEqual(@as(u5, 0), Register.g0.id());
43 try testing.expectEqual(@as(u5, 8), Register.o0.id());
44 try testing.expectEqual(@as(u5, 16), Register.l0.id());
45 try testing.expectEqual(@as(u5, 24), Register.@"i0".id());
46}
47
48test "Register.enc" {
49 // x0
50 try testing.expectEqual(@as(u5, 0), Register.g0.enc());
51 try testing.expectEqual(@as(u5, 8), Register.o0.enc());
52 try testing.expectEqual(@as(u5, 16), Register.l0.enc());
53 try testing.expectEqual(@as(u5, 24), Register.@"i0".enc());
54
55 // For integer registers, enc() == id().
56 try testing.expectEqual(Register.g0.enc(), Register.g0.id());
57 try testing.expectEqual(Register.o0.enc(), Register.o0.id());
58 try testing.expectEqual(Register.l0.enc(), Register.l0.id());
59 try testing.expectEqual(Register.@"i0".enc(), Register.@"i0".id());
60}
61
62/// Scalar floating point registers in the SPARCv9 instruction set
63pub const FloatingPointRegister = enum(u7) {
64 // SPARCv9 has 64 f32 registers, 32 f64 registers, and 16 f128 registers,
65 // which are aliased in this way:
66 //
67 // | %d0 | %d2 |
68 // %q0 | %f0 | %f1 | %f2 | %f3 |
69 // | %d4 | %d6 |
70 // %q4 | %f4 | %f5 | %f6 | %f7 |
71 // ...
72 // | %d60 | %d62 |
73 // %q60 | %f60 | %f61 | %f62 | %f63 |
74 //
75 // Though, since the instructions uses five-bit addressing, only %f0-%f31
76 // is usable with f32 instructions.
77
78 // zig fmt: off
79
80 // 32-bit registers
81 @"f0", @"f1", @"f2", @"f3", @"f4", @"f5", @"f6", @"f7",
82 @"f8", @"f9", @"f10", @"f11", @"f12", @"f13", @"f14", @"f15",
83 @"f16", @"f17", @"f18", @"f19", @"f20", @"f21", @"f22", @"f23",
84 @"f24", @"f25", @"f26", @"f27", @"f28", @"f29", @"f30", @"f31",
85
86 // 64-bit registers
87 d0, d2, d4, d6, d8, d10, d12, d14,
88 d16, d18, d20, d22, d24, d26, d28, d30,
89 d32, d34, d36, d38, d40, d42, d44, d46,
90 d48, d50, d52, d54, d56, d58, d60, d62,
91
92 // 128-bit registers
93 q0, q4, q8, q12, q16, q20, q24, q28,
94 q32, q36, q40, q44, q48, q52, q56, q60,
95 // zig fmt: on
96
97 pub fn id(self: FloatingPointRegister) u6 {
98 return switch (self.size()) {
99 32 => @truncate(u6, @enumToInt(self)),
100 64 => @truncate(u6, (@enumToInt(self) - 32) * 2),
101 128 => @truncate(u6, (@enumToInt(self) - 64) * 4),
102 else => unreachable,
103 };
104 }
105
106 pub fn enc(self: FloatingPointRegister) u5 {
107 // Floating point registers use an encoding scheme to map from the 6-bit
108 // ID to 5-bit encoded value.
109 // (See section 5.1.4.1 of SPARCv9 ISA specification)
110
111 const reg_id = self.id();
112 return @truncate(u5, reg_id | (reg_id >> 5));
113 }
114
115 /// Returns the bit-width of the register.
116 pub fn size(self: FloatingPointRegister) u8 {
117 return switch (@enumToInt(self)) {
118 0...31 => 32,
119 32...63 => 64,
120 64...79 => 128,
121 else => unreachable,
122 };
123 }
124};
125
126test "FloatingPointRegister.id" {
127 // Low region
128 try testing.expectEqual(@as(u6, 0), FloatingPointRegister.q0.id());
129 try testing.expectEqual(FloatingPointRegister.q0.id(), FloatingPointRegister.d0.id());
130 try testing.expectEqual(FloatingPointRegister.d0.id(), FloatingPointRegister.@"f0".id());
131
132 try testing.expectEqual(@as(u6, 28), FloatingPointRegister.q28.id());
133 try testing.expectEqual(FloatingPointRegister.q28.id(), FloatingPointRegister.d28.id());
134 try testing.expectEqual(FloatingPointRegister.d28.id(), FloatingPointRegister.@"f28".id());
135
136 // High region
137 try testing.expectEqual(@as(u6, 32), FloatingPointRegister.q32.id());
138 try testing.expectEqual(FloatingPointRegister.q32.id(), FloatingPointRegister.d32.id());
139
140 try testing.expectEqual(@as(u6, 60), FloatingPointRegister.q60.id());
141 try testing.expectEqual(FloatingPointRegister.q60.id(), FloatingPointRegister.d60.id());
142}
143
144test "FloatingPointRegister.enc" {
145 // f registers
146 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.@"f0".enc());
147 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.@"f1".enc());
148 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.@"f31".enc());
149
150 // d registers
151 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.enc());
152 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.d32.enc());
153 try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d62.enc());
154
155 // q registers
156 try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.enc());
157 try testing.expectEqual(@as(u5, 1), FloatingPointRegister.q32.enc());
158 try testing.expectEqual(@as(u5, 29), FloatingPointRegister.q60.enc());
159}
160
161/// Represents an instruction in the SPARCv9 instruction set
162pub const Instruction = union(enum) {
163 // Some of the instruction formats have several minor formats, here I
164 // name them with letters since there's no official naming scheme.
165 // TODO: need to rename the minor formats to a more descriptive name.
166
167 // I am using regular structs instead of packed ones to avoid
168 // endianness-dependent behavior when constructing the actual
169 // assembly instructions.
170 // See also: https://github.com/ziglang/zig/issues/10113
171 // TODO: change it back to packed structs once the issue is resolved.
172
173 // Format 1 (op = 1): CALL
174 format_1: struct {
175 op: u2 = 0b01,
176 disp30: u30,
177 },
178
179 // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)
180 format_2a: struct {
181 op: u2 = 0b00,
182 rd: u5,
183 op2: u3,
184 imm22: u22,
185 },
186 format_2b: struct {
187 op: u2 = 0b00,
188 a: u1,
189 cond: u4,
190 op2: u3,
191 disp22: u22,
192 },
193 format_2c: struct {
194 op: u2 = 0b00,
195 a: u1,
196 cond: u4,
197 op2: u3,
198 cc1: u1,
199 cc0: u1,
200 p: u1,
201 disp19: u19,
202 },
203 format_2d: struct {
204 op: u2 = 0b00,
205 a: u1,
206 fixed: u1 = 0b0,
207 rcond: u3,
208 op2: u3,
209 d16hi: u2,
210 p: u1,
211 rs1: u5,
212 d16lo: u14,
213 },
214
215 // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store
216 format_3a: struct {
217 op: u2,
218 rd: u5,
219 op3: u6,
220 rs1: u5,
221 i: u1 = 0b0,
222 reserved: u8 = 0b00000000,
223 rs2: u5,
224 },
225 format_3b: struct {
226 op: u2,
227 rd: u5,
228 op3: u6,
229 rs1: u5,
230 i: u1 = 0b1,
231 simm13: u13,
232 },
233 format_3c: struct {
234 op: u2,
235 reserved1: u5 = 0b00000,
236 op3: u6,
237 rs1: u5,
238 i: u1 = 0b0,
239 reserved2: u8 = 0b00000000,
240 rs2: u5,
241 },
242 format_3d: struct {
243 op: u2,
244 reserved: u5 = 0b00000,
245 op3: u6,
246 rs1: u5,
247 i: u1 = 0b1,
248 simm13: u13,
249 },
250 format_3e: struct {
251 op: u2,
252 rd: u5,
253 op3: u6,
254 rs1: u5,
255 i: u1 = 0b0,
256 rcond: u3,
257 reserved: u5 = 0b00000,
258 rs2: u5,
259 },
260 format_3f: struct {
261 op: u2,
262 rd: u5,
263 op3: u6,
264 rs1: u5,
265 i: u1 = 0b1,
266 rcond: u3,
267 simm10: u10,
268 },
269 format_3g: struct {
270 op: u2,
271 rd: u5,
272 op3: u6,
273 rs1: u5,
274 // See Errata 58 of SPARCv9 specification
275 // https://sparc.org/errata-for-v9/#58
276 i: u1 = 0b1,
277 reserved: u8 = 0b00000000,
278 rs2: u5,
279 },
280 format_3h: struct {
281 op: u2 = 0b10,
282 fixed1: u5 = 0b00000,
283 op3: u6 = 0b101000,
284 fixed2: u5 = 0b01111,
285 i: u1 = 0b1,
286 reserved: u6 = 0b000000,
287 cmask: u3,
288 mmask: u4,
289 },
290 format_3i: struct {
291 op: u2,
292 rd: u5,
293 op3: u6,
294 rs1: u5,
295 i: u1 = 0b0,
296 imm_asi: u8,
297 rs2: u5,
298 },
299 format_3j: struct {
300 op: u2,
301 impl_dep1: u5,
302 op3: u6,
303 impl_dep2: u19,
304 },
305 format_3k: struct {
306 op: u2,
307 rd: u5,
308 op3: u6,
309 rs1: u5,
310 i: u1 = 0b0,
311 x: u1,
312 reserved: u7 = 0b0000000,
313 rs2: u5,
314 },
315 format_3l: struct {
316 op: u2,
317 rd: u5,
318 op3: u6,
319 rs1: u5,
320 i: u1 = 0b1,
321 x: u1 = 0b0,
322 reserved: u7 = 0b0000000,
323 shcnt32: u5,
324 },
325 format_3m: struct {
326 op: u2,
327 rd: u5,
328 op3: u6,
329 rs1: u5,
330 i: u1 = 0b1,
331 x: u1 = 0b1,
332 reserved: u6 = 0b000000,
333 shcnt64: u6,
334 },
335 format_3n: struct {
336 op: u2,
337 rd: u5,
338 op3: u6,
339 reserved: u5 = 0b00000,
340 opf: u9,
341 rs2: u5,
342 },
343 format_3o: struct {
344 op: u2,
345 fixed: u3 = 0b000,
346 cc1: u1,
347 cc0: u1,
348 op3: u6,
349 rs1: u5,
350 opf: u9,
351 rs2: u5,
352 },
353 format_3p: struct {
354 op: u2,
355 rd: u5,
356 op3: u6,
357 rs1: u5,
358 opf: u9,
359 rs2: u5,
360 },
361 format_3q: struct {
362 op: u2,
363 rd: u5,
364 op3: u6,
365 rs1: u5,
366 reserved: u14 = 0b00000000000000,
367 },
368 format_3r: struct {
369 op: u2,
370 fcn: u5,
371 op3: u6,
372 reserved: u19 = 0b0000000000000000000,
373 },
374 format_3s: struct {
375 op: u2,
376 rd: u5,
377 op3: u6,
378 reserved: u19 = 0b0000000000000000000,
379 },
380
381 //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc
382 format_4a: struct {
383 op: u2 = 0b10,
384 rd: u5,
385 op3: u6,
386 rs1: u5,
387 i: u1 = 0b0,
388 cc1: u1,
389 cc0: u1,
390 reserved: u6 = 0b000000,
391 rs2: u5,
392 },
393 format_4b: struct {
394 op: u2 = 0b10,
395 rd: u5,
396 op3: u6,
397 rs1: u5,
398 i: u1 = 0b1,
399 cc1: u1,
400 cc0: u1,
401 simm11: u11,
402 },
403 format_4c: struct {
404 op: u2 = 0b10,
405 rd: u5,
406 op3: u6,
407 cc2: u1,
408 cond: u4,
409 i: u1 = 0b0,
410 cc1: u1,
411 cc0: u1,
412 reserved: u6 = 0b000000,
413 rs2: u5,
414 },
415 format_4d: struct {
416 op: u2 = 0b10,
417 rd: u5,
418 op3: u6,
419 cc2: u1,
420 cond: u4,
421 i: u1 = 0b1,
422 cc1: u1,
423 cc0: u1,
424 simm11: u11,
425 },
426 format_4e: struct {
427 op: u2 = 0b10,
428 rd: u5,
429 op3: u6,
430 rs1: u5,
431 i: u1 = 0b1,
432 cc1: u1,
433 cc0: u1,
434 reserved: u4 = 0b0000,
435 sw_trap: u7,
436 },
437 format_4f: struct {
438 op: u2 = 0b10,
439 rd: u5,
440 op3: u6,
441 rs1: u5,
442 fixed: u1 = 0b0,
443 rcond: u3,
444 opf_low: u5,
445 rs2: u5,
446 },
447 format_4g: struct {
448 op: u2 = 0b10,
449 rd: u5,
450 op3: u6,
451 fixed: u1 = 0b0,
452 cond: u4,
453 opf_cc: u3,
454 opf_low: u6,
455 rs2: u5,
456 },
457
458 pub const CCR = enum(u3) {
459 fcc0,
460 fcc1,
461 fcc2,
462 fcc3,
463 icc,
464 reserved1,
465 xcc,
466 reserved2,
467 };
468
469 pub const RCondition = enum(u3) {
470 reserved1,
471 eq_zero,
472 le_zero,
473 lt_zero,
474 reserved2,
475 ne_zero,
476 gt_zero,
477 ge_zero,
478 };
479
480 pub const ASI = enum(u8) {
481 asi_nucleus = 0x04,
482 asi_nucleus_little = 0x0c,
483 asi_as_if_user_primary = 0x10,
484 asi_as_if_user_secondary = 0x11,
485 asi_as_if_user_primary_little = 0x18,
486 asi_as_if_user_secondary_little = 0x19,
487 asi_primary = 0x80,
488 asi_secondary = 0x81,
489 asi_primary_nofault = 0x82,
490 asi_secondary_nofault = 0x83,
491 asi_primary_little = 0x88,
492 asi_secondary_little = 0x89,
493 asi_primary_nofault_little = 0x8a,
494 asi_secondary_nofault_little = 0x8b,
495 };
496
497 pub const ShiftWidth = enum(u1) {
498 shift32,
499 shift64,
500 };
501
502 pub const MemOrderingConstraint = packed struct {
503 store_store: bool = false,
504 load_store: bool = false,
505 store_load: bool = false,
506 load_load: bool = false,
507 };
508
509 pub const MemCompletionConstraint = packed struct {
510 sync: bool = false,
511 mem_issue: bool = false,
512 lookaside: bool = false,
513 };
514
515 // TODO: Need to define an enum for `cond` values
516 // This is kinda challenging since the cond values have different meanings
517 // depending on whether it's operating on integer or FP CCR.
518 pub const Condition = u4;
519
520 pub fn toU32(self: Instruction) u32 {
521 // TODO: Remove this once packed structs work.
522 return switch (self) {
523 .format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30),
524 .format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22),
525 .format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22),
526 .format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19),
527 .format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo),
528 .format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
529 .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
530 .format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2),
531 .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
532 .format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
533 .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10),
534 .format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
535 .format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask),
536 .format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2),
537 .format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2),
538 .format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
539 .format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32),
540 .format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64),
541 .format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
542 .format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
543 .format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
544 .format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved),
545 .format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
546 .format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
547 .format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
548 .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
549 .format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
550 .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
551 .format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap),
552 .format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
553 .format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
554 };
555 }
556
557 // SPARCv9 Instruction formats.
558 // See section 6.2 of the SPARCv9 ISA manual.
559
560 fn format1(disp: i32) Instruction {
561 const udisp = @bitCast(u32, disp);
562
563 // In SPARC, branch target needs to be aligned to 4 bytes.
564 assert(udisp % 4 == 0);
565
566 // Discard the last two bits since those are implicitly zero.
567 const udisp_truncated = @truncate(u30, udisp >> 2);
568 return Instruction{
569 .format_1 = .{
570 .disp30 = udisp_truncated,
571 },
572 };
573 }
574
575 fn format2a(op2: u3, imm: u22, rd: Register) Instruction {
576 return Instruction{
577 .format_2a = .{
578 .rd = rd.enc(),
579 .op2 = op2,
580 .imm22 = imm,
581 },
582 };
583 }
584
585 fn format2b(op2: u3, cond: Condition, annul: bool, disp: i24) Instruction {
586 const udisp = @bitCast(u24, disp);
587
588 // In SPARC, branch target needs to be aligned to 4 bytes.
589 assert(udisp % 4 == 0);
590
591 // Discard the last two bits since those are implicitly zero.
592 const udisp_truncated = @truncate(u22, udisp >> 2);
593 return Instruction{
594 .format_2b = .{
595 .a = @boolToInt(annul),
596 .cond = cond,
597 .op2 = op2,
598 .disp22 = udisp_truncated,
599 },
600 };
601 }
602
603 fn format2c(op2: u3, cond: Condition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction {
604 const udisp = @bitCast(u21, disp);
605
606 // In SPARC, branch target needs to be aligned to 4 bytes.
607 assert(udisp % 4 == 0);
608
609 // Discard the last two bits since those are implicitly zero.
610 const udisp_truncated = @truncate(u19, udisp >> 2);
611
612 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
613 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
614 return Instruction{
615 .format_2c = .{
616 .a = @boolToInt(annul),
617 .cond = cond,
618 .op2 = op2,
619 .cc1 = ccr_cc1,
620 .cc0 = ccr_cc0,
621 .p = @boolToInt(pt),
622 .disp19 = udisp_truncated,
623 },
624 };
625 }
626
627 fn format2d(op2: u3, rcond: RCondition, annul: bool, pt: bool, rs1: Register, disp: i18) Instruction {
628 const udisp = @bitCast(u18, disp);
629
630 // In SPARC, branch target needs to be aligned to 4 bytes.
631 assert(udisp % 4 == 0);
632
633 // Discard the last two bits since those are implicitly zero,
634 // and split it into low and high parts.
635 const udisp_truncated = @truncate(u16, udisp >> 2);
636 const udisp_hi = @truncate(u2, (udisp_truncated & 0b1100_0000_0000_0000) >> 14);
637 const udisp_lo = @truncate(u14, udisp_truncated & 0b0011_1111_1111_1111);
638 return Instruction{
639 .format_2d = .{
640 .a = @boolToInt(annul),
641 .rcond = @enumToInt(rcond),
642 .op2 = op2,
643 .p = @boolToInt(pt),
644 .rs1 = rs1.enc(),
645 .d16hi = udisp_hi,
646 .d16lo = udisp_lo,
647 },
648 };
649 }
650
651 fn format3a(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction {
652 return Instruction{
653 .format_3a = .{
654 .op = op,
655 .rd = rd.enc(),
656 .op3 = op3,
657 .rs1 = rs1.enc(),
658 .rs2 = rs2.enc(),
659 },
660 };
661 }
662 fn format3b(op: u2, op3: u6, rs1: Register, imm: i13, rd: Register) Instruction {
663 return Instruction{
664 .format_3b = .{
665 .op = op,
666 .rd = rd.enc(),
667 .op3 = op3,
668 .rs1 = rs1.enc(),
669 .simm13 = @bitCast(u13, imm),
670 },
671 };
672 }
673 fn format3c(op: u2, op3: u6, rs1: Register, rs2: Register) Instruction {
674 return Instruction{
675 .format_3c = .{
676 .op = op,
677 .op3 = op3,
678 .rs1 = rs1.enc(),
679 .rs2 = rs2.enc(),
680 },
681 };
682 }
683 fn format3d(op: u2, op3: u6, rs1: Register, imm: i13) Instruction {
684 return Instruction{
685 .format_3d = .{
686 .op = op,
687 .op3 = op3,
688 .rs1 = rs1.enc(),
689 .simm13 = @bitCast(u13, imm),
690 },
691 };
692 }
693 fn format3e(op: u2, op3: u6, rcond: RCondition, rs1: Register, rs2: Register, rd: Register) Instruction {
694 return Instruction{
695 .format_3e = .{
696 .op = op,
697 .rd = rd.enc(),
698 .op3 = op3,
699 .rs1 = rs1.enc(),
700 .rcond = @enumToInt(rcond),
701 .rs2 = rs2.enc(),
702 },
703 };
704 }
705 fn format3f(op: u2, op3: u6, rcond: RCondition, rs1: Register, imm: i10, rd: Register) Instruction {
706 return Instruction{
707 .format_3f = .{
708 .op = op,
709 .rd = rd.enc(),
710 .op3 = op3,
711 .rs1 = rs1.enc(),
712 .rcond = @enumToInt(rcond),
713 .simm10 = @bitCast(u10, imm),
714 },
715 };
716 }
717 fn format3g(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction {
718 return Instruction{
719 .format_3g = .{
720 .op = op,
721 .rd = rd.enc(),
722 .op3 = op3,
723 .rs1 = rs1.enc(),
724 .rs2 = rs2.enc(),
725 },
726 };
727 }
728 fn format3h(cmask: MemCompletionConstraint, mmask: MemOrderingConstraint) Instruction {
729 return Instruction{
730 .format_3h = .{
731 .cmask = @bitCast(u3, cmask),
732 .mmask = @bitCast(u4, mmask),
733 },
734 };
735 }
736 fn format3i(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register, asi: ASI) Instruction {
737 return Instruction{
738 .format_3i = .{
739 .op = op,
740 .rd = rd.enc(),
741 .op3 = op3,
742 .rs1 = rs1.enc(),
743 .imm_asi = @enumToInt(asi),
744 .rs2 = rs2.enc(),
745 },
746 };
747 }
748 fn format3j(op: u2, op3: u6, impl_dep1: u5, impl_dep2: u19) Instruction {
749 return Instruction{
750 .format_3j = .{
751 .op = op,
752 .impl_dep1 = impl_dep1,
753 .op3 = op3,
754 .impl_dep2 = impl_dep2,
755 },
756 };
757 }
758 fn format3k(op: u2, op3: u6, sw: ShiftWidth, rs1: Register, rs2: Register, rd: Register) Instruction {
759 return Instruction{
760 .format_3k = .{
761 .op = op,
762 .rd = rd.enc(),
763 .op3 = op3,
764 .rs1 = rs1.enc(),
765 .x = @enumToInt(sw),
766 .rs2 = rs2.enc(),
767 },
768 };
769 }
770 fn format3l(op: u2, op3: u6, rs1: Register, shift_count: u5, rd: Register) Instruction {
771 return Instruction{
772 .format_3l = .{
773 .op = op,
774 .rd = rd.enc(),
775 .op3 = op3,
776 .rs1 = rs1.enc(),
777 .shcnt32 = shift_count,
778 },
779 };
780 }
781 fn format3m(op: u2, op3: u6, rs1: Register, shift_count: u6, rd: Register) Instruction {
782 return Instruction{
783 .format_3m = .{
784 .op = op,
785 .rd = rd.enc(),
786 .op3 = op3,
787 .rs1 = rs1.enc(),
788 .shcnt64 = shift_count,
789 },
790 };
791 }
792 fn format3n(op: u2, op3: u6, opf: u9, rs2: Register, rd: Register) Instruction {
793 return Instruction{
794 .format_3n = .{
795 .op = op,
796 .rd = rd.enc(),
797 .op3 = op3,
798 .opf = opf,
799 .rs2 = rs2.enc(),
800 },
801 };
802 }
803 fn format3o(op: u2, op3: u6, opf: u9, ccr: CCR, rs1: Register, rs2: Register) Instruction {
804 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
805 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
806 return Instruction{
807 .format_3o = .{
808 .op = op,
809 .cc1 = ccr_cc1,
810 .cc0 = ccr_cc0,
811 .op3 = op3,
812 .rs1 = rs1.enc(),
813 .opf = opf,
814 .rs2 = rs2.enc(),
815 },
816 };
817 }
818 fn format3p(op: u2, op3: u6, opf: u9, rs1: Register, rs2: Register, rd: Register) Instruction {
819 return Instruction{
820 .format_3p = .{
821 .op = op,
822 .rd = rd.enc(),
823 .op3 = op3,
824 .rs1 = rs1.enc(),
825 .opf = opf,
826 .rs2 = rs2.enc(),
827 },
828 };
829 }
830 fn format3q(op: u2, op3: u6, rs1: Register, rd: Register) Instruction {
831 return Instruction{
832 .format_3q = .{
833 .op = op,
834 .rd = rd.enc(),
835 .op3 = op3,
836 .rs1 = rs1.enc(),
837 },
838 };
839 }
840 fn format3r(op: u2, op3: u6, fcn: u5) Instruction {
841 return Instruction{
842 .format_3r = .{
843 .op = op,
844 .fcn = fcn,
845 .op3 = op3,
846 },
847 };
848 }
849 fn format3s(op: u2, op3: u6, rd: Register) Instruction {
850 return Instruction{
851 .format_3s = .{
852 .op = op,
853 .rd = rd.enc(),
854 .op3 = op3,
855 },
856 };
857 }
858
859 fn format4a(op3: u6, ccr: CCR, rs1: Register, rs2: Register, rd: Register) Instruction {
860 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
861 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
862 return Instruction{
863 .format_4a = .{
864 .rd = rd.enc(),
865 .op3 = op3,
866 .rs1 = rs1.enc(),
867 .cc1 = ccr_cc1,
868 .cc0 = ccr_cc0,
869 .rs2 = rs2.enc(),
870 },
871 };
872 }
873
874 fn format4b(op3: u6, ccr: CCR, rs1: Register, imm: i11, rd: Register) Instruction {
875 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
876 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
877 return Instruction{
878 .format_4b = .{
879 .rd = rd.enc(),
880 .op3 = op3,
881 .rs1 = rs1.enc(),
882 .cc1 = ccr_cc1,
883 .cc0 = ccr_cc0,
884 .simm11 = @bitCast(u11, imm),
885 },
886 };
887 }
888
889 fn format4c(op3: u6, cond: Condition, ccr: CCR, rs2: Register, rd: Register) Instruction {
890 const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2);
891 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
892 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
893 return Instruction{
894 .format_4c = .{
895 .rd = rd.enc(),
896 .op3 = op3,
897 .cc2 = ccr_cc2,
898 .cond = cond,
899 .cc1 = ccr_cc1,
900 .cc0 = ccr_cc0,
901 .rs2 = rs2.enc(),
902 },
903 };
904 }
905
906 fn format4d(op3: u6, cond: Condition, ccr: CCR, imm: i11, rd: Register) Instruction {
907 const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2);
908 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
909 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
910 return Instruction{
911 .format_4d = .{
912 .rd = rd.enc(),
913 .op3 = op3,
914 .cc2 = ccr_cc2,
915 .cond = cond,
916 .cc1 = ccr_cc1,
917 .cc0 = ccr_cc0,
918 .simm11 = @bitCast(u11, imm),
919 },
920 };
921 }
922
923 fn format4e(op3: u6, ccr: CCR, rs1: Register, rd: Register, sw_trap: u7) Instruction {
924 const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1);
925 const ccr_cc0 = @truncate(u1, @enumToInt(ccr));
926 return Instruction{
927 .format_4e = .{
928 .rd = rd.enc(),
929 .op3 = op3,
930 .rs1 = rs1.enc(),
931 .cc1 = ccr_cc1,
932 .cc0 = ccr_cc0,
933 .sw_trap = sw_trap,
934 },
935 };
936 }
937
938 fn format4f(
939 op3: u6,
940 opf_low: u5,
941 rcond: RCondition,
942 rs1: Register,
943 rs2: Register,
944 rd: Register,
945 ) Instruction {
946 return Instruction{
947 .format_4f = .{
948 .rd = rd.enc(),
949 .op3 = op3,
950 .rs1 = rs1.enc(),
951 .rcond = @enumToInt(rcond),
952 .opf_low = opf_low,
953 .rs2 = rs2.enc(),
954 },
955 };
956 }
957
958 fn format4g(op3: u6, opf_low: u6, opf_cc: u3, cond: Condition, rs2: Register, rd: Register) Instruction {
959 return Instruction{
960 .format_4g = .{
961 .rd = rd.enc(),
962 .op3 = op3,
963 .cond = cond,
964 .opf_cc = opf_cc,
965 .opf_low = opf_low,
966 .rs2 = rs2.enc(),
967 },
968 };
969 }
970
971 // SPARCv9 Instruction definition.
972 // See appendix A of the SPARCv9 ISA manual.
973
974 pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
975 return switch (s2) {
976 Register => format3a(0b10, 0b00_0000, rs1, rs2, rd),
977 i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd),
978 else => unreachable,
979 };
980 }
981
982 pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
983 return switch (s2) {
984 Register => format3a(0b10, 0b11_1000, rs1, rs2, rd),
985 i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd),
986 else => unreachable,
987 };
988 }
989
990 pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
991 return switch (s2) {
992 Register => format3a(0b11, 0b00_0001, rs1, rs2, rd),
993 i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd),
994 else => unreachable,
995 };
996 }
997
998 pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
999 return switch (s2) {
1000 Register => format3a(0b11, 0b00_0010, rs1, rs2, rd),
1001 i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd),
1002 else => unreachable,
1003 };
1004 }
1005
1006 pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1007 return switch (s2) {
1008 Register => format3a(0b11, 0b00_0000, rs1, rs2, rd),
1009 i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd),
1010 else => unreachable,
1011 };
1012 }
1013
1014 pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1015 return switch (s2) {
1016 Register => format3a(0b11, 0b00_1011, rs1, rs2, rd),
1017 i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd),
1018 else => unreachable,
1019 };
1020 }
1021
1022 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1023 return switch (s2) {
1024 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
1025 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
1026 else => unreachable,
1027 };
1028 }
1029
1030 pub fn nop() Instruction {
1031 return sethi(0, .g0);
1032 }
1033
1034 pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction {
1035 return switch (s2) {
1036 Register => format3c(0b10, 0b11_1001, rs1, rs2),
1037 i13 => format3d(0b10, 0b11_1001, rs1, rs2),
1038 else => unreachable,
1039 };
1040 }
1041
1042 pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1043 return switch (s2) {
1044 Register => format3a(0b10, 0b11_1100, rs1, rs2, rd),
1045 i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd),
1046 else => unreachable,
1047 };
1048 }
1049
1050 pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1051 return switch (s2) {
1052 Register => format3a(0b10, 0b11_1101, rs1, rs2, rd),
1053 i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd),
1054 else => unreachable,
1055 };
1056 }
1057
1058 pub fn sethi(imm: u22, rd: Register) Instruction {
1059 return format2a(0b100, imm, rd);
1060 }
1061
1062 pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1063 return switch (s2) {
1064 Register => format3a(0b11, 0b00_0101, rs1, rs2, rd),
1065 i13 => format3b(0b11, 0b00_0101, rs1, rs2, rd),
1066 else => unreachable,
1067 };
1068 }
1069
1070 pub fn sth(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1071 return switch (s2) {
1072 Register => format3a(0b11, 0b00_0110, rs1, rs2, rd),
1073 i13 => format3b(0b11, 0b00_0110, rs1, rs2, rd),
1074 else => unreachable,
1075 };
1076 }
1077
1078 pub fn stw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1079 return switch (s2) {
1080 Register => format3a(0b11, 0b00_0100, rs1, rs2, rd),
1081 i13 => format3b(0b11, 0b00_0100, rs1, rs2, rd),
1082 else => unreachable,
1083 };
1084 }
1085
1086 pub fn stx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1087 return switch (s2) {
1088 Register => format3a(0b11, 0b00_1110, rs1, rs2, rd),
1089 i13 => format3b(0b11, 0b00_1110, rs1, rs2, rd),
1090 else => unreachable,
1091 };
1092 }
1093
1094 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1095 return switch (s2) {
1096 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
1097 i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd),
1098 else => unreachable,
1099 };
1100 }
1101
1102 pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
1103 // Tcc instructions abuse the rd field to store the conditionals.
1104 return switch (s2) {
1105 Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
1106 u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2),
1107 else => unreachable,
1108 };
1109 }
1110};
1111
1112test "Serialize formats" {
1113 const Testcase = struct {
1114 inst: Instruction,
1115 expected: u32,
1116 };
1117
1118 // Note that the testcases might or might not be a valid instruction
1119 // This is mostly just to check the behavior of the format packed structs
1120 // since currently stage1 doesn't properly implement it in all cases
1121 const testcases = [_]Testcase{
1122 .{
1123 .inst = Instruction.format1(4),
1124 .expected = 0b01_000000000000000000000000000001,
1125 },
1126 .{
1127 .inst = Instruction.format2a(4, 0, .g0),
1128 .expected = 0b00_00000_100_0000000000000000000000,
1129 },
1130 .{
1131 .inst = Instruction.format2b(6, 3, true, -4),
1132 .expected = 0b00_1_0011_110_1111111111111111111111,
1133 },
1134 .{
1135 .inst = Instruction.format2c(3, 0, false, true, .xcc, 8),
1136 .expected = 0b00_0_0000_011_1_0_1_0000000000000000010,
1137 },
1138 .{
1139 .inst = Instruction.format2d(7, .eq_zero, false, true, .o0, 20),
1140 .expected = 0b00_0_0_001_111_00_1_01000_00000000000101,
1141 },
1142 .{
1143 .inst = Instruction.format3a(3, 5, .g0, .o1, .l2),
1144 .expected = 0b11_10010_000101_00000_0_00000000_01001,
1145 },
1146 .{
1147 .inst = Instruction.format3b(3, 5, .g0, -1, .l2),
1148 .expected = 0b11_10010_000101_00000_1_1111111111111,
1149 },
1150 .{
1151 .inst = Instruction.format3c(3, 5, .g0, .o1),
1152 .expected = 0b11_00000_000101_00000_0_00000000_01001,
1153 },
1154 .{
1155 .inst = Instruction.format3d(3, 5, .g0, 0),
1156 .expected = 0b11_00000_000101_00000_1_0000000000000,
1157 },
1158 .{
1159 .inst = Instruction.format3e(3, 5, .ne_zero, .g0, .o1, .l2),
1160 .expected = 0b11_10010_000101_00000_0_101_00000_01001,
1161 },
1162 .{
1163 .inst = Instruction.format3f(3, 5, .ne_zero, .g0, -1, .l2),
1164 .expected = 0b11_10010_000101_00000_1_101_1111111111,
1165 },
1166 .{
1167 .inst = Instruction.format3g(3, 5, .g0, .o1, .l2),
1168 .expected = 0b11_10010_000101_00000_1_00000000_01001,
1169 },
1170 .{
1171 .inst = Instruction.format3h(.{}, .{}),
1172 .expected = 0b10_00000_101000_01111_1_000000_000_0000,
1173 },
1174 .{
1175 .inst = Instruction.format3i(3, 5, .g0, .o1, .l2, .asi_primary_little),
1176 .expected = 0b11_10010_000101_00000_0_10001000_01001,
1177 },
1178 .{
1179 .inst = Instruction.format3j(3, 5, 31, 0),
1180 .expected = 0b11_11111_000101_0000000000000000000,
1181 },
1182 .{
1183 .inst = Instruction.format3k(3, 5, .shift32, .g0, .o1, .l2),
1184 .expected = 0b11_10010_000101_00000_0_0_0000000_01001,
1185 },
1186 .{
1187 .inst = Instruction.format3l(3, 5, .g0, 31, .l2),
1188 .expected = 0b11_10010_000101_00000_1_0_0000000_11111,
1189 },
1190 .{
1191 .inst = Instruction.format3m(3, 5, .g0, 63, .l2),
1192 .expected = 0b11_10010_000101_00000_1_1_000000_111111,
1193 },
1194 .{
1195 .inst = Instruction.format3n(3, 5, 0, .o1, .l2),
1196 .expected = 0b11_10010_000101_00000_000000000_01001,
1197 },
1198 .{
1199 .inst = Instruction.format3o(3, 5, 0, .xcc, .o1, .l2),
1200 .expected = 0b11_000_1_0_000101_01001_000000000_10010,
1201 },
1202 .{
1203 .inst = Instruction.format3p(3, 5, 0, .g0, .o1, .l2),
1204 .expected = 0b11_10010_000101_00000_000000000_01001,
1205 },
1206 .{
1207 .inst = Instruction.format3q(3, 5, .g0, .o1),
1208 .expected = 0b11_01001_000101_00000_00000000000000,
1209 },
1210 .{
1211 .inst = Instruction.format3r(3, 5, 4),
1212 .expected = 0b11_00100_000101_0000000000000000000,
1213 },
1214 .{
1215 .inst = Instruction.format3s(3, 5, .g0),
1216 .expected = 0b11_00000_000101_0000000000000000000,
1217 },
1218 .{
1219 .inst = Instruction.format4a(8, .xcc, .g0, .o1, .l2),
1220 .expected = 0b10_10010_001000_00000_0_1_0_000000_01001,
1221 },
1222 .{
1223 .inst = Instruction.format4b(8, .xcc, .g0, -1, .l2),
1224 .expected = 0b10_10010_001000_00000_1_1_0_11111111111,
1225 },
1226 .{
1227 .inst = Instruction.format4c(8, 0, .xcc, .g0, .o1),
1228 .expected = 0b10_01001_001000_1_0000_0_1_0_000000_00000,
1229 },
1230 .{
1231 .inst = Instruction.format4d(8, 0, .xcc, 0, .l2),
1232 .expected = 0b10_10010_001000_1_0000_1_1_0_00000000000,
1233 },
1234 .{
1235 .inst = Instruction.format4e(8, .xcc, .g0, .o1, 0),
1236 .expected = 0b10_01001_001000_00000_1_1_0_0000_0000000,
1237 },
1238 .{
1239 .inst = Instruction.format4f(8, 4, .eq_zero, .g0, .o1, .l2),
1240 .expected = 0b10_10010_001000_00000_0_001_00100_01001,
1241 },
1242 .{
1243 .inst = Instruction.format4g(8, 4, 2, 0, .o1, .l2),
1244 .expected = 0b10_10010_001000_0_0000_010_000100_01001,
1245 },
1246 };
1247
1248 for (testcases) |case| {
1249 const actual = case.inst.toU32();
1250 testing.expectEqual(case.expected, actual) catch |err| {
1251 std.debug.print("error: {x}\n", .{err});
1252 std.debug.print("case: {x}\n", .{case});
1253 return err;
1254 };
1255 }
1256}
src/codegen.zig+1-1
...@@ -103,7 +103,7 @@ pub fn generateFunction(...@@ -103,7 +103,7 @@ pub fn generateFunction(
103 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),103 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
104 .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),104 .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
105 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),105 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
106 .sparcv9 => return @import("arch/sparcv9/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),106 .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
107 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),107 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
108 //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output),108 //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
109 //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output),109 //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
src/codegen/llvm.zig+2-2
...@@ -53,7 +53,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {...@@ -53,7 +53,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
53 .riscv32 => "riscv32",53 .riscv32 => "riscv32",
54 .riscv64 => "riscv64",54 .riscv64 => "riscv64",
55 .sparc => "sparc",55 .sparc => "sparc",
56 .sparcv9 => "sparcv9",56 .sparc64 => "sparc64",
57 .sparcel => "sparcel",57 .sparcel => "sparcel",
58 .s390x => "s390x",58 .s390x => "s390x",
59 .tce => "tce",59 .tce => "tce",
...@@ -7797,7 +7797,7 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {...@@ -7797,7 +7797,7 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void {
7797 llvm.LLVMInitializeRISCVAsmPrinter();7797 llvm.LLVMInitializeRISCVAsmPrinter();
7798 llvm.LLVMInitializeRISCVAsmParser();7798 llvm.LLVMInitializeRISCVAsmParser();
7799 },7799 },
7800 .sparc, .sparcv9, .sparcel => {7800 .sparc, .sparc64, .sparcel => {
7801 llvm.LLVMInitializeSparcTarget();7801 llvm.LLVMInitializeSparcTarget();
7802 llvm.LLVMInitializeSparcTargetInfo();7802 llvm.LLVMInitializeSparcTargetInfo();
7803 llvm.LLVMInitializeSparcTargetMC();7803 llvm.LLVMInitializeSparcTargetMC();
src/glibc.zig+51-4
...@@ -43,6 +43,29 @@ pub const libs = [_]Lib{...@@ -43,6 +43,29 @@ pub const libs = [_]Lib{
43 .{ .name = "util", .sover = 1 },43 .{ .name = "util", .sover = 1 },
44};44};
4545
46// glibc's naming of Zig architectures
47const Arch = enum(c_int) {
48 arm,
49 armeb,
50 aarch64,
51 aarch64_be,
52 mips,
53 mipsel,
54 mips64,
55 mips64el,
56 powerpc,
57 powerpc64,
58 powerpc64le,
59 riscv32,
60 riscv64,
61 sparc,
62 sparcv9,
63 sparcel,
64 s390x,
65 i386,
66 x86_64,
67};
68
46pub const LoadMetaDataError = error{69pub const LoadMetaDataError = error{
47 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.70 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
48 ZigInstallationCorrupt,71 ZigInstallationCorrupt,
...@@ -134,7 +157,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI...@@ -134,7 +157,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI
134 log.err("abilists: expected ABI name", .{});157 log.err("abilists: expected ABI name", .{});
135 return error.ZigInstallationCorrupt;158 return error.ZigInstallationCorrupt;
136 };159 };
137 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {160 const arch_tag = std.meta.stringToEnum(Arch, arch_name) orelse {
138 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});161 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
139 return error.ZigInstallationCorrupt;162 return error.ZigInstallationCorrupt;
140 };163 };
...@@ -148,7 +171,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI...@@ -148,7 +171,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI
148 };171 };
149172
150 targets[i] = .{173 targets[i] = .{
151 .arch = arch_tag,174 .arch = glibcToZigArch(arch_tag),
152 .os = .linux,175 .os = .linux,
153 .abi = abi_tag,176 .abi = abi_tag,
154 };177 };
...@@ -381,7 +404,7 @@ fn start_asm_path(comp: *Compilation, arena: Allocator, basename: []const u8) ![...@@ -381,7 +404,7 @@ fn start_asm_path(comp: *Compilation, arena: Allocator, basename: []const u8) ![
381 const arch = comp.getTarget().cpu.arch;404 const arch = comp.getTarget().cpu.arch;
382 const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;405 const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
383 const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;406 const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
384 const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;407 const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparc64;
385 const is_64 = arch.ptrBitWidth() == 64;408 const is_64 = arch.ptrBitWidth() == 64;
386409
387 const s = path.sep_str;410 const s = path.sep_str;
...@@ -519,7 +542,7 @@ fn add_include_dirs_arch(...@@ -519,7 +542,7 @@ fn add_include_dirs_arch(
519 const is_x86 = arch == .i386 or arch == .x86_64;542 const is_x86 = arch == .i386 or arch == .x86_64;
520 const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;543 const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
521 const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;544 const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
522 const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;545 const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparc64;
523 const is_64 = arch.ptrBitWidth() == 64;546 const is_64 = arch.ptrBitWidth() == 64;
524547
525 const s = path.sep_str;548 const s = path.sep_str;
...@@ -1115,6 +1138,30 @@ fn buildSharedLib(...@@ -1115,6 +1138,30 @@ fn buildSharedLib(
1115 try sub_compilation.updateSubCompilation();1138 try sub_compilation.updateSubCompilation();
1116}1139}
11171140
1141fn glibcToZigArch(arch_tag: Arch) std.Target.Cpu.Arch {
1142 return switch (arch_tag) {
1143 .arm => .arm,
1144 .armeb => .armeb,
1145 .aarch64 => .aarch64,
1146 .aarch64_be => .aarch64_be,
1147 .mips => .mips,
1148 .mipsel => .mipsel,
1149 .mips64 => .mips64,
1150 .mips64el => .mips64el,
1151 .powerpc => .powerpc,
1152 .powerpc64 => .powerpc64,
1153 .powerpc64le => .powerpc64le,
1154 .riscv32 => .riscv32,
1155 .riscv64 => .riscv64,
1156 .sparc => .sparc,
1157 .sparcv9 => .sparc64, // In glibc, sparc64 is called sparcv9.
1158 .sparcel => .sparcel,
1159 .s390x => .s390x,
1160 .i386 => .i386,
1161 .x86_64 => .x86_64,
1162 };
1163}
1164
1118// Return true if glibc has crti/crtn sources for that architecture.1165// Return true if glibc has crti/crtn sources for that architecture.
1119pub fn needsCrtiCrtn(target: std.Target) bool {1166pub fn needsCrtiCrtn(target: std.Target) bool {
1120 return switch (target.cpu.arch) {1167 return switch (target.cpu.arch) {
src/link/Elf.zig+2-2
...@@ -307,7 +307,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -307,7 +307,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
307307
308 const page_size: u32 = switch (options.target.cpu.arch) {308 const page_size: u32 = switch (options.target.cpu.arch) {
309 .powerpc64le => 0x10000,309 .powerpc64le => 0x10000,
310 .sparcv9 => 0x2000,310 .sparc64 => 0x2000,
311 else => 0x1000,311 else => 0x1000,
312 };312 };
313313
...@@ -2940,7 +2940,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {...@@ -2940,7 +2940,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
2940 .powerpc64 => return "elf64ppc",2940 .powerpc64 => return "elf64ppc",
2941 .powerpc64le => return "elf64lppc",2941 .powerpc64le => return "elf64lppc",
2942 .sparc, .sparcel => return "elf32_sparc",2942 .sparc, .sparcel => return "elf32_sparc",
2943 .sparcv9 => return "elf64_sparc",2943 .sparc64 => return "elf64_sparc",
2944 .mips => return "elf32btsmip",2944 .mips => return "elf32btsmip",
2945 .mipsel => return "elf32ltsmip",2945 .mipsel => return "elf32ltsmip",
2946 .mips64 => {2946 .mips64 => {
src/link/Plan9/aout.zig+1-1
...@@ -110,7 +110,7 @@ pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64...@@ -110,7 +110,7 @@ pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64
110pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {110pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
111 return switch (arch) {111 return switch (arch) {
112 .i386 => I_MAGIC,112 .i386 => I_MAGIC,
113 .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here?113 .sparc => K_MAGIC, // TODO should sparc64 and sparcel go here?
114 .mips => V_MAGIC,114 .mips => V_MAGIC,
115 .arm => E_MAGIC,115 .arm => E_MAGIC,
116 .aarch64 => R_MAGIC,116 .aarch64 => R_MAGIC,
src/stage1/codegen.cpp+8-1
...@@ -21,6 +21,7 @@...@@ -21,6 +21,7 @@
21#include "zigendian.h"21#include "zigendian.h"
2222
23#include <stdio.h>23#include <stdio.h>
24#include <string.h>
24#include <errno.h>25#include <errno.h>
25#include <math.h>26#include <math.h>
2627
...@@ -3596,7 +3597,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL...@@ -3596,7 +3597,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL
3596 result = LLVMBuildLoad(g->builder, result, "");3597 result = LLVMBuildLoad(g->builder, result, "");
3597 }3598 }
35983599
3599 // Some operations are implemented as compound ops and require us to perform some 3600 // Some operations are implemented as compound ops and require us to perform some
3600 // more operations before we obtain the final result3601 // more operations before we obtain the final result
3601 switch (op_id) {3602 switch (op_id) {
3602 case IrBinOpDivTrunc:3603 case IrBinOpDivTrunc:
...@@ -9983,6 +9984,12 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {...@@ -9983,6 +9984,12 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
9983 cur_arch = arch_name;9984 cur_arch = arch_name;
9984 }9985 }
9985 }9986 }
9987
9988 // Workaround to LLVM/Zig naming mismatch.
9989 // LLVM calls it sparcv9, while Zig calls it sparc64.
9990 if (!strcmp(cur_arch, "sparcv9")) {
9991 cur_arch = "sparc64";
9992 }
9986 }9993 }
9987 assert(cur_arch != nullptr);9994 assert(cur_arch != nullptr);
99889995
src/target.zig+6-6
...@@ -63,7 +63,7 @@ pub const available_libcs = [_]ArchOsAbi{...@@ -63,7 +63,7 @@ pub const available_libcs = [_]ArchOsAbi{
63 .{ .arch = .s390x, .os = .linux, .abi = .gnu },63 .{ .arch = .s390x, .os = .linux, .abi = .gnu },
64 .{ .arch = .s390x, .os = .linux, .abi = .musl },64 .{ .arch = .s390x, .os = .linux, .abi = .musl },
65 .{ .arch = .sparc, .os = .linux, .abi = .gnu },65 .{ .arch = .sparc, .os = .linux, .abi = .gnu },
66 .{ .arch = .sparcv9, .os = .linux, .abi = .gnu },66 .{ .arch = .sparc64, .os = .linux, .abi = .gnu },
67 .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },67 .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
68 .{ .arch = .wasm32, .os = .wasi, .abi = .musl },68 .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
69 .{ .arch = .x86_64, .os = .linux, .abi = .gnu },69 .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
...@@ -118,7 +118,7 @@ pub fn osArchName(target: std.Target) [:0]const u8 {...@@ -118,7 +118,7 @@ pub fn osArchName(target: std.Target) [:0]const u8 {
118 .mips, .mipsel, .mips64, .mips64el => "mips",118 .mips, .mipsel, .mips64, .mips64el => "mips",
119 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",119 .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
120 .riscv32, .riscv64 => "riscv",120 .riscv32, .riscv64 => "riscv",
121 .sparc, .sparcel, .sparcv9 => "sparc",121 .sparc, .sparcel, .sparc64 => "sparc",
122 .i386, .x86_64 => "x86",122 .i386, .x86_64 => "x86",
123 else => @tagName(target.cpu.arch),123 else => @tagName(target.cpu.arch),
124 },124 },
...@@ -232,7 +232,7 @@ pub fn hasLlvmSupport(target: std.Target) bool {...@@ -232,7 +232,7 @@ pub fn hasLlvmSupport(target: std.Target) bool {
232 .riscv32,232 .riscv32,
233 .riscv64,233 .riscv64,
234 .sparc,234 .sparc,
235 .sparcv9,235 .sparc64,
236 .sparcel,236 .sparcel,
237 .s390x,237 .s390x,
238 .tce,238 .tce,
...@@ -351,7 +351,7 @@ pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {...@@ -351,7 +351,7 @@ pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
351 .riscv32 => .riscv32,351 .riscv32 => .riscv32,
352 .riscv64 => .riscv64,352 .riscv64 => .riscv64,
353 .sparc => .sparc,353 .sparc => .sparc,
354 .sparcv9 => .sparcv9,354 .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9.
355 .sparcel => .sparcel,355 .sparcel => .sparcel,
356 .s390x => .systemz,356 .s390x => .systemz,
357 .tce => .tce,357 .tce => .tce,
...@@ -617,7 +617,7 @@ pub fn atomicPtrAlignment(...@@ -617,7 +617,7 @@ pub fn atomicPtrAlignment(
617 .powerpc64,617 .powerpc64,
618 .powerpc64le,618 .powerpc64le,
619 .riscv64,619 .riscv64,
620 .sparcv9,620 .sparc64,
621 .s390x,621 .s390x,
622 .amdil64,622 .amdil64,
623 .hsail64,623 .hsail64,
...@@ -723,7 +723,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 {...@@ -723,7 +723,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 {
723 return switch (target.cpu.arch) {723 return switch (target.cpu.arch) {
724 .arm, .armeb => 4,724 .arm, .armeb => 4,
725 .aarch64, .aarch64_32, .aarch64_be => 4,725 .aarch64, .aarch64_32, .aarch64_be => 4,
726 .sparc, .sparcel, .sparcv9 => 4,726 .sparc, .sparcel, .sparc64 => 4,
727 .riscv64 => 2,727 .riscv64 => 2,
728 else => 1,728 else => 1,
729 };729 };
src/type.zig+2-2
...@@ -6229,7 +6229,7 @@ pub const CType = enum {...@@ -6229,7 +6229,7 @@ pub const CType = enum {
6229 .mips64,6229 .mips64,
6230 .mips64el,6230 .mips64el,
6231 .sparc,6231 .sparc,
6232 .sparcv9,6232 .sparc64,
6233 .sparcel,6233 .sparcel,
6234 .powerpc,6234 .powerpc,
6235 .powerpcle,6235 .powerpcle,
...@@ -6267,7 +6267,7 @@ pub const CType = enum {...@@ -6267,7 +6267,7 @@ pub const CType = enum {
6267 .mips64,6267 .mips64,
6268 .mips64el,6268 .mips64el,
6269 .sparc,6269 .sparc,
6270 .sparcv9,6270 .sparc64,
6271 .sparcel,6271 .sparcel,
6272 .powerpc,6272 .powerpc,
6273 .powerpcle,6273 .powerpcle,
test/behavior/align.zig+1-1
...@@ -135,7 +135,7 @@ test "alignment and size of structs with 128-bit fields" {...@@ -135,7 +135,7 @@ test "alignment and size of structs with 128-bit fields" {
135 .powerpc64,135 .powerpc64,
136 .powerpc64le,136 .powerpc64le,
137 .riscv64,137 .riscv64,
138 .sparcv9,138 .sparc64,
139 .x86_64,139 .x86_64,
140 .aarch64,140 .aarch64,
141 .aarch64_be,141 .aarch64_be,
test/behavior/vector.zig+1-1
...@@ -604,7 +604,7 @@ test "vector shift operators" {...@@ -604,7 +604,7 @@ test "vector shift operators" {
604 .mips64,604 .mips64,
605 .mips64el,605 .mips64el,
606 .riscv64,606 .riscv64,
607 .sparcv9,607 .sparc64,
608 => {608 => {
609 // LLVM miscompiles on this architecture609 // LLVM miscompiles on this architecture
610 // https://github.com/ziglang/zig/issues/4951610 // https://github.com/ziglang/zig/issues/4951
test/cases/sparc64-linux/hello_world.zig created+22
...@@ -0,0 +1,22 @@
1const msg = "Hello, World!\n";
2
3fn length() usize {
4 return msg.len;
5}
6
7pub fn main() void {
8 asm volatile ("ta 0x6d"
9 :
10 : [number] "{g1}" (4),
11 [arg1] "{o0}" (1),
12 [arg2] "{o1}" (@ptrToInt(msg)),
13 [arg3] "{o2}" (length()),
14 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
15 );
16}
17
18// run
19// target=sparc64-linux
20//
21// Hello, World!
22//
test/cases/sparcv9-linux/hello_world.zig deleted-22
...@@ -1,22 +0,0 @@
1const msg = "Hello, World!\n";
2
3fn length() usize {
4 return msg.len;
5}
6
7pub fn main() void {
8 asm volatile ("ta 0x6d"
9 :
10 : [number] "{g1}" (4),
11 [arg1] "{o0}" (1),
12 [arg2] "{o1}" (@ptrToInt(msg)),
13 [arg3] "{o2}" (length()),
14 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
15 );
16}
17
18// run
19// target=sparcv9-linux
20//
21// Hello, World!
22//
tools/process_headers.zig+2-2
...@@ -170,13 +170,13 @@ const glibc_targets = [_]LibCTarget{...@@ -170,13 +170,13 @@ const glibc_targets = [_]LibCTarget{
170 .abi = MultiAbi{ .specific = Abi.gnu },170 .abi = MultiAbi{ .specific = Abi.gnu },
171 },171 },
172 LibCTarget{172 LibCTarget{
173 .name = "sparc64-linux-gnu",173 .name = "sparc-linux-gnu",
174 .arch = MultiArch{ .specific = Arch.sparc },174 .arch = MultiArch{ .specific = Arch.sparc },
175 .abi = MultiAbi{ .specific = Abi.gnu },175 .abi = MultiAbi{ .specific = Abi.gnu },
176 },176 },
177 LibCTarget{177 LibCTarget{
178 .name = "sparcv9-linux-gnu",178 .name = "sparcv9-linux-gnu",
179 .arch = MultiArch{ .specific = Arch.sparcv9 },179 .arch = MultiArch{ .specific = Arch.sparc64 },
180 .abi = MultiAbi{ .specific = Abi.gnu },180 .abi = MultiAbi{ .specific = Abi.gnu },
181 },181 },
182 LibCTarget{182 LibCTarget{