zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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;