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