| author | |
| committer | |
| log | fb0692334ed64a995690c21525fc3e729e63f424 |
| tree | 1a7a8f2617bf890c66ba965a6cc49352b9c48629 |
| parent | aceb7e18bd1eb25affe8118e01e066c9e6bb0c04 |
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 | riscv32 | 11392 | riscv32 |
| 11393 | riscv64 | 11393 | riscv64 |
| 11394 | sparc | 11394 | sparc |
| 11395 | sparcv9 | 11395 | sparc64 |
| 11396 | sparcel | 11396 | sparcel |
| 11397 | s390x | 11397 | s390x |
| 11398 | thumb | 11398 | thumb |
| ... | @@ -11543,7 +11543,7 @@ Available libcs: | ... | @@ -11543,7 +11543,7 @@ Available libcs: |
| 11543 | s390x-linux-gnu | 11543 | s390x-linux-gnu |
| 11544 | s390x-linux-musl | 11544 | s390x-linux-musl |
| 11545 | sparc-linux-gnu | 11545 | sparc-linux-gnu |
| 11546 | sparcv9-linux-gnu | 11546 | sparc64-linux-gnu |
| 11547 | wasm32-freestanding-musl | 11547 | wasm32-freestanding-musl |
| 11548 | wasm32-wasi-musl | 11548 | wasm32-wasi-musl |
| 11549 | x86_64-linux-gnu | 11549 | 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 | \\ sc | 625 | \\ 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, sp | 630 | // 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 frames | 863 | \\ # SPARCs really don't like it when active stack frames |
| 864 | \\ # is unmapped (it will result in a segfault), so we | 864 | \\ # is unmapped (it will result in a segfault), so we |
| 865 | \\ # force-deactivate it by running `restore` until | 865 | \\ # 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 the | 729 | /// The reference implementation self-hosted compiler of Zig, using the |
| 730 | /// sparcv9 backend. | 730 | /// sparc64 backend. |
| 731 | stage2_sparcv9 = 10, | 731 | stage2_sparc64 = 10, |
| 732 | 732 | ||
| 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 or | 775 | builtin.zig_backend == .stage2_x86_64 or |
| 776 | builtin.zig_backend == .stage2_x86 or | 776 | builtin.zig_backend == .stage2_x86 or |
| 777 | builtin.zig_backend == .stage2_riscv64 or | 777 | 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 | }; |
| 116 | 116 | ||
| 117 | pub const Stat = switch (native_arch) { | 117 | pub 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 | }; |
| 106 | 106 | ||
| 107 | const padded_pthread_spin_t = switch (builtin.cpu.arch) { | 107 | const 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 | }; |
| 112 | 112 | ||
| ... | @@ -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) { |
| 1263 | 1263 | ||
| 1264 | const _MAX_PAGE_SHIFT = switch (builtin.cpu.arch) { | 1264 | const _MAX_PAGE_SHIFT = switch (builtin.cpu.arch) { |
| 1265 | .i386 => 12, | 1265 | .i386 => 12, |
| 1266 | .sparcv9 => 13, | 1266 | .sparc64 => 13, |
| 1267 | }; | 1267 | }; |
| 1268 | pub const MINSIGSTKSZ = 1 << _MAX_PAGE_SHIFT; | 1268 | pub const MINSIGSTKSZ = 1 << _MAX_PAGE_SHIFT; |
| 1269 | pub const SIGSTKSZ = MINSIGSTKSZ + (1 << _MAX_PAGE_SHIFT) * 4; | 1269 | pub 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, |
| 402 | 402 | ||
| 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 | \\ flushw | 407 | \\ 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 a | 1517 | // 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 | }; |
| 24 | 24 |
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 |
| 1098 | 1098 | ||
| 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 { |
| 1698 | 1698 | ||
| 1699 | pub const E = switch (native_arch) { | 1699 | pub 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 | }; |
| 1704 | 1704 | ||
| ... | @@ -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), |
| 5459 | 5459 | ||
| 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 { |
| 49 | 49 | ||
| 50 | const tls_variant = switch (native_arch) { | 50 | const 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 | }; |
| 55 | 55 | ||
| ... | @@ -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], %%g7 | 179 | \\ 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 or | 29 | builtin.zig_backend == .stage2_aarch64 or |
| 30 | builtin.zig_backend == .stage2_arm or | 30 | builtin.zig_backend == .stage2_arm or |
| 31 | builtin.zig_backend == .stage2_riscv64 or | 31 | 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 bias | 327 | // 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, %%i6 | 329 | \\ 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 { |
| 884 | 884 | ||
| 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, |
| 1821 | 1821 | ||
| 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 { |
| 66 | const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); | 66 | const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); |
| 67 | 67 | ||
| 68 | test "cpuinfo: SPARC" { | 68 | test "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 FPU | 71 | \\fpu : UltraSparc T2 integrated FPU |
| 72 | \\pmu : niagara2 | 72 | \\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. | ||
| 5 | const std = @import("std"); | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const log = std.log.scoped(.codegen); | ||
| 8 | const math = std.math; | ||
| 9 | const mem = std.mem; | ||
| 10 | const Allocator = mem.Allocator; | ||
| 11 | const builtin = @import("builtin"); | ||
| 12 | const link = @import("../../link.zig"); | ||
| 13 | const Module = @import("../../Module.zig"); | ||
| 14 | const TypedValue = @import("../../TypedValue.zig"); | ||
| 15 | const ErrorMsg = Module.ErrorMsg; | ||
| 16 | const Air = @import("../../Air.zig"); | ||
| 17 | const Mir = @import("Mir.zig"); | ||
| 18 | const Emit = @import("Emit.zig"); | ||
| 19 | const Liveness = @import("../../Liveness.zig"); | ||
| 20 | const Type = @import("../../type.zig").Type; | ||
| 21 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | ||
| 22 | const FnResult = @import("../../codegen.zig").FnResult; | ||
| 23 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | ||
| 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | ||
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); | ||
| 26 | |||
| 27 | const build_options = @import("build_options"); | ||
| 28 | |||
| 29 | const bits = @import("bits.zig"); | ||
| 30 | const abi = @import("abi.zig"); | ||
| 31 | const Register = bits.Register; | ||
| 32 | |||
| 33 | const Self = @This(); | ||
| 34 | |||
| 35 | const InnerError = error{ | ||
| 36 | OutOfMemory, | ||
| 37 | CodegenFail, | ||
| 38 | OutOfRegisters, | ||
| 39 | }; | ||
| 40 | |||
| 41 | const RegisterView = enum(u1) { | ||
| 42 | caller, | ||
| 43 | callee, | ||
| 44 | }; | ||
| 45 | |||
| 46 | gpa: Allocator, | ||
| 47 | air: Air, | ||
| 48 | liveness: Liveness, | ||
| 49 | bin_file: *link.File, | ||
| 50 | target: *const std.Target, | ||
| 51 | mod_fn: *const Module.Fn, | ||
| 52 | code: *std.ArrayList(u8), | ||
| 53 | debug_output: DebugInfoOutput, | ||
| 54 | err_msg: ?*ErrorMsg, | ||
| 55 | args: []MCValue, | ||
| 56 | ret_mcv: MCValue, | ||
| 57 | fn_type: Type, | ||
| 58 | arg_index: usize, | ||
| 59 | src_loc: Module.SrcLoc, | ||
| 60 | stack_align: u32, | ||
| 61 | |||
| 62 | /// MIR Instructions | ||
| 63 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ||
| 64 | /// MIR extra data | ||
| 65 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, | ||
| 66 | |||
| 67 | /// Byte offset within the source file of the ending curly. | ||
| 68 | end_di_line: u32, | ||
| 69 | end_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. | ||
| 74 | exitlude_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. | ||
| 83 | branch_stack: *std.ArrayList(Branch), | ||
| 84 | |||
| 85 | // Key is the block instruction | ||
| 86 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | ||
| 87 | |||
| 88 | register_manager: RegisterManager = .{}, | ||
| 89 | |||
| 90 | /// Maps offset to what is stored there. | ||
| 91 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | ||
| 92 | |||
| 93 | /// Offset from the stack base, representing the end of the stack frame. | ||
| 94 | max_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`. | ||
| 97 | next_stack_offset: u32 = 0, | ||
| 98 | |||
| 99 | /// Debug field, used to find bugs in the compiler. | ||
| 100 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | ||
| 101 | |||
| 102 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | ||
| 103 | |||
| 104 | const 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 | |||
| 162 | const 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 | |||
| 171 | const 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 | |||
| 177 | const 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 | |||
| 186 | const 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 | |||
| 198 | const 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 | |||
| 235 | pub 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 | |||
| 338 | fn 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 | |||
| 452 | fn 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 | |||
| 651 | fn 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 | |||
| 771 | fn 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 | |||
| 798 | fn 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 | |||
| 832 | fn 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 | |||
| 847 | fn 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 | |||
| 963 | fn 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 | |||
| 968 | fn 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 | |||
| 976 | fn 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 | |||
| 992 | fn 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 | |||
| 1001 | fn 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 | |||
| 1007 | fn 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 | |||
| 1014 | fn 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 | |||
| 1022 | fn 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 | |||
| 1027 | fn 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 | |||
| 1034 | fn 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. | ||
| 1045 | fn 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 | |||
| 1063 | fn 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 | |||
| 1071 | fn 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. | ||
| 1087 | fn 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 | |||
| 1107 | fn 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. | ||
| 1132 | fn 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 | |||
| 1138 | fn 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 | |||
| 1143 | fn 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. | ||
| 1151 | fn finishAirBookkeeping(self: *Self) void { | ||
| 1152 | if (std.debug.runtime_safety) { | ||
| 1153 | self.air_bookkeeping += 1; | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | fn 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 | |||
| 1191 | fn 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 | |||
| 1225 | fn 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 | |||
| 1258 | fn 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 | |||
| 1412 | fn 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 | |||
| 1445 | fn 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 | |||
| 1478 | fn 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 | |||
| 1513 | fn 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 | |||
| 1525 | fn 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 | |||
| 1536 | fn 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 | |||
| 1561 | fn 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 | |||
| 1568 | fn 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. | ||
| 1577 | fn 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`. | ||
| 1593 | fn 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 | |||
| 1681 | fn 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 | |||
| 1717 | fn 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 | |||
| 1729 | fn 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. | ||
| 1761 | fn 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 | |||
| 1773 | pub 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`. | ||
| 1784 | fn 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 | |||
| 4 | const std = @import("std"); | ||
| 5 | const Endian = std.builtin.Endian; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const link = @import("../../link.zig"); | ||
| 8 | const Module = @import("../../Module.zig"); | ||
| 9 | const ErrorMsg = Module.ErrorMsg; | ||
| 10 | const Liveness = @import("../../Liveness.zig"); | ||
| 11 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | ||
| 12 | const DW = std.dwarf; | ||
| 13 | const leb128 = std.leb; | ||
| 14 | |||
| 15 | const Emit = @This(); | ||
| 16 | const Mir = @import("Mir.zig"); | ||
| 17 | const bits = @import("bits.zig"); | ||
| 18 | const Instruction = bits.Instruction; | ||
| 19 | const Register = bits.Register; | ||
| 20 | |||
| 21 | mir: Mir, | ||
| 22 | bin_file: *link.File, | ||
| 23 | debug_output: DebugInfoOutput, | ||
| 24 | target: *const std.Target, | ||
| 25 | err_msg: ?*ErrorMsg = null, | ||
| 26 | src_loc: Module.SrcLoc, | ||
| 27 | code: *std.ArrayList(u8), | ||
| 28 | |||
| 29 | prev_di_line: u32, | ||
| 30 | prev_di_column: u32, | ||
| 31 | /// Relative to the beginning of `code`. | ||
| 32 | prev_di_pc: usize, | ||
| 33 | |||
| 34 | const InnerError = error{ | ||
| 35 | OutOfMemory, | ||
| 36 | EmitFail, | ||
| 37 | }; | ||
| 38 | |||
| 39 | pub 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 | |||
| 90 | pub fn deinit(emit: *Emit) void { | ||
| 91 | emit.* = undefined; | ||
| 92 | } | ||
| 93 | |||
| 94 | fn 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 | |||
| 104 | fn 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 | |||
| 115 | fn 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 | |||
| 126 | fn 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 | |||
| 147 | fn 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 | |||
| 195 | fn mirNop(emit: *Emit) !void { | ||
| 196 | try emit.writeInstruction(Instruction.nop()); | ||
| 197 | } | ||
| 198 | |||
| 199 | fn 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 | |||
| 210 | fn 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 | |||
| 235 | fn 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 | |||
| 260 | fn 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 | |||
| 267 | fn 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 | |||
| 9 | const std = @import("std"); | ||
| 10 | const builtin = @import("builtin"); | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | |||
| 13 | const Mir = @This(); | ||
| 14 | const bits = @import("bits.zig"); | ||
| 15 | const Air = @import("../../Air.zig"); | ||
| 16 | |||
| 17 | const Instruction = bits.Instruction; | ||
| 18 | const Register = bits.Register; | ||
| 19 | |||
| 20 | instructions: std.MultiArrayList(Inst).Slice, | ||
| 21 | |||
| 22 | /// The meaning of this data is determined by `Inst.Tag` value. | ||
| 23 | extra: []const u32, | ||
| 24 | |||
| 25 | pub 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 | |||
| 232 | pub 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. | ||
| 240 | pub 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 @@ | ||
| 1 | const bits = @import("bits.zig"); | ||
| 2 | const 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. | ||
| 9 | pub 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. | ||
| 14 | pub 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. | ||
| 21 | pub 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. | ||
| 24 | pub 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 | |||
| 33 | pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; | ||
| 34 | pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; | ||
| 35 | |||
| 36 | pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 }; | ||
| 37 | pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" }; | ||
src/arch/sparc64/bits.zig created+1256| ... | @@ -0,0 +1,1256 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | /// General purpose registers in the SPARCv9 instruction set | ||
| 7 | pub 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 | |||
| 32 | test "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 | |||
| 48 | test "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 | ||
| 63 | pub 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 | |||
| 126 | test "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 | |||
| 144 | test "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 | ||
| 162 | pub 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 | |||
| 1112 | test "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. | ||
| 5 | const std = @import("std"); | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const log = std.log.scoped(.codegen); | ||
| 8 | const math = std.math; | ||
| 9 | const mem = std.mem; | ||
| 10 | const Allocator = mem.Allocator; | ||
| 11 | const builtin = @import("builtin"); | ||
| 12 | const link = @import("../../link.zig"); | ||
| 13 | const Module = @import("../../Module.zig"); | ||
| 14 | const TypedValue = @import("../../TypedValue.zig"); | ||
| 15 | const ErrorMsg = Module.ErrorMsg; | ||
| 16 | const Air = @import("../../Air.zig"); | ||
| 17 | const Mir = @import("Mir.zig"); | ||
| 18 | const Emit = @import("Emit.zig"); | ||
| 19 | const Liveness = @import("../../Liveness.zig"); | ||
| 20 | const Type = @import("../../type.zig").Type; | ||
| 21 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | ||
| 22 | const FnResult = @import("../../codegen.zig").FnResult; | ||
| 23 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | ||
| 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | ||
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); | ||
| 26 | |||
| 27 | const build_options = @import("build_options"); | ||
| 28 | |||
| 29 | const bits = @import("bits.zig"); | ||
| 30 | const abi = @import("abi.zig"); | ||
| 31 | const Register = bits.Register; | ||
| 32 | |||
| 33 | const Self = @This(); | ||
| 34 | |||
| 35 | const InnerError = error{ | ||
| 36 | OutOfMemory, | ||
| 37 | CodegenFail, | ||
| 38 | OutOfRegisters, | ||
| 39 | }; | ||
| 40 | |||
| 41 | const RegisterView = enum(u1) { | ||
| 42 | caller, | ||
| 43 | callee, | ||
| 44 | }; | ||
| 45 | |||
| 46 | gpa: Allocator, | ||
| 47 | air: Air, | ||
| 48 | liveness: Liveness, | ||
| 49 | bin_file: *link.File, | ||
| 50 | target: *const std.Target, | ||
| 51 | mod_fn: *const Module.Fn, | ||
| 52 | code: *std.ArrayList(u8), | ||
| 53 | debug_output: DebugInfoOutput, | ||
| 54 | err_msg: ?*ErrorMsg, | ||
| 55 | args: []MCValue, | ||
| 56 | ret_mcv: MCValue, | ||
| 57 | fn_type: Type, | ||
| 58 | arg_index: usize, | ||
| 59 | src_loc: Module.SrcLoc, | ||
| 60 | stack_align: u32, | ||
| 61 | |||
| 62 | /// MIR Instructions | ||
| 63 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ||
| 64 | /// MIR extra data | ||
| 65 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, | ||
| 66 | |||
| 67 | /// Byte offset within the source file of the ending curly. | ||
| 68 | end_di_line: u32, | ||
| 69 | end_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. | ||
| 74 | exitlude_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. | ||
| 83 | branch_stack: *std.ArrayList(Branch), | ||
| 84 | |||
| 85 | // Key is the block instruction | ||
| 86 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | ||
| 87 | |||
| 88 | register_manager: RegisterManager = .{}, | ||
| 89 | |||
| 90 | /// Maps offset to what is stored there. | ||
| 91 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | ||
| 92 | |||
| 93 | /// Offset from the stack base, representing the end of the stack frame. | ||
| 94 | max_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`. | ||
| 97 | next_stack_offset: u32 = 0, | ||
| 98 | |||
| 99 | /// Debug field, used to find bugs in the compiler. | ||
| 100 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | ||
| 101 | |||
| 102 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | ||
| 103 | |||
| 104 | const 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 | |||
| 162 | const 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 | |||
| 171 | const 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 | |||
| 177 | const 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 | |||
| 186 | const 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 | |||
| 198 | const 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 | |||
| 235 | pub 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 | |||
| 338 | fn 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 | |||
| 452 | fn 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 | |||
| 651 | fn 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 | |||
| 771 | fn 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 | |||
| 798 | fn 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 | |||
| 832 | fn 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 | |||
| 847 | fn 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 | |||
| 963 | fn 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 | |||
| 968 | fn 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 | |||
| 976 | fn 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 | |||
| 992 | fn 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 | |||
| 1001 | fn 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 | |||
| 1007 | fn 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 | |||
| 1014 | fn 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 | |||
| 1022 | fn 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 | |||
| 1027 | fn 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 | |||
| 1034 | fn 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. | ||
| 1045 | fn 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 | |||
| 1063 | fn 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 | |||
| 1071 | fn 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. | ||
| 1087 | fn 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 | |||
| 1107 | fn 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. | ||
| 1132 | fn 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 | |||
| 1138 | fn 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 | |||
| 1143 | fn 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. | ||
| 1151 | fn finishAirBookkeeping(self: *Self) void { | ||
| 1152 | if (std.debug.runtime_safety) { | ||
| 1153 | self.air_bookkeeping += 1; | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | fn 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 | |||
| 1191 | fn 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 | |||
| 1225 | fn 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 | |||
| 1258 | fn 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 | |||
| 1412 | fn 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 | |||
| 1445 | fn 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 | |||
| 1478 | fn 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 | |||
| 1513 | fn 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 | |||
| 1525 | fn 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 | |||
| 1536 | fn 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 | |||
| 1561 | fn 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 | |||
| 1568 | fn 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. | ||
| 1577 | fn 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`. | ||
| 1593 | fn 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 | |||
| 1681 | fn 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 | |||
| 1717 | fn 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 | |||
| 1729 | fn 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. | ||
| 1761 | fn 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 | |||
| 1773 | pub 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`. | ||
| 1784 | fn 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 | |||
| 4 | const std = @import("std"); | ||
| 5 | const Endian = std.builtin.Endian; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const link = @import("../../link.zig"); | ||
| 8 | const Module = @import("../../Module.zig"); | ||
| 9 | const ErrorMsg = Module.ErrorMsg; | ||
| 10 | const Liveness = @import("../../Liveness.zig"); | ||
| 11 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | ||
| 12 | const DW = std.dwarf; | ||
| 13 | const leb128 = std.leb; | ||
| 14 | |||
| 15 | const Emit = @This(); | ||
| 16 | const Mir = @import("Mir.zig"); | ||
| 17 | const bits = @import("bits.zig"); | ||
| 18 | const Instruction = bits.Instruction; | ||
| 19 | const Register = bits.Register; | ||
| 20 | |||
| 21 | mir: Mir, | ||
| 22 | bin_file: *link.File, | ||
| 23 | debug_output: DebugInfoOutput, | ||
| 24 | target: *const std.Target, | ||
| 25 | err_msg: ?*ErrorMsg = null, | ||
| 26 | src_loc: Module.SrcLoc, | ||
| 27 | code: *std.ArrayList(u8), | ||
| 28 | |||
| 29 | prev_di_line: u32, | ||
| 30 | prev_di_column: u32, | ||
| 31 | /// Relative to the beginning of `code`. | ||
| 32 | prev_di_pc: usize, | ||
| 33 | |||
| 34 | const InnerError = error{ | ||
| 35 | OutOfMemory, | ||
| 36 | EmitFail, | ||
| 37 | }; | ||
| 38 | |||
| 39 | pub 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 | |||
| 90 | pub fn deinit(emit: *Emit) void { | ||
| 91 | emit.* = undefined; | ||
| 92 | } | ||
| 93 | |||
| 94 | fn 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 | |||
| 104 | fn 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 | |||
| 115 | fn 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 | |||
| 126 | fn 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 | |||
| 147 | fn 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 | |||
| 195 | fn mirNop(emit: *Emit) !void { | ||
| 196 | try emit.writeInstruction(Instruction.nop()); | ||
| 197 | } | ||
| 198 | |||
| 199 | fn 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 | |||
| 210 | fn 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 | |||
| 235 | fn 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 | |||
| 260 | fn 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 | |||
| 267 | fn 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 | |||
| 9 | const std = @import("std"); | ||
| 10 | const builtin = @import("builtin"); | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | |||
| 13 | const Mir = @This(); | ||
| 14 | const bits = @import("bits.zig"); | ||
| 15 | const Air = @import("../../Air.zig"); | ||
| 16 | |||
| 17 | const Instruction = bits.Instruction; | ||
| 18 | const Register = bits.Register; | ||
| 19 | |||
| 20 | instructions: std.MultiArrayList(Inst).Slice, | ||
| 21 | |||
| 22 | /// The meaning of this data is determined by `Inst.Tag` value. | ||
| 23 | extra: []const u32, | ||
| 24 | |||
| 25 | pub 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 | |||
| 232 | pub 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. | ||
| 240 | pub 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 @@ | ||
| 1 | const bits = @import("bits.zig"); | ||
| 2 | const 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. | ||
| 9 | pub 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. | ||
| 14 | pub 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. | ||
| 21 | pub 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. | ||
| 24 | pub 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 | |||
| 33 | pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; | ||
| 34 | pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; | ||
| 35 | |||
| 36 | pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 }; | ||
| 37 | pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" }; | ||
src/arch/sparcv9/bits.zig deleted-1256| ... | @@ -1,1256 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const DW = std.dwarf; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | /// General purpose registers in the SPARCv9 instruction set | ||
| 7 | pub 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 | |||
| 32 | test "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 | |||
| 48 | test "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 | ||
| 63 | pub 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 | |||
| 126 | test "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 | |||
| 144 | test "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 | ||
| 162 | pub 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 | |||
| 1112 | test "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 | }; |
| 45 | 45 | ||
| 46 | // glibc's naming of Zig architectures | ||
| 47 | const 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 | |||
| 46 | pub const LoadMetaDataError = error{ | 69 | pub 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 | }; |
| 149 | 172 | ||
| 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; |
| 386 | 409 | ||
| 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; |
| 524 | 547 | ||
| 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 | } |
| 1117 | 1140 | ||
| 1141 | fn 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. |
| 1119 | pub fn needsCrtiCrtn(target: std.Target) bool { | 1166 | pub 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 { |
| 307 | 307 | ||
| 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 | }; |
| 313 | 313 | ||
| ... | @@ -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 |
| 110 | pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { | 110 | pub 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" |
| 22 | 22 | ||
| 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> |
| 26 | 27 | ||
| ... | @@ -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 | } |
| 3598 | 3599 | ||
| 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 result | 3601 | // 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); |
| 9988 | 9995 |
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 architecture | 609 | // LLVM miscompiles on this architecture |
| 610 | // https://github.com/ziglang/zig/issues/4951 | 610 | // https://github.com/ziglang/zig/issues/4951 |
test/cases/sparc64-linux/hello_world.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | const msg = "Hello, World!\n"; | ||
| 2 | |||
| 3 | fn length() usize { | ||
| 4 | return msg.len; | ||
| 5 | } | ||
| 6 | |||
| 7 | pub 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 @@ | ||
| 1 | const msg = "Hello, World!\n"; | ||
| 2 | |||
| 3 | fn length() usize { | ||
| 4 | return msg.len; | ||
| 5 | } | ||
| 6 | |||
| 7 | pub 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{ |