| ... | ... | @@ -0,0 +1,155 @@ |
| 1 | const std = @import("std"); |
| 2 | const arch = std.builtin.cpu.arch; |
| 3 | const os = std.builtin.os.tag; |
| 4 | |
| 5 | // Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 |
| 6 | |
| 7 | // The compiler generates calls to __clear_cache() when creating |
| 8 | // trampoline functions on the stack for use with nested functions. |
| 9 | // It is expected to invalidate the instruction cache for the |
| 10 | // specified range. |
| 11 | |
| 12 | pub fn clear_cache(start: usize, end: usize) callconv(.C) void { |
| 13 | const x86 = switch (arch) { |
| 14 | .i386, .x86_64 => true, |
| 15 | else => false, |
| 16 | }; |
| 17 | const arm32 = switch (arch) { |
| 18 | .arm, .armeb, .thumb, .thumbeb => true, |
| 19 | else => false, |
| 20 | }; |
| 21 | const arm64 = switch (arch) { |
| 22 | .aarch64, .aarch64_be, .aarch64_32 => true, |
| 23 | else => false, |
| 24 | }; |
| 25 | const mips = switch (arch) { |
| 26 | .mips, .mipsel, .mips64, .mips64el => true, |
| 27 | else => false, |
| 28 | }; |
| 29 | const powerpc64 = switch (arch) { |
| 30 | .powerpc64, .powerpc64le => true, |
| 31 | else => false, |
| 32 | }; |
| 33 | const sparc = switch (arch) { |
| 34 | .sparc, .sparcv9, .sparcel => true, |
| 35 | else => false, |
| 36 | }; |
| 37 | const apple = switch (os) { |
| 38 | .ios, .macosx, .watchos, .tvos => true, |
| 39 | else => false, |
| 40 | }; |
| 41 | if (x86) { |
| 42 | // Intel processors have a unified instruction and data cache |
| 43 | // so there is nothing to do |
| 44 | } else if (os == .windows and (arm32 or arm64)) { |
| 45 | @compileError("TODO"); |
| 46 | // FlushInstructionCache(GetCurrentProcess(), start, end - start); |
| 47 | } else if (arm32 and !apple) { |
| 48 | @compileError("TODO"); |
| 49 | //#if defined(__FreeBSD__) || defined(__NetBSD__) |
| 50 | // struct arm_sync_icache_args arg; |
| 51 | // |
| 52 | // arg.addr = (uintptr_t)start; |
| 53 | // arg.len = (uintptr_t)end - (uintptr_t)start; |
| 54 | // |
| 55 | // sysarch(ARM_SYNC_ICACHE, &arg); |
| 56 | //#elif defined(__linux__) |
| 57 | //// We used to include asm/unistd.h for the __ARM_NR_cacheflush define, but |
| 58 | //// it also brought many other unused defines, as well as a dependency on |
| 59 | //// kernel headers to be installed. |
| 60 | //// |
| 61 | //// This value is stable at least since Linux 3.13 and should remain so for |
| 62 | //// compatibility reasons, warranting it's re-definition here. |
| 63 | //#define __ARM_NR_cacheflush 0x0f0002 |
| 64 | // register int start_reg __asm("r0") = (int)(intptr_t)start; |
| 65 | // const register int end_reg __asm("r1") = (int)(intptr_t)end; |
| 66 | // const register int flags __asm("r2") = 0; |
| 67 | // const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush; |
| 68 | // __asm __volatile("svc 0x0" |
| 69 | // : "=r"(start_reg) |
| 70 | // : "r"(syscall_nr), "r"(start_reg), "r"(end_reg), "r"(flags)); |
| 71 | // assert(start_reg == 0 && "Cache flush syscall failed."); |
| 72 | //#else |
| 73 | // compilerrt_abort(); |
| 74 | //#endif |
| 75 | } else if (os == .linux and mips) { |
| 76 | @compileError("TODO"); |
| 77 | //const uintptr_t start_int = (uintptr_t)start; |
| 78 | //const uintptr_t end_int = (uintptr_t)end; |
| 79 | //syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); |
| 80 | } else if (mips and os == .openbsd) { |
| 81 | @compileError("TODO"); |
| 82 | //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); |
| 83 | } else if (arm64 and !apple) { |
| 84 | // Get Cache Type Info. |
| 85 | // TODO memoize this? |
| 86 | var ctr_el0: u64 = 0; |
| 87 | asm volatile ( |
| 88 | \\mrs %[x], ctr_el0 |
| 89 | \\ |
| 90 | : [x] "=r" (ctr_el0) |
| 91 | ); |
| 92 | // The DC and IC instructions must use 64-bit registers so we don't use |
| 93 | // uintptr_t in case this runs in an IPL32 environment. |
| 94 | var addr: u64 = undefined; |
| 95 | // If CTR_EL0.IDC is set, data cache cleaning to the point of unification |
| 96 | // is not required for instruction to data coherence. |
| 97 | if (((ctr_el0 >> 28) & 0x1) == 0x0) { |
| 98 | const dcache_line_size: usize = @as(usize, 4) << @intCast(u6, (ctr_el0 >> 16) & 15); |
| 99 | addr = start & ~(dcache_line_size - 1); |
| 100 | while (addr < end) : (addr += dcache_line_size) { |
| 101 | asm volatile ("dc cvau, %[addr]" |
| 102 | : |
| 103 | : [addr] "r" (addr) |
| 104 | ); |
| 105 | } |
| 106 | } |
| 107 | asm volatile ("dsb ish"); |
| 108 | // If CTR_EL0.DIC is set, instruction cache invalidation to the point of |
| 109 | // unification is not required for instruction to data coherence. |
| 110 | if (((ctr_el0 >> 29) & 0x1) == 0x0) { |
| 111 | const icache_line_size: usize = @as(usize, 4) << @intCast(u6, (ctr_el0 >> 0) & 15); |
| 112 | addr = start & ~(icache_line_size - 1); |
| 113 | while (addr < end) : (addr += icache_line_size) { |
| 114 | asm volatile ("ic ivau, %[addr]" |
| 115 | : |
| 116 | : [addr] "r" (addr) |
| 117 | ); |
| 118 | } |
| 119 | } |
| 120 | asm volatile ("isb sy"); |
| 121 | } else if (powerpc64) { |
| 122 | @compileError("TODO"); |
| 123 | //const size_t line_size = 32; |
| 124 | //const size_t len = (uintptr_t)end - (uintptr_t)start; |
| 125 | // |
| 126 | //const uintptr_t mask = ~(line_size - 1); |
| 127 | //const uintptr_t start_line = ((uintptr_t)start) & mask; |
| 128 | //const uintptr_t end_line = ((uintptr_t)start + len + line_size - 1) & mask; |
| 129 | // |
| 130 | //for (uintptr_t line = start_line; line < end_line; line += line_size) |
| 131 | // __asm__ volatile("dcbf 0, %0" : : "r"(line)); |
| 132 | //__asm__ volatile("sync"); |
| 133 | // |
| 134 | //for (uintptr_t line = start_line; line < end_line; line += line_size) |
| 135 | // __asm__ volatile("icbi 0, %0" : : "r"(line)); |
| 136 | //__asm__ volatile("isync"); |
| 137 | } else if (sparc) { |
| 138 | @compileError("TODO"); |
| 139 | //const size_t dword_size = 8; |
| 140 | //const size_t len = (uintptr_t)end - (uintptr_t)start; |
| 141 | // |
| 142 | //const uintptr_t mask = ~(dword_size - 1); |
| 143 | //const uintptr_t start_dword = ((uintptr_t)start) & mask; |
| 144 | //const uintptr_t end_dword = ((uintptr_t)start + len + dword_size - 1) & mask; |
| 145 | // |
| 146 | //for (uintptr_t dword = start_dword; dword < end_dword; dword += dword_size) |
| 147 | // __asm__ volatile("flush %0" : : "r"(dword)); |
| 148 | } else if (apple) { |
| 149 | @compileError("TODO"); |
| 150 | //// On Darwin, sys_icache_invalidate() provides this functionality |
| 151 | //sys_icache_invalidate(start, end - start); |
| 152 | } else { |
| 153 | @compileError("no __clear_cache implementation available for this target"); |
| 154 | } |
| 155 | } |