clear_cache.zig (7746B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const arch = builtin.cpu.arch; 4 const os = builtin.os.tag; 5 const common = @import("common.zig"); 6 pub const panic = common.panic; 7 8 // Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 9 10 // The compiler generates calls to __clear_cache() when creating 11 // trampoline functions on the stack for use with nested functions. 12 // It is expected to invalidate the instruction cache for the 13 // specified range. 14 15 comptime { 16 _ = &clear_cache; 17 } 18 19 fn clear_cache(start: usize, end: usize) callconv(.c) void { 20 const x86 = switch (arch) { 21 .x86, .x86_64 => true, 22 else => false, 23 }; 24 const arm32 = switch (arch) { 25 .arm, .armeb, .thumb, .thumbeb => true, 26 else => false, 27 }; 28 const arm64 = switch (arch) { 29 .aarch64, .aarch64_be => true, 30 else => false, 31 }; 32 const loongarch = switch (arch) { 33 .loongarch32, 34 .loongarch64, 35 => true, 36 else => false, 37 }; 38 const mips = switch (arch) { 39 .mips, .mipsel, .mips64, .mips64el => true, 40 else => false, 41 }; 42 const riscv = switch (arch) { 43 .riscv32, .riscv64 => true, 44 else => false, 45 }; 46 const powerpc64 = switch (arch) { 47 .powerpc64, .powerpc64le => true, 48 else => false, 49 }; 50 const sparc = switch (arch) { 51 .sparc, .sparc64 => true, 52 else => false, 53 }; 54 const apple = switch (os) { 55 .ios, .macos, .watchos, .tvos, .visionos => true, 56 else => false, 57 }; 58 if (x86) { 59 // Intel processors have a unified instruction and data cache 60 // so there is nothing to do 61 exportIt(); 62 } else if (os == .windows and (arm32 or arm64)) { 63 // TODO 64 // FlushInstructionCache(GetCurrentProcess(), start, end - start); 65 // exportIt(); 66 } else if (arm32 and !apple) { 67 switch (os) { 68 .freebsd, .netbsd => { 69 var arg = arm_sync_icache_args{ 70 .addr = start, 71 .len = end - start, 72 }; 73 const result = sysarch(ARM_SYNC_ICACHE, @intFromPtr(&arg)); 74 std.debug.assert(result == 0); 75 exportIt(); 76 }, 77 .linux => { 78 const result = std.os.linux.syscall3(.cacheflush, start, end, 0); 79 std.debug.assert(result == 0); 80 exportIt(); 81 }, 82 else => {}, 83 } 84 } else if (os == .linux and mips) { 85 const flags = 3; // ICACHE | DCACHE 86 const result = std.os.linux.syscall3(.cacheflush, start, end - start, flags); 87 std.debug.assert(result == 0); 88 exportIt(); 89 } else if (os == .netbsd and mips) { 90 // Replace with https://github.com/ziglang/zig/issues/23904 in the future. 91 const cfa: extern struct { 92 va: usize, 93 nbytes: usize, 94 whichcache: u32, 95 } = .{ 96 .va = start, 97 .nbytes = end - start, 98 .whichcache = 3, // ICACHE | DCACHE 99 }; 100 asm volatile ("syscall" 101 : 102 : [_] "{$2}" (165), // nr = SYS_sysarch 103 [_] "{$4}" (0), // op = MIPS_CACHEFLUSH 104 [_] "{$5}" (&cfa), // args = &cfa 105 : .{ .r1 = true, .r2 = true, .r3 = true, .r4 = true, .r5 = true, .r6 = true, .r7 = true, .r8 = true, .r9 = true, .r10 = true, .r11 = true, .r12 = true, .r13 = true, .r14 = true, .r15 = true, .r24 = true, .r25 = true, .hi = true, .lo = true, .memory = true }); 106 exportIt(); 107 } else if (mips and os == .openbsd) { 108 // TODO 109 //cacheflush(start, (uintptr_t)end - (uintptr_t)start, BCACHE); 110 // exportIt(); 111 } else if (os == .linux and riscv) { 112 const result = std.os.linux.syscall3(.riscv_flush_icache, start, end - start, 0); 113 std.debug.assert(result == 0); 114 exportIt(); 115 } else if (arm64 and !apple) { 116 // Get Cache Type Info. 117 // TODO memoize this? 118 const ctr_el0 = asm volatile ("mrs %[ctr_el0], ctr_el0" 119 : [ctr_el0] "=r" (-> u64), 120 ); 121 // The DC and IC instructions must use 64-bit registers so we don't use 122 // uintptr_t in case this runs in an IPL32 environment. 123 var addr: u64 = undefined; 124 // If CTR_EL0.IDC is set, data cache cleaning to the point of unification 125 // is not required for instruction to data coherence. 126 if (((ctr_el0 >> 28) & 0x1) == 0x0) { 127 const dcache_line_size = @as(usize, 4) << @intCast((ctr_el0 >> 16) & 15); 128 addr = start & ~(dcache_line_size - 1); 129 while (addr < end) : (addr += dcache_line_size) { 130 asm volatile ("dc cvau, %[addr]" 131 : 132 : [addr] "r" (addr), 133 ); 134 } 135 } 136 asm volatile ("dsb ish"); 137 // If CTR_EL0.DIC is set, instruction cache invalidation to the point of 138 // unification is not required for instruction to data coherence. 139 if (((ctr_el0 >> 29) & 0x1) == 0x0) { 140 const icache_line_size = @as(usize, 4) << @intCast((ctr_el0 >> 0) & 15); 141 addr = start & ~(icache_line_size - 1); 142 while (addr < end) : (addr += icache_line_size) { 143 asm volatile ("ic ivau, %[addr]" 144 : 145 : [addr] "r" (addr), 146 ); 147 } 148 } 149 asm volatile ("isb sy"); 150 exportIt(); 151 } else if (powerpc64) { 152 // TODO 153 //const size_t line_size = 32; 154 //const size_t len = (uintptr_t)end - (uintptr_t)start; 155 // 156 //const uintptr_t mask = ~(line_size - 1); 157 //const uintptr_t start_line = ((uintptr_t)start) & mask; 158 //const uintptr_t end_line = ((uintptr_t)start + len + line_size - 1) & mask; 159 // 160 //for (uintptr_t line = start_line; line < end_line; line += line_size) 161 // __asm__ volatile("dcbf 0, %0" : : "r"(line)); 162 //__asm__ volatile("sync"); 163 // 164 //for (uintptr_t line = start_line; line < end_line; line += line_size) 165 // __asm__ volatile("icbi 0, %0" : : "r"(line)); 166 //__asm__ volatile("isync"); 167 // exportIt(); 168 } else if (sparc) { 169 // TODO 170 //const size_t dword_size = 8; 171 //const size_t len = (uintptr_t)end - (uintptr_t)start; 172 // 173 //const uintptr_t mask = ~(dword_size - 1); 174 //const uintptr_t start_dword = ((uintptr_t)start) & mask; 175 //const uintptr_t end_dword = ((uintptr_t)start + len + dword_size - 1) & mask; 176 // 177 //for (uintptr_t dword = start_dword; dword < end_dword; dword += dword_size) 178 // __asm__ volatile("flush %0" : : "r"(dword)); 179 // exportIt(); 180 } else if (apple) { 181 // On Darwin, sys_icache_invalidate() provides this functionality 182 sys_icache_invalidate(start, end - start); 183 exportIt(); 184 } else if (os == .linux and loongarch) { 185 // See: https://github.com/llvm/llvm-project/blob/cf54cae26b65fc3201eff7200ffb9b0c9e8f9a13/compiler-rt/lib/builtins/clear_cache.c#L94-L95 186 asm volatile ("ibar 0"); 187 exportIt(); 188 } 189 190 std.valgrind.discardTranslations(@as([*]u8, @ptrFromInt(start))[0 .. end - start]); 191 } 192 193 fn exportIt() void { 194 @export(&clear_cache, .{ .name = "__clear_cache", .linkage = common.linkage, .visibility = common.visibility }); 195 } 196 197 // Darwin-only 198 extern fn sys_icache_invalidate(start: usize, len: usize) void; 199 // BSD-only 200 const arm_sync_icache_args = extern struct { 201 addr: usize, // Virtual start address 202 len: usize, // Region size 203 }; 204 const ARM_SYNC_ICACHE = 0; 205 extern "c" fn sysarch(number: i32, args: usize) i32;